Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 106 additions & 2 deletions src/main/java/core/basesyntax/MyHashMap.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,123 @@
package core.basesyntax;

import java.util.Objects;

public class MyHashMap<K, V> implements MyMap<K, V> {
private static final int INITIAL_CAPACITY = 16;
private static final float LOAD_FACTOR = 0.75f;
private int capacity = INITIAL_CAPACITY;
private int size = 0;
private Node<K, V>[] elements = new Node[capacity];

public MyHashMap() {
}

@Override
public void put(K key, V value) {
int hash = this.hash(key);
int position = hash % this.capacity;
Node<K, V> currentNode = this.getNode(key);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the put method, you calculate the hash and position on lines 17 and 18. However, the getNode(key) call on this line recalculates the same values internally.

To avoid this redundant work, you can directly access the head of the bucket using the position you already calculated, like this:
Node<K, V> currentNode = this.elements[position];


if (currentNode == null) {
this.elements[position] = new Node<>(hash, key, value, null);
} else {
Node<K, V> lastNode = null;

while (currentNode != null) {
if (Objects.equals(key, currentNode.key)) {
currentNode.value = value;
return;
}

lastNode = currentNode;
currentNode = currentNode.next;
}

lastNode.next = new Node<>(hash, key, value, null);
}

if (++this.size > (int)(capacity * LOAD_FACTOR)) {
this.resize();
}
}

@Override
public V getValue(K key) {
return null;
Node<K, V> currentNode = this.getNode(key);

if (currentNode == null) {
return null;
}

while (currentNode != null) {
if (Objects.equals(currentNode.key, key)) {
break;
}

currentNode = currentNode.next;
}

return currentNode == null ? null : currentNode.value;
}

@Override
public int getSize() {
return 0;
return this.size;
}

private void resize() {
int oldCap = this.capacity;
int newCap = this.capacity << 1;
Node<K, V>[] newElements = new Node[newCap];

for (int i = 0; i < oldCap; i++) {
Node<K, V> node = this.elements[i];

while (node != null) {
int newPosition = node.hash % newCap;
Node<K, V> newNode = new Node<>(node.hash, node.key, node.value, null);

if (newElements[newPosition] == null) {
newElements[newPosition] = newNode;
} else {
Node<K, V> currentNode = newElements[newPosition];
Node<K, V> lastNode = null;

while (currentNode != null) {
lastNode = currentNode;
currentNode = currentNode.next;
}

lastNode.next = newNode;
}

node = node.next;
}
Comment on lines +76 to +95

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation of resize correctly rehashes all the elements, but it's inefficient for two main reasons:

  1. Object Creation: A new Node is created for every element in the map (line 78). It's more memory-efficient to move the existing nodes.
  2. List Traversal: To add a node to a new bucket, you traverse the entire linked list in that bucket to find the end (lines 83-89). This can be very slow if collision chains get long.

A much more efficient approach is to prepend the old nodes to the new buckets. This avoids both creating new objects and traversing lists.

Consider this logic inside your while (node != null) loop:

Node<K, V> nextNode = node.next; // Save the next node from the old chain
int newPosition = node.hash % newCap;
node.next = newElements[newPosition]; // Link the current node to the start of the new chain
newElements[newPosition] = node; // The current node becomes the new head
node = nextNode; // Move to the next node in the old chain

}

this.elements = newElements;
this.capacity = newCap;
}

private int hash(K key) {
return key == null ? 0 : key.hashCode() & Integer.MAX_VALUE;
}

private Node<K, V> getNode(K key) {
return this.elements[this.hash(key) % this.capacity];
}

private static class Node<K, V> {
private final int hash;
private final K key;
private V value;
private Node<K, V> next;

private Node(int hash, K key, V value, Node<K, V> node) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = node;
}
}
}