I am supposed to make a chain hash table to put names in each bucket as a linked list. I know how to do this with buckets that hold one value but I do not know how to put a link list in each bucket. I have a person class with first and last name already as well as a hashcode class. I have written remove but I am not sure how to put a LinkedList into the method. I also have a bucketList class; is this where I need to implement the LinkedList? If I can get some pointers on what do on the remove or put methods, I should be able to figure out how to do the rest. Thank you
public class MyChainHashTable<K, V> {
private static final int BUCKET_COUNT = 10;
private BucketList[] buckets = new BucketList[BUCKET_COUNT];
private void remove(K key, V value) {
int bucketIndex = key.hashCode(); //TODO
int bucketsProbed = 0;
while (!buckets[bucketIndex].isEmptySinceStart() && bucketsProbed < BUCKET_COUNT) {
// if this bucket isn't empty, and it matches what we're looking for
if (!buckets[bucketIndex].isEmpty()
&& buckets[bucketIndex].getElement().equals(value)) {
buckets[bucketIndex].clear();
return;
}
bucketsProbed++;
bucketIndex++;
bucketIndex %= BUCKET_COUNT; // circle back to 0
}
}
private boolean put(K key, V value) {
return false;
}
private void showTable() {
// old phone UI
String[] keyBoard = {"1 ", "2 ABC", "3 DEF", "4 GHI", "5 JKL",
"6 MNO", "7 PRS", "8 TUV", "9 WXY", "0 "};
}
So from what I gather you want to implement a hash table with linked lists. I also saw this comment
BucketList generically – Rawsick
So let me try and implement parts of this data structure.
Let's start with BucketList. Since this sounds like a just a Bucket with a generic parameter T defining the bucket and V which is what's in the bucket. I'm gonna refactor it to Bucket<T, V>
public class Bucket<T extends Colletion<V>, V> {
private T bucket;
public T add(V value) {
return bucket.add(V);
}
// More functions here
}
Now the hash table,
public class MyChainHashTable<K, V> {
private static final int BUCKET_COUNT = 10;
// Advisable to use a resizeable array here, like an ArrayList
// No need for bucket count then
private Bucket<LinkedList, V>[] buckets = new Bucket<>[BUCKET_COUNT];
public V put(K key, V value) {
int bucketIndex = key.hashCode() % BUCKET_COUNT;
buckets[bucketIndex].add(V);
}
// More functions here
}
This was a very simple approach to a hash table, only the bucket itself was slightly complicated in that the bucket could store values in the form of any class from the Java Collections framework.
I would advise reading into the source code of several popular implementations of collections in Java. You will get a better idea of how to approach problems like this.
Check out google's Gauva library. Look at some of the complex collections like MultiMap and go through how they implemented it.
I think a Chained HashTable is really a HashTable of HashTables which maps your hash key to a HashTable so that you don't have to jump around the table when you have key collisions.
Your asking for a variant of that with a HashTable of LinkedLists instead of a HashTable of HashTables.
In a basic HashTable you need to compute the next index to try when you get a key collision, basically what your doing with bucketIndex %= BUCKET_COUNT; but with a chained HashTable you don't do that. Instead of inserting your element at the indexed location in your underlying array, your array is an array of HashTables (or an array of LinkedLists in your case) and you insert your elements into that collection.
Related
I have to implement a hash table which will use an array, however have to follow a guide and create functions for each procedure.
I would appreciate if anyone could help out in completing this for me as I am having some trouble.
public class HashTable {
// public for testing purposes
public int buckets[];
public HashTable(long _a, long _c, long _m) {
}
public void insert(int key) {
}
}
What I've got so far:
public class HashTable {
// public for testing purposes
public int buckets[];
public HashTable(long _a, long _c, long _m) {
table = new Node[];
}
public void insert(int key) {
Node<T> newNode = new Node(key);
int posPosition = calPosition(key);
}
I have included what I have done so far. Maybe I'm going about it the wrong way. I understand the concept but cannot seem to write code for the hash table so far. Would appreciate any help, Thanks Again
A hash table is simply a list or array of buckets.
Each bucket holds all items the key that hashes to for that particular bucket.
those items are entries that contain the key and the value you are seeking.
When putting something in a hash table, you use the key/value pair. If the buckets are in an array, use the hash code of the key to get the proper index of the array. If you use a linked list you might have to count to the location.
Then use another array or linked list to store then entry at that cell. Linked lists are better, imo, because they can be added without worry of exceeding the size of the array. They can just be added to the front like a regular linked list.
When adding a value, create the entry, hash the key and find the bucket. Then add the entry to the bucket.
When retrieving a value, hash the key, get to the bucket and do a linear search on the bucket to find the entry for the key you are looking for. Then return the value for that entry.
Note: As with most hash tables, you cannot have duplicate keys. And any Object which is used as a key must override equals and hashCode for this to work.
I am given a Map Data Structure with the Generic Types (K,V) where the K represents the key, and the V represents the value.
Now I am asked to implement the classic put(K key, V value) method, which comes along with the collection. The method is pre-implemented, and overrides the one from an interface which is used:
#Override
public void put(K key, V value) {
for (int i = 0; i <= this.entries.length; i++) {
if (this.entries[i] == null || this.entries[i].getKey().equals(key)) {
this.entries[i] = new Entry<K, V>(key, value);
return;
} else {
this.entries = GenericArrayHelper.copyArrayWithIncreasedSize(this.entries, (this.entries.length) * 2);
/* replace [...]
this.entries[...] = new Entry<K, V>(key, value);
*/
}
}
}
With the exception of the part that's commented out, where I would need to replace the [...] with the correct position of the array. Namely:
If an Entry<K,V> at index i in the map is null, or the key of the entry at index i equals the key that was passed over, replace the entry with a new entry containing the key and value that are passed, and finish the method.
If such an Entry<K,V> cannot be found, the map (which is an array of the form entries<K,V>[ ]) which the method is called on, shall be copied in full and its array size shall be doubled (with the aiding method GenericArrayHelper.copyArrayWithIncreasedSize). Successively, at the very first vacant slot of the copied and resized array, put in a new Entry with the passed key and value.
And this is where the confusion arose. I have tried to replace [...] with all kinds of indices, but have never gotten a satisfactory result.
When I try to put in these several entries, with putInside being a Map:
putInside.put("sizeInMB", 42);
putInside.put("version", 4);
putInside.put("yearOfRelease", 2015);
I shall get the following result, when printing ("stringified" with a separate toString method):
yearOfRelease : 2015
sizeInMB : 42
version : 4
yearOfRelease : 2015
sizeInMB : 42
version : 4
but when I, say, use the array index of entries.length-1 for [...], which is the closest I got to after hours of trying, and watch the debugger, it looks abysmal:
with the first three entries being correct, but the other three getting mashed up... and when I print the whole thing I merely get an output of the first tree entries, since the other three seem to be ignored completely (perhaps because the larger arrays in the for loop are merely defined in the loop itself?)
My question is: How do I define a suitable replacement for the index [...], or, maybe also for better understanding: Why on earth would we need to double the size of the array first? I have never implemented any Java Collection data structure before, and also haven't taken the data structures class at my uni yet... How do I get to a "doubled" output?
Any form of help would be really really appreciated!
EDIT: To make things clearer, here is the toString method:
public static void toString(Map<String, Integer> print) {
for(String key : print.keysAsSet()){
System.out.println(key + ": " + print.getValueFor(key));
}
}
with keysAsSet() returning a HashSet of the Map, which merely contains the keys, but not the values.
public Set<K> keysAsSet() {
HashSet<K> current = new HashSet<>();
for(Entry<K,V> entry : entries){
if(entry != null) {
current.add(entry.getKey());
}
}
return current;
}
The code doesn’t match the description. To wit, you are resizing the array inside the loop, unless the very first element in entries is a hit (i.e. it’s null or it equals the key).
You need to put the array resizing after the loop (plus fix the loop condition check):
#Override
public void put(K key, V value) {
int oldLength = this.entries.length;
for (int i = 0; i < oldLength; i++) {
if (this.entries[i] == null || this.entries[i].getKey().equals(key)) {
this.entries[i] = new Entry<K, V>(key, value);
return;
}
}
this.entries = GenericArrayHelper.copyArrayWithIncreasedSize(this.entries, oldLength * 2);
this.entries[oldLength] = new Entry<K, V>(key, value);
}
… I assume you’re aware that this is a very inefficient map implementation: each search and insertion take O(n) tries. In reality you’d either use a hash table or some sort of search tree to speed up insertion and lookup.
This question was asked to me in a job interview and I still don't know answer so ask here. Lets say hashCode() of key object returns a fixed integer so HashMap would look like a LinkedList.
How would a duplicate element be found and replaced by new value in map?
e.g. if following 1001 puts are performed in order listed below,
put(1000,1000), put(1,1), put( 2, 2), put ( 3,3 ) ....put(999,999), put(1000,1000 )
Would map be traversed all the way to end and then new one be inserted at head when last put(1000,1000) is performed?
OR
Map has some other way to locate and replace duplicate keys?
First case is correct.
In your case when hashCode() is returning same hash value for all the keys. In the java HashMap, Key and Value both are stored in the bucket as Map.Entry object. When perform the second or further put() operations into the map, it will traverse all the element to check whether Key is already present in the Map. If Key is not found then new Key and Value pair will be added into the linked list. If Key is found in the list then it update the Value for the pair.
Details explanation about java HashMap working: How HashMap works in Java
Take this sample code and run in the debug mode and observe how the new Key and Value pair are inserted into the Map.
In the class you will need to hashCode() (we want to control how the hash codes are generated for Node), toString() (just to output the Node value in SOUT) and equals() (defines the equality of the keys based on the value of Node member variable Integer, for updating the values.) methods for getting it working.
public class HashMapTest {
static class Node {
Integer n;
public Node(int n) {
this.n = n;
}
#Override
public int hashCode() {
return n%3;
}
#Override
public boolean equals(Object object) {
Node node = (Node)object;
return this.n.equals(node.n);
}
#Override
public String toString() {
return n.toString();
}
}
public static void main(String[] args) {
Map<Node, String> map = new HashMap<>();
for (int i = 0; i<6; i++) {
map.put(new Node(i), ""+i); // <-- Debug Point
}
map.put(new Node(0), "xxx");
} // <-- Debug Point
}
First 3 entries in the map: (hash code is n%3)
Three more values: (hash code is n%3)
Now don't confused about the ordering of the node, I have executed them on java 1.8 and HashMap uses TreeNode, an implementation of Red-Black tree as per the code documentation. This can be different in different versions of the java.
Now lets update the Value of Key 0:
When the hash code is the same, the hash map compares objects using the equals method.
For example, let's say you put a bunch of elements in the hash map:
put(1000,1000), put(1,1), put( 2, 2), put ( 3,3 ) ....put(999,999)
And then you do this:
put(1000,1000 )
1000 is already in the map, the hash code is the same, it is also the same in terms of the equals method, so the element will be replaced, no need to iterate further.
Now if you do:
put(1234, 1234)
1234 is not yet in the map. All the elements are in a linked list, due to the fixed hash code. The hash map will iterate over the elements, comparing them using equals. It will be false for all elements, the end of the list will be reached, and the entry will be appended.
JDK implementations changes over time !
In JDK8, if the hashCode() is a constant value, the implementation creates a tree not a linked list in order to protect against DDOS attack 1.
How is HashMap internally implemented? I read somewhere that it uses LinkedList while other places it mentions Arrays.
I tried studying the code for HashSet and found Entry array. Then where is LinkedList used?
It basically looks like this:
this is the main array
↓
[Entry] → Entry → Entry ← here is the linked-list
[Entry]
[Entry] → Entry
[Entry]
[null ]
[null ]
So you have the main array where each index corresponds to some hash value (mod'ed* to the size of the array).
Then each of them will point to the next entry with the same hash value (again mod'ed*). This is where the linked-list comes in.
*: As a technical note, it's first hashed with a different function before being mod'ed, but, as a basic implementation, just modding will work.
Each HashMap has an Array and in that Array it places each Entry in a position according to its key's hash code (e.g. int position = entry.getKey().hashCode() % array.length). The position where an Entry is stored is called a bucket.
If more than one Entry ends up in the same bucket, those Entries are combined in a LinkedList (also see #Dukeling's answer). Thus the bucket metaphor: each Array index is a "bucket" where you dump in all matching keys.
You have to use an Array for the buckets in order to achieve the desired constant time performance for random access. Within a bucket you have to traverse all elements to find the desired key anyways, so you can use a LinkedList as it is easier to append to (no resize needed).
This also shows the need for a good hash function, because if all keys hash to only a few values you will get long LinkedLists to search and a lot of (fast to access) empty buckets.
HashMap has an array of HashMap.Entry objects :
/**
* The table, resized as necessary. Length MUST Always be a power of two.
*/
transient Entry<K,V>[] table;
We can say that Entry is a one-way linked list (such HashMap.Entry linkage is called "Bucket") but it is not actually a java.util.LinkedList.
See for yourself :
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
public final K getKey() {
return key;
}
public final V getValue() {
return value;
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public final int hashCode() {
return (key==null ? 0 : key.hashCode()) ^
(value==null ? 0 : value.hashCode());
}
public final String toString() {
return getKey() + "=" + getValue();
}
/**
* This method is invoked whenever the value in an entry is
* overwritten by an invocation of put(k,v) for a key k that's already
* in the HashMap.
*/
void recordAccess(HashMap<K,V> m) {
}
/**
* This method is invoked whenever the entry is
* removed from the table.
*/
void recordRemoval(HashMap<K,V> m) {
}
}
HashMap internally uses Entry for storing key-value pair. Entry is of LinkedList type.
Entry contains following ->
K key,
V value and
Entry next > i.e. next entry on that location of bucket.
static class Entry<K, V> {
K key;
V value;
Entry<K,V> next;
public Entry(K key, V value, Entry<K,V> next){
this.key = key;
this.value = value;
this.next = next;
}
}
HashMap diagram -
From : http://www.javamadesoeasy.com/2015/02/hashmap-custom-implementation.html
The map is something that retrieves/puts a value based on a key since the key is mapped with that particular value.
But Internally, this mapping technique is slightly different.
This HashMap is defined as an array(Let's say for simplicity we have a size of 8).
Hashing is done to the key to identifying the place of the array where that particular key-value pair is going to store.
a. Key may be primitive type or object
b. Get the hashcode based on the key (If an object, we should implement better hashcode and equal methods in their class)
c. This hashcode makes the indexing and searching faster.
d. Maths - 12112, Science - 23454, Tamil - 3222112, English - 3243212
We can't put that key-value pair into the index which we have as hashcode since it is greater than the length of the array. So we do mod to get the place of the array where we have to put the key-value pair.
a. Maths will be in 12112 % 8 = 0
b. Science will be in 23454 % 8 = 4
c. Tamil will be in 3222112 % 8 = 0
d. English will be in 3243212 % 8 = 6
If your look carefully, we have collisions in the 0th index. How can we solve this collision? We have to save both key-value pairs in the same index For that, they introduce Node or Entry.Node has Key, Value, Hashcode and next node of that particular index.
when we have collisions It will add up to the next node. So finally It would be like this.
The hashMap is nothing but an array of linked lists. So each position has a linked list of the array to avoid collisions.
After Java 8
This LinkedList mechanism is changed to balance Tree since the time complexity of searching in the linked list is O(n). We have to go one by one to find the exact element in the LinkedList. in the balance Tree, It will be O(log(n)).
After Java 8, The hashmap is nothing but an array of balance Trees.
How to retrieve an element from HashMap by its position, is it possible at all?
Use a LinkedHashMap and when you need to retrieve by position, convert the values into an ArrayList.
LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap<String,String>();
/* Populate */
linkedHashMap.put("key0","value0");
linkedHashMap.put("key1","value1");
linkedHashMap.put("key2","value2");
/* Get by position */
int pos = 1;
String value = (new ArrayList<String>(linkedHashMap.values())).get(pos);
HashMaps do not preserve ordering:
This class makes no guarantees as to
the order of the map; in particular,
it does not guarantee that the order
will remain constant over time.
Take a look at LinkedHashMap, which guarantees a predictable iteration order.
If you want to maintain the order in which you added the elements to the map, use LinkedHashMap as opposed to just HashMap.
Here is an approach that will allow you to get a value by its index in the map:
public Object getElementByIndex(LinkedHashMap map,int index){
return map.get( (map.keySet().toArray())[ index ] );
}
If you, for some reason, have to stick with the hashMap, you can convert the keySet to an array and index the keys in the array to get the values in the map like so:
Object[] keys = map.keySet().toArray();
You can then access the map like:
map.get(keys[i]);
Use LinkedHashMap:
Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries.
Use LinkedHashMap and use this function.
private LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>();
Define like this and.
private Entry getEntry(int id){
Iterator iterator = map.entrySet().iterator();
int n = 0;
while(iterator.hasNext()){
Entry entry = (Entry) iterator.next();
if(n == id){
return entry;
}
n ++;
}
return null;
}
The function can return the selected entry.
By default, java LinkedHasMap does not support for getting value by position. So I suggest go with customized IndexedLinkedHashMap
public class IndexedLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
private ArrayList<K> keysList = new ArrayList<>();
public void add(K key, V val) {
super.put(key, val);
keysList.add(key);
}
public void update(K key, V val) {
super.put(key, val);
}
public void removeItemByKey(K key) {
super.remove(key);
keysList.remove(key);
}
public void removeItemByIndex(int index) {
super.remove(keysList.get(index));
keysList.remove(index);
}
public V getItemByIndex(int i) {
return (V) super.get(keysList.get(i));
}
public int getIndexByKey(K key) {
return keysList.indexOf(key);
}
}
Then you can use this customized LinkedHasMap as
IndexedLinkedHashMap<String,UserModel> indexedLinkedHashMap=new IndexedLinkedHashMap<>();
TO add Values
indexedLinkedHashMap.add("key1",UserModel);
To getValue by index
indexedLinkedHashMap.getItemByIndex(position);
I'm assuming by 'position' you're referring to the order in which you've inserted the elements into the HashMap. In that case you want to be using a LinkedHashMap. The LinkedHashMap doesn't offer an accessor method however; you will need to write one like
public Object getElementAt(LinkedHashMap map, int index) {
for (Map.Entry entry : map.entrySet()) {
if (index-- == 0) {
return entry.value();
}
}
return null;
}
Another working approach is transforming map values into an array and then retrieve element at index. Test run of 100 000 element by index searches in LinkedHashMap of 100 000 objects using following approaches led to following results:
//My answer:
public Particle getElementByIndex(LinkedHashMap<Point, Particle> map,int index){
return map.values().toArray(new Particle[map.values().size()])[index];
} //68 965 ms
//Syd Lambert's answer:
public Particle getElementByIndex(LinkedHashMap<Point, Particle> map,int index){
return map.get( (map.keySet().toArray())[ index ] );
} //80 700 ms
All in all retrieving element by index from LinkedHashMap seems to be pretty heavy operation.
HashMap - and the underlying data structure - hash tables, do not have a notion of position. Unlike a LinkedList or Vector, the input key is transformed to a 'bucket' where the value is stored. These buckets are not ordered in a way that makes sense outside the HashMap interface and as such, the items you put into the HashMap are not in order in the sense that you would expect with the other data structures
HashMap has no concept of position so there is no way to get an object by position. Objects in Maps are set and get by keys.
HashMaps don't allow access by position, it only knows about the hash code and and it can retrieve the value if it can calculate the hash code of the key. TreeMaps have a notion of ordering. Linkedhas maps preserve the order in which they entered the map.
you can use below code to get key :
String [] keys = (String[]) item.keySet().toArray(new String[0]);
and get object or list that insert in HashMap with key of this item like this :
item.get(keys[position]);
You can try to implement something like that, look at:
Map<String, Integer> map = new LinkedHashMap<String, Integer>();
map.put("juan", 2);
map.put("pedro", 3);
map.put("pablo", 5);
map.put("iphoncio",9)
List<String> indexes = new ArrayList<String>(map.keySet()); // <== Parse
System.out.println(indexes.indexOf("juan")); // ==> 0
System.out.println(indexes.indexOf("iphoncio")); // ==> 3