# Challenge: LRU Cache Implementation
**Difficulty:** Medium
**Language:** Java
**Subject:** Data Structures

## Description
In high-performance software engineering, caching is a critical technique used to reduce data retrieval time by storing frequently accessed information in fast-access memory. One of the most common eviction policies is **LRU (Least Recently Used)**. This policy discards the least recently accessed items first when the cache reaches its capacity.

Your task is to design and implement a data structure for an LRU Cache. The cache must support two primary operations: `get` and `put`. 
- `get(key)`: Retrieve the value associated with the key. If the key exists, it should be marked as "recently used." If it doesn't exist, return -1.
- `put(key, value)`: Insert or update the value for a given key. If the key already exists, update its value and mark it as "recently used." If the key is new and the cache is at full capacity, you must remove the least recently used item before inserting the new one.

The primary constraint is that both `get` and `put` operations must run in **O(1)** average time complexity.

## Requirements
- Implement a class `LRUCache` that takes an integer `capacity` as a constructor argument.
- Implement the `get(int key)` method:
    - Return the value if the key exists; otherwise, return -1.
    - Moving a key to the "most recently used" position must happen automatically upon access.
- Implement the `put(int key, int value)` method:
    - If the key exists, update the value and move it to the "most recently used" position.
    - If the key is new, add it to the cache.
    - If the cache exceeds `capacity`, remove the entry that was accessed least recently.
- **Performance Constraint:** You must achieve $O(1)$ time complexity for both operations. (Hint: Using only a HashMap or only a Linked List will not satisfy the time complexity requirements for both operations simultaneously).

## Hints
- To achieve $O(1)$ lookup, a `HashMap` is essential. However, a standard `HashMap` does not maintain the order of access.
- To achieve $O(1)$ removal and insertion at specific positions, a **Doubly Linked List** is the ideal companion to the HashMap.
- The HashMap should store the key as the map key and the corresponding Node object (from your Doubly Linked List) as the map value. This allows you to jump directly to the node in the list to re-link it in constant time.

## Validation
- **Check:** Initialize `LRUCache cache = new LRUCache(2)`.
- **Sequence:** 
    1. `cache.put(1, 1)`
    2. `cache.put(2, 2)`
    3. `cache.get(1)` (Should return 1, and 1 becomes most recent)
    4. `cache.put(3, 3)` (Capacity is 2, so the least recently used key '2' should be evicted)
    5. `cache.get(2)` (Should return -1)
    6. `cache.get(3)` (Should return 3)
    7. `cache.put(4, 4)` (Capacity is 2, so the least recently used key '1' should be evicted)
    8. `cache.get(1)` (Should return -1)
    9. `cache.get(3)` (Should return 3)
    10. `cache.get(4)` (Should return 4)
- **Expected Output:** `[1, -1, 3, -1, 3, 4]`