Skip to content

Conversation

@dchou1618
Copy link
Owner

@dchou1618 dchou1618 commented Jan 3, 2026

User description

Description

second batch of hash table practice


PR Type

Enhancement, Tests


Description

  • Implement LRU Cache with doubly linked list and hash table

    • Node and NodeList classes for efficient list operations
    • LRUCache class with get/put operations and capacity management
  • Fix import path in test file for hash table module


Diagram Walkthrough

flowchart LR
  A["LRUCache"] -->|uses| B["HashMap"]
  A -->|uses| C["NodeList"]
  C -->|contains| D["Node"]
  D -->|links| D
  B -->|stores| D
  E["get/put operations"] -->|interact with| A
Loading

File Walkthrough

Relevant files
Enhancement
cache.py
LRU Cache implementation with linked list                               

Algorithms/datastructs/hash_table/cache.py

  • Added Node class representing doubly linked list nodes with key-value
    pairs
  • Implemented NodeList class managing doubly linked list operations
    (add, remove, move_to_end, evict)
  • Created LRUCache class combining hash table and doubly linked list for
    O(1) get/put operations
  • Supports capacity-based eviction of least recently used items
+93/-0   
Bug fix
test_hash_table.py
Fix hash table import path                                                             

tests/test_hash_table.py

  • Updated import statement to use correct module path with explicit file
    reference
+1/-1     

practice lru cache with doubly linked list implementation
@dchou1618 dchou1618 changed the title update with lru cache practice with caches Jan 3, 2026
@qodo-code-review
Copy link

qodo-code-review bot commented Jan 3, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status:
Generic variable names: New code introduces non-descriptive identifiers (e.g., temp, res) that reduce readability
and are not self-documenting.

Referred Code
    temp = Node(key, value)
    if self.start:
        if self.start == self.end:
            self.start.next = temp
            self.end = temp
            self.end.prev = self.start
        else:
            temp.prev = self.end
            self.end.next = temp
            self.end = temp
    else:
        self.start = temp
        self.end = self.start
    return temp
def remove(self, node: Node):
    prev_node = node.prev
    next_node = node.next
    if prev_node:
        prev_node.next = next_node
    else:
        self.start = next_node


 ... (clipped 31 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Missing capacity validation: LRUCache.init accepts capacity without validating boundary values (e.g., negative),
which can lead to incorrect/unbounded cache behavior.

Referred Code
class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.table = dict()
        self.cache = NodeList()

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
No input validation: LRUCache does not validate the externally provided capacity argument (e.g., ensuring it is
a non-negative integer), which can cause unsafe/incorrect runtime behavior.

Referred Code
class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.table = dict()
        self.cache = NodeList()

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link

qodo-code-review bot commented Jan 3, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix race condition and error handling

Prevent a potential AttributeError in the put method by verifying that
self.cache.evict() returns a valid node before attempting to delete the evicted
key from the table.

Algorithms/datastructs/hash_table/cache.py [78-93]

 def put(self, key: int, value: int) -> None:
     if key in self.table:
         old_node = self.table[key]
         old_node.value = value
         self.cache.move_to_end(old_node)
     else:
         if self.capacity == 0:
             return
+        
+        lru_key_to_delete = None
         if len(self.table) == self.capacity:
             lru = self.cache.evict()
-            del self.table[lru.key]
-            node = self.cache.add(key, value)
-            self.table[key] = node
-        else:
-            node = self.cache.add(key, value)
-            self.table[key] = node
+            if lru:
+                lru_key_to_delete = lru.key
+        
+        node = self.cache.add(key, value)
+        self.table[key] = node
 
+        if lru_key_to_delete:
+            del self.table[lru_key_to_delete]
+
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a critical AttributeError that occurs if self.cache.evict() returns None. The proposed change correctly handles this by checking the return value of evict before attempting to access its attributes, preventing a crash.

Medium
Fix an AttributeError in move_to_end

Fix a potential AttributeError in the move_to_end method by handling the case
where the list becomes empty after a node is removed.

Algorithms/datastructs/hash_table/cache.py [38-45]

 def move_to_end(self, node):
     if node == self.end:
         return
     self.remove(node)
-    node.prev = self.end
+    # After removal, if the list becomes empty, self.end will be None.
+    if self.end:
+        node.prev = self.end
+        self.end.next = node
+    else:
+        # The list is now empty, so this node becomes the start and end.
+        self.start = node
+        node.prev = None
+    
     node.next = None
-    self.end.next = node
     self.end = node
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies a latent AttributeError in the move_to_end method. While the bug is not triggerable by the current usage within LRUCache, fixing it makes the NodeList class more robust and safe for future use.

Low
Clear evicted node pointers

Refactor the evict method to reuse the remove method, reducing code duplication,
and clear the evicted node's pointers to prevent stale references.

Algorithms/datastructs/hash_table/cache.py [46-57]

 def evict(self):
-    start = self.start
-    if not start:
+    node = self.start
+    if not node:
         return None
-    next_node = start.next
-    if next_node:
-        next_node.prev = None
-        self.start = next_node
-    else:
-        self.start = None
-        self.end = None
-    return start
+    # remove from list
+    self.remove(node)
+    # clear pointers on evicted node
+    node.prev = None
+    node.next = None
+    return node
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: This suggestion improves the evict method by reusing the remove method, which reduces code duplication and improves maintainability. Clearing the evicted node's pointers is also a good practice to prevent stale references.

Low
General
Validate positive capacity

Add input validation to the LRUCache constructor to ensure the capacity is a
positive integer, raising a ValueError for non-positive values.

Algorithms/datastructs/hash_table/cache.py [67-70]

 def __init__(self, capacity: int):
+    if capacity < 1:
+        raise ValueError("capacity must be >= 1")
     self.capacity = capacity
     self.table = dict()
     self.cache = NodeList()
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly proposes adding input validation for the capacity parameter. This is a good practice that makes the LRUCache class more robust by preventing initialization with invalid values like zero or negative numbers, which would lead to incorrect behavior.

Medium
Simplify list node addition logic

Simplify the add method in NodeList by removing the redundant special case for a
single-element list, making the code more concise and readable.

Algorithms/datastructs/hash_table/cache.py [12-26]

 def add(self, key: int, value: int):
     temp = Node(key, value)
-    if self.start:
-        if self.start == self.end:
-            self.start.next = temp
-            self.end = temp
-            self.end.prev = self.start
-        else:
-            temp.prev = self.end
-            self.end.next = temp
-            self.end = temp
+    if not self.start:
+        self.start = temp
+        self.end = temp
     else:
-        self.start = temp
-        self.end = self.start
+        self.end.next = temp
+        temp.prev = self.end
+        self.end = temp
     return temp
  • Apply / Chat
Suggestion importance[1-10]: 4

__

Why: The suggestion correctly points out that the add method can be simplified by removing a redundant conditional branch. This improves code readability and maintainability without changing the functionality.

Low
Show prev/next keys in __str__

Improve the str method to display the keys of the prev and next nodes
instead of their object references, enhancing debuggability.

Algorithms/datastructs/hash_table/cache.py [58-64]

 def __str__(self):
     head = self.start
     res = ""
     while head:
-        res += f"Node(key: {head.key}, value: {head.value}, prev: {head.prev}, next: {head.next})"
+        prev_key = head.prev.key if head.prev else None
+        next_key = head.next.key if head.next else None
+        res += f"Node(key: {head.key}, value: {head.value}, prev: {prev_key}, next: {next_key})"
         head = head.next
     return res
  • Apply / Chat
Suggestion importance[1-10]: 3

__

Why: This suggestion improves the __str__ method's output, making it significantly more readable for debugging purposes by showing node keys instead of object references. This is a useful quality-of-life improvement.

Low
  • Update

@dchou1618 dchou1618 merged commit cb62e9d into master Jan 4, 2026
3 checks passed
@dchou1618 dchou1618 deleted the feat/hash-table-2 branch January 4, 2026 05:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants