How to make a key have multiple values in a Hashtable? [duplicate] - java

This question already has answers here:
HashMap with multiple values under the same key
(21 answers)
Closed 6 years ago.
I have 100 entries and I have to have to hash these into a hashtable of a limited size.
I know how to work with the first entry, ht.put(k,v) does the trick.
But as soon as I want to add another value to it, the old one gets overwritten. I don't want to do that, I want to append it in a linkedlist or arraylist.
Hashtable<Integer,Integer> ht = new Hashtable<Integer,Integer>(211);
ht.put(1, 40);
ht.put (1, 60);
System.out.println(ht.get(1));
// output is 60
How to make it both 40 and 60?

You can have List as value type like:
Hashtable<Integer,List<Integer>> ht = new Hashtable<Integer,List<Integer>>(211);
And your put operation would look like:
public static void put(Hashtable<Integer,List<Integer>> ht, int key, int value) {
List<Integer> list = ht.get(key);
if (list == null) {
list = new ArrayList<Integer>();
ht.put(key, list);
}
list.add(value);
}
[UPDATE1]
If you want you can make your one extension of Hashtable like:
public class MyHashtable extends Hashtable<Integer,List<Integer>> {
public MyHashtable(...) { // add params if needed
super(...);
}
// with additional method:
public static void putOne(int key, int value) {
List<Integer> list = this.get(key);
if (list == null) {
list = new ArrayList<Integer>();
this.put(key, list);
}
list.add(value);
}
}

You need linear probing http://www.sanfoundry.com/java-program-implement-hash-tables-linear-probing/
It's not possible to store more than one value in a cell of a hash table
When trying to map a new key to an already occupied cell this is called a collision.
There are a few algorithm schemes to try and work around collision, one is Linear probing - which finds the next most appropriate free space for the key to be stored

The data structure you are looking for is called Multi Map. By definition it has different interface than a map, because it allows multiple values associated with the same key.
There's no standard library implementation for this data structure yet. But you can find good ones in some open source libraries:
Guava
Apache Commons Collections

Multimap (https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/collect/Multimap.html) should help if you are allowed to use it.
Alternatively, you could use Map<Integer, List<Integer>>.

You are using same key (1), which is not what you wanted, unless you wanted to add more values to the same key, in that case have hashtable of list of arrays HashMap<Integer,List<Integer>> integerArrayMap.
In Hashtable, the Key MUST be unique, as you are NOT using unique keys, the same value is being replaced. so try to put the values with different keys.
ht.put(1, 40);
ht.put (2, 60);
I suggest you to refer the Hashtable api here:
https://docs.oracle.com/javase/7/docs/api/java/util/Hashtable.html

Related

Duplicate element search and replacement logic in HashMap

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.

What is HashCollision in java? How to get recovery from this? How to get the element in the second bucket because of collision?

public void hashcollision(){
Map m=new HashMap();
m.put(1,2);
m.put(1,3);
system.out.println(m.get(1));
}
o/p will be 3 , How to get access to 2.
You need to start by working out what exactly you're wanting to achieve, and whether a HashMap is the right tool for the job.
A HashMap is a type of Map, and the point of a Map is to allow you to assign a value to a key. Think of it as a row of boxes. Each box has a label on it (that's the key) and something inside it (that's the value).
When you put something into the Map, you specify a key and a value:
map.put(1,2);
This means: find the box labelled 1, and throw away whatever's inside it, and replace its contents with 2. If there's no box labelled 1, then label a new box 1, and put 2 inside it.
The point is that because this throws away whatever was already in that box, your code does this:
map.put(1,2); //first time, so label a new box with 1, and put 2 inside it
map.put(1,3); //already a box labelled 1, so throw its contents away and put 3 inside
The 2 is no longer stored at all. It can't be retrieved because it's not there.
If you're trying to get out a value you've overwritten, what that suggests to me is that you didn't really want a HashMap in the first place, and you'd have been better off with something that allows you to store multiple values for a given key. There are things that do that, but they're not so common as a generic Map. It might be that you just need to rethink your code and what you're trying to do.
But forget about hash collisions: they're something that's internal to HashMap, and they get sorted out for you by the HashMap so that you don't really have to worry about them. They're nothing to do with what's going on here.
The previous value associated with the key is returned by the put call. See: The Documentation for HashMap
Thus, if you want the old one:
public void hashCollision() {
Map m = new HashMap();
m.put(1,2);
int oldVal = (int)m.put(1,3);
}
Note that you should check that for null which I'm not doing here to save space and time. If the key was not previously in use, you'll get null back (which won't cast to an int like this). Also, use the generics. That's why they're there.
You can't get access to to '2' because in Java, the first value associated with a given key is over-written if you insert a second value associated with the same key. This technically isn't a hash collision. A hash collision occurs when DIFFERENT keys hash to the same bucket.
You could use ConcurrentHashMap and use putIfAbsent which will put into map if and only if its not present so in your case output will be 2. See here http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentHashMap.html#putIfAbsent%28K,%20V%29
Or try to save value when doing put to get previous value i.e.
int previousValue = map.put(1, 3);
System.out.println(previousValue);
See here for details http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html#put%28K,%20V%29
Use a multimap:
final Map<Integer, List<Integer>> myMap = new HashMap<>();
public void addToMap(final Integer key, final Integer value) {
if (myMap.get(key) == null) {
myMap.put(key, new ArrayList<Integer>());
}
myMap.get(key).add(value);
}
public List<Integer> getFromMap(final Integer key) {
return myMap.get(key);
}
public Integer getFirstElementFromMap(final Integer key) {
return myMap.get(key).get(0);
}
If you don't want to make your own implementation of a multi-map, you can use the one from commons-collections or google guava (better):
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;
//...
final Multimap<Integer, Integer> myMap = LinkedListMultimap.create();
myMap.put(1, 2);
myMap.put(1, 3);
System.out.println(myMap.get(1));
//Prints [2, 3]

HashMap overrides the nextvalue Java

Lets say I have hashmap store and it contains for example-(11,name1) (11,name2) and i call HashMap.get(11), it only shows name2 which means it overrides the first input for 11. How can i store both name1 and name2 with ID 11 using hashmap?I know i can use both HashMap and HashSet but i dont want to create every HashSet for HashMap. I just want to use hashSet only. how should I do this? I hope you can help me with it. Thank you.
public void insert(int ID, String key){
int hashKey = Hash(key);
System.out.println("Hash Key" + hashKey);
int node = Find(ID,hashKey);
storeR.put(node, key);
}
You can use:
HashMap<Integer, List<String>>
In HashMap you must put a value with every key. So of course, if you put the same key twice, the value will be override.
The solution is to hold a collection of values for every key.
in your code instead of:
storeR.put(node, key);
you should write:
List<String> nodeValues = storeR.get(node);
if (nodeValues == null) {
nodeValues = new ArrayList<String>();
storeR.put(node, nodeValues );
}
nodeValues.add(key);
And you should also change storeR type to be HashMap<Integer, List<String>>
MultiMap is also a similar solution.
You can probably use MultiMap from Apache Commons Collections.
You will have to either have a HashMap where the value of each key is another collection (list or set) or concatenate the string values together (e.g. comma separated).
Alternatively you may be able to find a data collection that supports multiple values per key.
To store multiple values for a single key, use a HashMap that contains a list as a value. HashMap's implementation overrides values for existing keys.
HashMap<Integer,List<String>>
Also, you could use MultiMap from Apache Commons or, if you're just using Integer I can suggest you use an array directly:
List<String>[] yourList = new List<String>[initCapacity];
So you can access that list like this:
yourList[0].add("A New Value");
As a final note, you can use any collection you deem appropiate, even a HashSet if performance is important for you and you won't store duplicated values for a same index.

Java Hashtable problem

I am having some problem with java hashtable. Following is my hastable key and values
{corpus\2.txt=[cat sparrow], corpus\4.txt=[elephant sparrow], corpus\1.txt=[elephant cow], corpus\3.txt=[cow cat]}
So if i want to access first tuple i have to pass key "corpus\2.txt" to get its value. If i pass value i can get it's key. But I want to make a function I pass like 1 2 3 4 etc. and get both key and value. Any idea?
2nd question:
Is it possible to store an index with key and value too?? Or is it possible to get index ( 0,1,2,3 etc. ) from existing hashtable?
Thanks !
For starters, I would use a HashMap, rather than the (now obsolete) HashTable. If you do that, then you can use Map.Entry to return a key/value pair (as per your first question).
You can't easily store an index with your key. You might want to create a special Key object thus:
public class Key {
private String name;
private int index;
....
}
with a suitable equals()/hashCode() implementation (as pointed out below in the comments) and use that as the key in your HashMap. You've have to perform a lookup using this key and thus construct one from your current String-based key, but I don't think that's a big deal.
There is no method in the API to get a specific entry from a Java hash table. You can access the collection of all entries with the entrySet method, and iterating over that you will get all the key-value pairs as Map.Entry objects.
Hash tables are completely unordered. They are just mappings from keys to values and do not have any definite indices. There is a specific order that the entries will be processed if you iterate over the entrySet result, but this might also change when you modify the hash table.
Take a look at LinkedHashMap, a map implementation that preserves input ordering.
Rather use a Map<Integer, ValueObject> wherein ValueObject is just a custom javabean class with two properties e.g. filename and description.
Basic kickoff example:
public class ValueObject {
private String filename;
private String description;
public ValueObject() {
// Always keep default constructor alive.
}
public ValueObject(String filename, String description) {
this.filename = filename;
this.description = description;
}
// Add/generate public getters and setters for filename and description.
}
which you can use as follows:
Map<Integer, ValueObject> map = new HashMap<Integer, ValueObject>();
map.put(1, new ValueObject("corpus1.txt", "elephant cow"));
map.put(2, new ValueObject("corpus2.txt", "cat sparrow"));
map.put(3, new ValueObject("corpus3.txt", "cow cat"));
map.put(4, new ValueObject("corpus4.txt", "elephant sparrow"));
ValueObject vo = map.get(1); // Returns VO with corpus1.txt and elephant cow.
There's no way to access a Map by index. However, if what you really want to do is access the key-value pairs in the map one by one, you can just do:
for (Map.Entry<String, List<String>> nameAndWords: hashmap) {
String name = nameAndWords.getKey();
List<String> words = nameAndWords.getValue();
// do your stuff here
}
If you actually need indexing, you can add an external order to the map by keeping the keys in a list, which must be updated when you edit the map:
HashMap<String, List<String>> wordsByCorpus;
List<String> corpusNames;
public void addCorpus(String name, List<String> words) {
List<String> oldValue = wordsByCorpus.put(name, words);
if (oldValue == null) corpusNames.add(name);
}
public void removeCorpus(String name) {
wordsByCorpus.remove(name);
corpusNames.remove(name);
}
public Map.Entry<String, List<String>> getCorpus(int i) {
String name = corpusNames.get(i);
List<String> words = wordsByCorpus.get(name);
return wordsByCorpus.new SimpleImmutableEntry(name, words); // 1.6 only!
}
You either want to use a LinkedHashMap which allows you to access values added to the map using the index of the order they were added in.
Or you want to use 2 HashMaps. One to index by the string value and the second one to convert the integer value into the string value key of the first map. Then simple to get key and value from index:
String key = mapByIntToStringKey.get(index);
V value = mapByStringKey.get(key);
// now have both key and value, no linear searching so should be fast
Thus your maps would contain:
mapByStringKey={corpus\2.txt=[cat sparrow], corpus\4.txt=[elephant sparrow], corpus\1.txt=[elephant cow], corpus\3.txt=[cow cat]}
mapByIntToStringKey{2=corpus\2.txt, 4=corpus\4.txt, 1=corpus\1.txt}
although this is assuming that all your keys are not simply "corpus"+index+".txt".
If all keys are as above then if the indexes are not sparse then you could use a simple ArrayList (previously mentioned) and use get(index) which is fast (directly looks up in an array, can't get much faster than that), and then reconstruct the string key using the expression above.
If the indexes are sparse (i.e. some are missing, there are gaps) then just use the mapByIntToStringKey but replace with mapByIntToValue and reconstruct any string key you need using previous string expression.
The current high answer seems very odd to me, in that the suggestion is to key the map using the int index bit only of a compound key. Unless I'm reading it wrong, it means that you loose the ability to lookup values in the map using the string key alone or maybe just implies that you can always deduce the int index from the string key.

Best way to create a hashmap of arraylist

I have one million rows of data in .txt format. the format is very simple. For each row:
user1,value1
user2,value2
user3,value3
user1,value4
...
You know what I mean. For each user, it could appear many times, or appear only once (you never know). I need to find out all the values for each user. Because user may appear randomly, I used Hashmap to do it. That is: HashMap(key: String, value: ArrayList). But to add data to the arrayList, I have to constantly use HashMap get(key) to get the arrayList, add value to it, then put it back to HashMap. I feel it is not that very efficient. Anybody knows a better way to do that?
You don't need to re-add the ArrayList back to your Map. If the ArrayList already exists then just add your value to it.
An improved implementation might look like:
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
while processing each line:
String user = user field from line
String value = value field from line
Collection<String> values = map.get(user);
if (values==null) {
values = new ArrayList<String>();
map.put(user, values)
}
values.add(value);
Follow-up April 2014 - I wrote the original answer back in 2009 when my knowledge of Google Guava was limited. In light of all that Google Guava does, I now recommend using its Multimap instead of reinvent it.
Multimap<String, String> values = HashMultimap.create();
values.put("user1", "value1");
values.put("user2", "value2");
values.put("user3", "value3");
values.put("user1", "value4");
System.out.println(values.get("user1"));
System.out.println(values.get("user2"));
System.out.println(values.get("user3"));
Outputs:
[value4, value1]
[value2]
[value3]
Use Multimap from Google Collections. It allows multiple values for the same key
https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html
Since Java 8 you can use map.computeIfAbsent
https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#computeIfAbsent-K-java.util.function.Function-
Collection<String> values = map.computeIfAbsent(user, k -> new ArrayList<>());
values.add(value);
The ArrayList values in your HashMap are references. You don't need to "put it back to HashMap". You're operating on the object that already exists as a value in the HashMap.
If you don't want to import a library.
package util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* A simple implementation of a MultiMap. This implementation allows duplicate elements in the the
* values. (I know classes like this are out there but the ones available to me didn't work).
*/
public class MultiMap<K, V> extends HashMap<K, List<V>> {
/**
* Looks for a list that is mapped to the given key. If there is not one then a new one is created
* mapped and has the value added to it.
*
* #param key
* #param value
* #return true if the list has already been created, false if a new list is created.
*/
public boolean putOne(K key, V value) {
if (this.containsKey(key)) {
this.get(key).add(value);
return true;
} else {
List<V> values = new ArrayList<>();
values.add(value);
this.put(key, values);
return false;
}
}
}
i think what you want is the Multimap. You can get it from apache's commons collection, or google-collections.
http://commons.apache.org/collections/
http://code.google.com/p/google-collections/
"collection similar to a Map, but
which may associate multiple values
with a single key. If you call put(K,
V) twice, with the same key but
different values, the multimap
contains mappings from the key to both
values."
I Could not find any easy way. MultiMap is not always an option available. So I wrote something this.
public class Context<K, V> extends HashMap<K, V> {
public V addMulti(K paramK, V paramV) {
V value = get(paramK);
if (value == null) {
List<V> list = new ArrayList<V>();
list.add(paramV);
put(paramK, paramV);
} else if (value instanceof List<?>) {
((List<V>)value).add(paramV);
} else {
List<V> list = new ArrayList<V>();
list.add(value);
list.add(paramV);
put(paramK, (V) list);
}
return paramV;
}
}
it would be faster if you used a LinkedList instead of an ArrayList, as the ArrayList will need to resize when it nears capacity.
you will also want to appropriately estimate the capacity of the wrapping collection (HashMap or Multimap) you are creating to avoid repetitive rehashing.
As already mentioned, MultiMap is your best option.
Depending on your business requirements or constraints on the data file, you may want to consider doing a one-off sorting of it, to make it more optimised for loading.

Categories

Resources