I'm using a HashMap and I haven't been able to get a straight answer on how the get() method works in the case of collisions.
Let's say n > 1 objects get placed in the same key. Are they stored in a LinkedList? Are they overwritten so that only the last object placed in that key exists there anymore? Are they using some other collision method?
If they are placed in a LinkedList, is there a way to retrieve that entire list? If not, is there some other built in map for Java in which I can do this?
For my purposes, separate chaining would be ideal, as if there are collisions, I need to be able to look through the list and get information about all the objects in it. What would be the best way to do this in Java?
Thanks for all your help!
The documentation for Hashmap.put() clearly states, "Associates the specified value with the specified key in this map. If the map previously contained a mapping for the key, the old value is replaced"
If you would like to have a list of objects associated with a key, then store a list as the value.
Note that 'collision' generally refers to the internal working of the HashMap, where two keys have the same hash value, not the use of the same key for two different values.
Are they overwritten so that only the last object placed in that key exists there anymore?
Yes, assuming you're putting multiple values with the same key (according to Object.equals, not Object.hashCode.) That's specified in the Map.put javadoc:
If the map previously contained a mapping for the key, the old value is replaced by the specified value.
If you want to map a key to multiple values, you're probably better off using something like Guava's ListMultimap, ArrayListMultimap in specific, which maps keys to lists of values. (Disclosure: I contribute to Guava.) If you can't tolerate a third-party library, then really you have to have a Map<Key, List<Value>>, though that can get a bit unwieldy.
Let's say n > 1 objects get placed in the same key. Are they stored in a linked list? Are they overwritten so that only the last object placed in that key exists there anymore? Are they using some other collision method?
There could be single instance for the same key so the last one overrides the prior one
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1);
map.put("a", 2);// it overrides 1 and puts 2 there
chaining comes where there turns the same hash for different keys
See
Java papers hash table working
Cite: "Let's say n > 1 objects get placed in the same key. Are they stored in a linked list? Are they overwritten so that only the last object placed in that key exists there anymore? Are they using some other collision method?"
Yes, if the hashmap contained something under this key, it will override it.
You can implement your own class to handle that or more simple use a HashMap> in where K is your Key Object and V the object value.
Have in mind that with last solution when you do a map.get(K) will retrieve a List or the implementation that you choose (i.e: ArrayList) so all the methods of this implementation are available for you and perhaps fulfils your requirements. For example if you used Arraylist you have the size, trimToSize, removeRange, etc.
collision resolution for hashing in java is not based on chaining. To my understanding, JDK uses double hashing which is one of the best way of open addressing. So there's no list going to be associated with a hash slot.
You might put the objects for which the hash function resolves to the same key can be put in list and this list can be updated in the table/map.
package hashing;
import java.util.HashMap;
import java.util.Map;
public class MainAnimal {
/**
* #param args
*/
public static void main(String[] args) {
Animal a1 = new Animal(1);
Animal a2 = new Animal(2);
Map<Animal, String> animalsMap = new HashMap<Animal, String>();
animalsMap.put(a1,"1");
animalsMap.put(a2,"2");
System.out.println(animalsMap.get(a1));
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1);
map.put("a", 2);// it overrides 1 and puts 2 there
System.out.println(map.get("a"));
}
}
class Animal {
private int index = 0;
Animal(int index){
this.index = index;
}
public boolean equals(Object obj){
if(obj instanceof Animal) {
Animal animal = (Animal) obj;
if(animal.getIndex()==this.getIndex())
return true;
else
return false;
}
return false;
}
public int hashCode() {
return 0;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
In the above code, am showing two different things.
case 1 - two different instances resolving to same hashkey
case 2 - two same instances acting as keys for two different entries.
Animal instances, a1 & a2 resolves to same key. But they are not overriden. Hashing mechanism probes through the hash slots and places the entries on different slots.
with the second case, keys resolve to same hash key and also the equals method satisfies. Hence overriding happens.
Now if in the animal class I override the equals method this way -
public boolean equals(Object obj){
// if(obj instanceof Animal) {
// Animal animal = (Animal) obj;
// if(animal.getIndex()==this.getIndex())
// return true;
// else
// return false;
// }
// return false;
return true;
}
Overriding happens. The behavior is like using same instance. Since a1 and a2 are in the same bucket and equals return true as well.
Related
I am learning how to code in Java and struggling with this concept.
I need to write a method that takes a char argument which exists within the Objects that are in a map.
It needs to return a currently non existing Set of Objects that contain this char value as a new Set after iterating over the map to find these objects.
The following is my current code which doesn't get my desired outcome, I believe the problem is in the if statement. I have done lots of different combinations of equals() and containsValue on the object and map and cannot seem to get it to return true.
public Set findObj(char aChar)
{
Set<String> objSet = new HashSet();
for (Object Obj: map1.values())
{
if (map1.values().contains(aChar))
{
//if true add to objSet
}
}
return objSet;
}
If it helps the Map is
Map<String,Obj> map1= new HashMap<>();
and the Object is created by a class and contains a 3 variables, 2 string and one being the char value I am trying to find within each iteration of the map.
you are correct the problem is mainly in your if statement, since you're iterating over the objects in the map, you should check if the current object contains the string not whether or not the map as a whole contains it
public Set findObj(char aChar) {
Set<String> objSet = new HashSet();
for (Object c : map1.values()) {
if (c.toString().indexOf(aChar) >= 0) {
//if true add to objSet
}
}
return objSet;
}
I have a map like this with several million entries:
private final Map<String, SomeItem> tops = new HashMap<>();
I need to get list of values, which could be done by calling java.util.Map values() method.
Is Collection of values created every time I call values() method or is it pre-computed from performance perspective?
As my Map has several millions elements, I do not want to create new list object every time values() is called.
Below is the copied implementation of Map.values() in java.util.HashMap:
public Collection<V> values() {
Collection<V> vs = values;
if (vs == null) {
vs = new Values();
values = vs;
}
return vs;
}
This clearly shows that the value collection isn't created unless necessary. So, there should not be additional overhead caused by calls to values()
One important point here may be: It does not matter!
But first, referring to the other answers so far: The collection that is returned there is usually "cached", in that it is lazily created, and afterwards, the same instance will be returned. For example, considering the implementation in the HashMap class:
public Collection<V> values() {
Collection<V> vs;
return (vs = values) == null ? (values = new Values()) : vs;
}
This is even specified (as part of the contract, as an implementation specification) in the documentation of the AbstractMap class (which most Map implementations are based on) :
The collection is created the first time this method is called, and returned in response to all subsequent calls. No synchronization is performed, so there is a slight chance that multiple calls to this method will not all return the same collection.
But now, one could argue that the implementation might change later. The implementation of the HashMap class could change, or one might switch to another Map implementation that does not extend AbstractMap, and which is implemented differently. The fact that it is currently implemented like this is (for itself) no guarantee that it will always be implemented like this.
So the more important point (and the reason why it does not matter) is that the values() method is indeed supposed to return a collection view. As stated in the documentation of the Map interface :
The Map interface provides three collection views, which allow a map's contents to be viewed as a set of keys, collection of values, or set of key-value mappings.
and specifically, the documentation of the Map#values() method :
Returns a Collection view of the values contained in this map. The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa.
I cannot imagine a reasonable way of implementing such a view that involves processing all values of the Map.
So for example, imagine the implementation in HashMap was like this:
public Collection<V> values() {
return new Values();
}
Then it would return a new collection each time that it was called. But creating this collection does not involve processing the values at all.
Or to put it that way: The cost of calling this method is independent of the size of the map. It basically has the cost of a single object creation, regardless of whether the map contains 10 or 10000 elements.
As others have mentioned you can see this by looking at the code. You can also code up a quick example to prove it to yourself. The code below will print true 10 times as the object identity will always be the same for values.
public static void main(String[] args) {
Map<String, String> myMap = new HashMap();
Collection<String> lastValues = myMap.values();
for (int i=0; i < 10; i++) {
System.out.println(lastValues == myMap.values());
lastValues = myMap.values();
}
}
The following code will print true the first time and then false the next 9 times.
public static void main(String[] args) {
Map<String, String> myMap = new HashMap();
Collection<String> lastValues = myMap.values();
for (int i=0; i < 10; i++) {
System.out.println(lastValues == myMap.values());
lastValues = myMap.values();
myMap = new HashMap();
}
}
One more suggestion after reading this thread, if the Map tops declared contents are not changed - you could use google guava ImmutableMap object. For more info- UnmodifiableMap (Java Collections) vs ImmutableMap (Google)
I have a fully working version of MineSweeper implemented in Java. However, I am trying to add an additional feature that updates a Map to store the indexes of the locations of the mines within a 2D array. For example, if location [x][y] holds a mine, I am storing a linked list containing x and y, which maps to a boolean that is true to indicate that the space holds a mine. (This feature is seemingly trivial, but I am just doing this to practice with Collections in Java.)
My relevant private instance variables include:
public Class World{ ...
private LinkedList<Integer> index;
private Map<LinkedList<Integer>, Boolean> revealed;
"index" is the list to be stored in the map as the key for each boolean.
In my constructor I have:
public World(){ ...
tileArr = new Tile[worldWidth][worldHeight];
revealed = new TreeMap<LinkedList<Integer>, Boolean>();
index = new LinkedList<Integer>();
... }
Now, in the method in which I place the mines, I have the following:
private void placeBomb(){
int x = ran.nextInt(worldWidth); //Random stream
int y = ran.nextInt(worldHeight); //Random stream
if (!tileArr[x][y].isBomb()){
tileArr[x][y].setBomb(true);
index.add(x); //ADDED COMPONENT
index.add(y);
revealed.put(index, true);
index.remove(x);
index.remove(y); //END OF ADDED COMPONENT
} else placeBomb();
}
Without the marked added component my program runs fine, and I have a fully working game. However, this addition gives me the following error.
Exception in thread "main" java.lang.ClassCastException: java.util.LinkedList
cannot be cast to java.lang.Comparable
If anyone could help point out where this error might be, it would be very helpful! This is solely for additional practice with collections and is not required to run the game.
There are actually about 3 issues here. One that you know about, one that you don't and a third which is just that using LinkedList as a key for a map is clunky.
The ClassCastException happens because TreeMap is a sorted set and requires that every key in it implement the Comparable interface, or else you have to provide a custom Comparator. LinkedList doesn't implement Comparable, so you get an exception. The solution here could be to use a different map, like HashMap, or you could write a custom Comparator.
A custom Comparator could be like this:
revealed = new TreeMap<List<Integer>, Boolean>(
// sort by x value first
Comparator.comparing( list -> list.get(0) )
// then sort by y if both x values are the same
.thenComparing( list -> list.get(1) )
);
(And I felt compelled to include this, which is a more robust example that isn't dependent on specific elements at specific indexes):
revealed = new TreeMap<>(new Comparator<List<Integer>>() {
#Override
public int compare(List<Integer> lhs, List<Integer> rhs) {
int sizeComp = Integer.compare(lhs.size(), rhs.size());
if (sizeComp != 0) {
return sizeComp;
}
Iterator<Integer> lhsIter = lhs.iterator();
Iterator<Integer> rhsIter = rhs.iterator();
while ( lhsIter.hasNext() && rhsIter.hasNext() ) {
int intComp = lhsIter.next().compareTo( rhsIter.next() );
if (intComp != 0) {
return intComp;
}
}
return 0;
}
});
The issue that you don't know about is that you're only ever adding one LinkedList to the map:
index.add(x);
index.add(y);
// putting index in to the map
// without making a copy
revealed.put(index, true);
// modifying index immediately
// afterwards
index.remove(x);
index.remove(y);
This is unspecified behavior, because you put the key in, then modify it. The documentation for Map says the following about this:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.
What will actually happen (for TreeMap) is that you are always erasing the previous mapping. (For example, the first time you call put, let's say x=0 and y=0. Then the next time around, you set the list so that x=1 and y=1. This also modifies the list inside the map, so that when put is called, it finds there was already a key with x=1 and y=1 and replaces the mapping.)
So you could fix this by saying something like either of the following:
// copying the List called index
revealed.put(new LinkedList<>(index), true);
// this makes more sense to me
revealed.put(Arrays.asList(x, y), true);
However, this leads me to the 3rd point.
There are better ways to do this, if you want practice with collections. One way would be to use a Map<Integer, Map<Integer, Boolean>>, like this:
Map<Integer, Map<Integer, Boolean>> revealed = new HashMap<>();
{
revealed.computeIfAbsent(x, HashMap::new).put(y, true);
// the preceding line is the same as saying
// Map<Integer, Boolean> yMap = revealed.get(x);
// if (yMap == null) {
// yMap = new HashMap<>();
// revealed.put(x, yMap);
// }
// yMap.put(y, true);
}
That is basically like a 2D array, but with a HashMap. (It could make sense if you had a very, very large game board.)
And judging by your description, it sounds like you already know that you could just make a boolean isRevealed; variable in the Tile class.
From the spec of a treemap gives me this:
The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.
The Java Linkedlist can not be compared just like that. You have to give it a way to compare them or just use another type of map, that does not need sorting.
I am building a couple of methods which are supposed to create a cache of input strings, load them in to a list, and then determine the number of occurrences of each string in that list, ranking them in order of the most common elements.
The string, or elements themselves are coming from a JUnit test. It's calling up a method called
lookupDistance(dest)
where "dest" is a String (destination airport code), and the lookupDistance returns the distance between two airport codes....
There's the background. The problem is that I want to load all of the "dest" strings in to a cache. What's the best way to do that?
I have skeleton code that has a method called:
public List<String> mostCommonDestinations()
How would I add "dest" strings to the List in a transparent way? The JUnit test case is only calling lookupDistance(dest), so how can I also redirect those "dest" strings to the List in this method?
How would I then quantify the number of occurrences of each element and say, rank the top three or four?
Have a Map<String, Integer> destinations = new HashMap<>();
In lookupDistance(dest), do something like this (untested pseudocode):
Integer count = destinations.get(dest);
if (count == null) {
destinations.put(dest, Integer.valueOf(1));
} else {
count = Integer.valueOf(count.intValue() + 1);
}
This way, you count the occurences of each dest.
Go through the Map and find the highest counts. That's a bit tricky. One approach might be:
List> list = new ArrayList<>();
list.addAll(destinations.entrySet());
// now you have a list of "entries", each of which maps from dest to its respective counter
// that list now has to be sorted
Collections.sort(list, comparator);
The comparator we used in this invocation has still to be written. It has to take two arguments, which are elements of the list, and compare them according to their counter value. the sort routine will do the rest.
Comparator<Map.Entry<String, Integer>> comparator = new Comparator<>() {
public #Override int compare(Map.Entry<String, Integer> a, Map.Entry<String, Integer> b) {
return a.getValue().intValue() - b.getValue().intValue();
}
}
Ok, so we have a sorted List of Entrys now from which you can pick the top 5 or so. Think that's pretty much it. All this looks more complicated than it should be, so I'm curios for other solutions.
You can add known destination at startup and keep adding new strings to cache as they arrive. That's one way. The other way is to cache strings as they are requested, keeping them for future request. In that case your lookupDistance should also cache string.
Start by making a small class that contains a Hashmap. The key would be your destination string, and the value can either be an object if you want to keep multiple information or just a number specifying how many times that string is used. I would recommend using a data object.
Please note that code below is just to you an idea, more like a pseudo-code.
class Cache {
private Hashmap<String, CacheObject>;
public void Add(string, CacheObject);
public CacheObject Lookup(string);
public CacheObject Remove(string);
public static Cache getInstance(); //single cache
}
class CacheObject {
public int lookupCount;
public int lastUsed;
}
In your lookupDistance you can simply do
if(Cache.getInstance().Lookup(string) == null) {
Cache.getInstance().Add(string, new CacheObject() { 1, Date.now});
}
It puzzles me how the following segment can lead to a null value of the Boolean mandatory, although it is not null at the corresponding key in the actual hashtable:
for (List<List<A>> a : hashMap.keySet()) {
Boolean mandatory = hashMap.get(a);
}
A HashMap will return null if the key specified is not bound to a value.
Issue is almost certainly that comparison op on a -- a List -- against keys is failing.
Let me guess: are you modifying these lists (the key object) after you have called a put? Did you remove all entries in one of the keys? Remember an empty list is equal to all empty ArrayLists. Further remember that List.equals() compares list content (one by one) to test equality.
package sof_6462281;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Demonstrate the fact that the Map uses key.equals(k) to
* test for key equality. Further demonstrate that it is a
* very bad idea to use mutable collections are keys to maps.
*/
public class ListAsKey {
public static void main(String[] args) {
Map<List<A>, Boolean> map = new HashMap<List<A>, Boolean>();
List<A> alist = new ArrayList<A>();
map.put(alist, true);
for (List<A> a : map.keySet()) {
Boolean b = map.get(a);
System.out.format("\t%s(ArrayList#%d) => %s\n",a, a.hashCode(), map.get(a));
}
// you changed your list after the put, didn't you?
alist.add(new A());
for (List<A> a : map.keySet()) {
Boolean b = map.get(a);
System.out.format("\t%s(ArrayList#%d) => %s\n",a, a.hashCode(), map.get(a));
}
alist.clear();
for (List<A> a : map.keySet()) {
Boolean b = map.get(a);
System.out.format("\t%s(ArrayList#%d) => %s\n",a, a.hashCode(), map.get(a));
}
}
public static final class A { /* foo */ }
}
Results:
[](ArrayList#1) => true
[sof_6462281.ListAsKey$A#4b71bbc9](ArrayList#1265744872) => null
[](ArrayList#1) => true
edit: added more ops to above and added console out.
Boolean can be null because it wraps the value-type primitive boolean. I am unsure what you mean its not null at the corresponding key in the actual hashtable. You are iterating over the keys then getting the values at those keys. The value at a key was inserted as null so when you are retrieving it you are getting the null.
Using a mutable object for a Map key is always a dangerous thing. If you maintain any reference to any of those keys after inserting into the map, then it is very likely that one of those keys will be modified at some point in the future which will invalidate the contents of your map.
A less likely, but possible scenario, even assuming you somehow don't screw up your List<List<>> key is if you have messed up the equals method of class A, then your Lists' equals method will also be messed up, again screwing up your map.
Look at alphazero's nice code example if you need further proof that what you are attempting to do is a bad idea.