Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 4b8a3b9

Browse files
committed
Added HashMap
1 parent a914cd4 commit 4b8a3b9

File tree

2 files changed

+284
-0
lines changed

2 files changed

+284
-0
lines changed

Hashmap/HashMap.java

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
public class HashMap {
2+
3+
// NODE
4+
public class Node {
5+
public String key;
6+
public String value;
7+
private Node next;
8+
9+
public Node(String key, String value) {
10+
this.key = key;
11+
this.value = value;
12+
this.next = null;
13+
}
14+
15+
public void setNextNode(Node node) {
16+
this.next = node;
17+
}
18+
19+
public Node getNextNode() {
20+
return this.next;
21+
}
22+
23+
public void setKeyValue(String key, String value) {
24+
this.key = key;
25+
this.value = value;
26+
}
27+
}
28+
29+
// LINKED LIST
30+
public class LinkedList {
31+
public Node head;
32+
33+
public LinkedList() {
34+
this.head = null;
35+
}
36+
37+
public void addToHead(String key, String value) {
38+
Node newHead = new Node(key, value);
39+
Node currentHead = this.head;
40+
this.head = newHead;
41+
if (currentHead != null) {
42+
this.head.setNextNode(currentHead);
43+
}
44+
}
45+
46+
public void addToTail(String key, String value) {
47+
Node tail = this.head;
48+
if (tail == null) {
49+
this.head = new Node(key, value);
50+
} else {
51+
while (tail.getNextNode() != null) {
52+
tail = tail.getNextNode();
53+
}
54+
tail.setNextNode(new Node(key, value));
55+
}
56+
}
57+
58+
public void removeHead() {
59+
Node removedHead = this.head;
60+
if (removedHead == null) {
61+
return;
62+
}
63+
this.head = removedHead.getNextNode();
64+
}
65+
}
66+
67+
// HASHMAP IMPLEMENTATION
68+
public LinkedList[] hashmap;
69+
70+
public HashMap(int size) {
71+
this.hashmap = new LinkedList[size];
72+
for (int i = 0; i < size; i++) {
73+
this.hashmap[i] = new LinkedList();
74+
}
75+
}
76+
77+
public int hash(String key) {
78+
int hashCode = 0;
79+
for (int i = 0; i < key.length(); i++) {
80+
hashCode = hashCode + Character.codePointAt(key, i);
81+
}
82+
hashCode = hashCode % this.hashmap.length;
83+
return hashCode;
84+
}
85+
86+
public void assign(String key, String value) {
87+
int arrayIndex = this.hash(key);
88+
LinkedList list = this.hashmap[arrayIndex];
89+
if (list.head == null) {
90+
list.addToHead(key, value);
91+
return;
92+
}
93+
Node current = list.head;
94+
while (current != null) {
95+
if (current.key == key) {
96+
current.setKeyValue(key, value);
97+
}
98+
if (current.getNextNode() == null) {
99+
current.setNextNode(new Node(key, value));
100+
break;
101+
}
102+
current = current.getNextNode();
103+
}
104+
}
105+
106+
public String retrieve(String key) {
107+
int arrayIndex = this.hash(key);
108+
Node current = this.hashmap[arrayIndex].head;
109+
while (current != null) {
110+
if (current.key == key) {
111+
return current.value;
112+
}
113+
current = current.getNextNode();
114+
}
115+
return null;
116+
}
117+
118+
public static void main(String[] args) {
119+
HashMap food = new HashMap(2);
120+
food.assign("item1", "pizza");
121+
food.assign("item2", "burger");
122+
System.out.println(food.retrieve("item1"));
123+
System.out.println(food.retrieve("item2"));
124+
}
125+
}

Hashmap/readme.md

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# Hash Maps
2+
3+
Hashing is a Data Structure which is designed to use a special function called the Hash function which is used to map
4+
a given value with a particular key for faster access of elements.
5+
6+
## Tables
7+
8+
A data structure’s main utility is allowing for data to be represented in a way that
9+
resembles the way people will use that data.
10+
11+
In some cases, the primary function of that data is that it will be
12+
sequenced through like a list and so we use a data structure that allows for easier iteration, like a linked list. In others, the usefulness comes from specifying interrelationships within the data.
13+
14+
In the case of tabular data there is a relationship between the elements
15+
of a row. Each column corresponds to a different feature of the row.
16+
17+
**Maps**: Being a map means relating two pieces of information.
18+
19+
## Methodology
20+
21+
In the case of a map between two things, we don’t really care about the exact sequence of the data.
22+
23+
We only care that a given input, when fed into the map, gives the accurate output.
24+
25+
Developing a data structure that performs this is tricky because computers care much more about values than relationships.
26+
27+
A computer doesn’t really care to memorize the astrological signs of all of our friends, so we need to trick the computer into caring.
28+
29+
We perform this trick using a structure that our computer is already familiar with, an array. An array uses indices to keep track of values in memory, so we’ll need a way of turning each key in our map to an index in our array.
30+
31+
We use a special function that turns data like the string “Hello” into a number. This function is called a **hashing function**, or a **hash function**.
32+
33+
## Hash Functions
34+
35+
A hash function takes a string (or other type of data) as input and returns an array index as output.
36+
37+
In order for it to return an array index, our hash map implementation needs to _know the size of our array_.
38+
39+
In order for our hash map implementation to guarantee that it returns an index that fits into the underlying array, the hash function will first compute a value using some scoring metric: this is the hash value, hash code, or just the hash.
40+
41+
Our hash map implementation then takes that hash value mod the size of the array.
42+
43+
This guarantees that the value returned by the hash function can be used as an index into the array we’re using.
44+
45+
Hash functions greatly reduce any possible inputs (any string you can imagine) into a much smaller range of potential outputs (an integer smaller than the size of our array).
46+
47+
For this reason _hash functions_ are also known as **compression functions**.
48+
49+
Hashing is a _irreversible process_ which means that with just a hash value, it is _impossible_ to know for sure the key that was plugged into the hashing function.
50+
51+
## Writing A Hash Function
52+
53+
Hash function needs to be _simple by design_.
54+
55+
Performing complex mathematical calculations that our hash table needs to compute every time it wants to assign or retrieve a value for a key will significantly damage a hash table’s performance.
56+
57+
Hash functions also need to be able to take whatever types of data we want to use as a key.
58+
59+
A very common hash function for integers, for example, is to perform the modular operation on it to make sure it’s less than the size of the underlying array.
60+
61+
If the integer is already small enough to be an index into the array, there’s nothing to be done.
62+
63+
Many hash functions implementations for strings take advantage of the fact that strings are represented internally as numerical data.
64+
65+
Frequently a hash function will perform a shift of the data bitwise, which is computationally simple for a computer to do but also can predictably assign numbers to strings.
66+
67+
## Collisions
68+
69+
Hash functions are designed to _compress data_ from a large number of possible keys to a much smaller range.
70+
71+
Because of this compression, it’s likely that our hash function might produce the same hash for two different keys. This is known as a **hash collision**.
72+
73+
## Strategies To Avoid Collision
74+
75+
We can avoid collision in Hash Maps using these commonly used techniques.
76+
77+
## Separate Chaining
78+
79+
The user wants to assign a value to a key in the map. The hash map takes the key and transforms it into a hash code.
80+
81+
The hash code is then converted into an index to an array using the modulus operation. If the value of the array at the hash function’s returned index is empty, a new linked list is created with the value as the first element of the linked list.
82+
83+
If a linked list already exists at the address, append the value to the linked list given.
84+
85+
This is effective for hash functions that are particularly good at giving unique indices, so the linked lists never get very long.
86+
87+
But in the worst-case scenario, where the hash function gives all keys the same index, lookup performance is only as good as it would be on a linked list.
88+
89+
Hash maps are frequently employed because looking up a value (for a given key) is quick.
90+
91+
Looking up a value in a linked list is much slower than a perfect, collision-free hash map of the same size.
92+
93+
A hash map that uses separate chaining with linked lists but experiences frequent collisions loses one of its most essential features.
94+
95+
## Saving Keys
96+
97+
A hash collision resolution strategy like _separate chaining_ involves assigning two keys with the same hash to different parts of the underlying data structure.
98+
99+
How do we know which values relate back to which keys?
100+
101+
If the linked list at the array index given by the hash has multiple elements, they would be indistinguishable to someone with just the key.
102+
103+
If we save both the key and the value, then we will be able to check against the saved key when we’re accessing data in a hash map.
104+
105+
By saving the key with the value, we can avoid situations in which two keys have the same hash code where we might not be able to distinguish which value goes with a given key.
106+
107+
Now, when we go to read or write a value for a key we do the following: calculate the hash for the key, find the appropriate index for that hash, and begin iterating through our linked list.
108+
109+
For each element, if the saved key is the same as our key, return the value.
110+
111+
Otherwise, continue iterating through the list comparing the keys saved in that list with our key.
112+
113+
## Open Addressing - Linear Probing
114+
115+
In open addressing we stick to the array as our underlying data structure, but we continue looking for a new index to save our data if the first result of our hash function has a different key’s data.
116+
117+
A common open method of open addressing is called **probing**.
118+
119+
Probing means continuing to find new array indices in a fixed sequence until an empty index is found.
120+
121+
## Other Open Addressing Techniques
122+
123+
There are more sophisticated ways to find the next address after a hash collision, although anything too calculation-intensive would negatively affect a hash table’s performance.
124+
125+
**Linear probing systems**, for instance, could jump by five steps instead of one step.
126+
127+
In a **Quadratic Probing** open addressing system, we add increasingly large numbers to the hash code.
128+
129+
At the first collision we just add 1, but if the hash collides there too we add 4 ,and the third time we add 9.
130+
131+
Having a probe sequence change over time like this avoids clustering.
132+
133+
**Clustering** is what happens when a _single hash collision causes additional hash_ _collisions_.
134+
135+
As a result the new key needs to be assigned to the next, next bucket over.
136+
137+
This propagates the problem because now there are two hash buckets taken up by key-value pairs that were assigned as a result of a hash collision, displacing further pairs of information we might want to save to the table.
138+
139+
## Overview
140+
141+
1. **Hash map**: A key-value store that uses an array and a hashing function to save and retrieve values.
142+
2. **Key**: The identifier given to a value for later retrieval.
143+
3. **Hash function**: A function that takes some input and returns a number.
144+
4. **Compression function**: A function that transforms its inputs into some smaller range of possible outputs.
145+
146+
**Recipe for saving to a hash table:**
147+
148+
- Take the key and plug it into the hash function, getting the hash code.
149+
- Modulo that hash code by the length of the underlying array, getting an array index.
150+
- Check if the array at that index is empty, if so, save the value (and the key) there.
151+
- If the array is full at that index continue to the next possible position depending on your collision strategy.
152+
153+
**Recipe for retrieving from a hash table:**
154+
155+
- Take the key and plug it into the hash function, getting the hash code.
156+
- Modulo that hash code by the length of the underlying array, getting an array index.
157+
- Check if the array at that index has contents, if so, check the key saved there.
158+
- If the key matches the one you're looking for, return the value.
159+
- If the keys don't match, continue to the next position depending on your collision strategy.

0 commit comments

Comments
 (0)