Java HashMap with Int Array - java

I am using this code to check that array is present in the HashMap:
public class Test {
public static void main(String[] arg) {
HashMap<int[], String> map = new HashMap<int[], String>();
map.put(new int[]{1, 2}, "sun");
System.out.println(map.containsKey((new int[]{1, 2})));
}
}
But this prints False. How can I check that array is present in the HashMap?

The problem is because the two int[] aren't equal.
System.out.println(
(new int[] { 1, 2 }).equals(new int[] { 1, 2 })
); // prints "false"
Map and other Java Collections Framework classes defines its interface in terms of equals. From Map API:
Many methods in Collections Framework interfaces are defined in terms of the equals method. For example, the specification for the containsKey(Object key) method says: "returns true if and only if this map contains a mapping for a key k such that (key==null ? k==null : key.equals(k))."
Note that they don't have to be the same object; they just have to be equals. Arrays in Java extends from Object, whose default implementation of equals returns true only on object identity; hence why it prints false in above snippet.
You can solve your problem in one of many ways:
Define your own wrapper class for arrays whose equals uses java.util.Arrays equals/deepEquals method.
And don't forget that when you #Override equals(Object), you must also #Override hashCode
Use something like List<Integer> that does define equals in terms of the values they contain
Or, if you can work with reference equality for equals, you can just stick with what you have. Just as you shouldn't expect the above snippet to ever print true, you shouldn't ever expect to be able to find your arrays by its values alone; you must hang-on to and use the original references every time.
See also:
Overriding equals and hashCode in Java
How to ensure hashCode() is consistent with equals()?
Understanding the workings of equals and hashCode in a HashMap
API
Object.equals and Object.hashCode
It's essential for a Java programmer to be aware of these contracts and how to make them work with/for the rest of the system

You are comparing two different references.
Something like this will work:
public class Test {
public static void main(String[] arg)
{
HashMap<int[],String> map= new HashMap<int[],String>();
int[] a = new int[]{1,2};
map.put(a, "sun");
System.out.println(map.containsKey(a));
}
}
Since a is the same reference, you will receive true as expected. If your application has no option of passing references to do the comparison, I would make a new object type which contains the int[] and override the equals() method (don't forget to override hashCode() at the same time), so that will reflect in the containsKey() call.

I would use a different approach. As mentioned before, the problem is with arrays equality, which is based on reference equality and makes your map useless for your needs.
Another potential problem, assuming that you use ArrayList instead, is the problem of consistency: if you change a list after is has been added to the map, you will have a hashmap corruption since the position of the list will not reflect its hashcode anymore.
In order to solve these two problems, I would use some kind of immutable list. You may want to make an immutable wrapper on int array for example, and implement equals() and hashCode() yourself.

I think the problem is your array is doing an '==' comparison, i.e. it's checking the reference. When you do containsKey(new int[] { ... }), it's creating a new object and thus the reference is not the same.
If you change the array type to something like ArrayList<Integer> that should work, however I would tend to avoid using Lists as map keys as this is not going to be very efficient.

The hashCode() implementation for arrays is derived from Object.hashCode(), so it depends on the memory location of the array. Since the two arrays are instantiated separately, they have different memory locations and thus different hashcodes. If you made one array it would work:
int[] arr = {1, 2};
map.put(arr, "sun");
System.out.println(map.containsKey(arr));

You've got two different objects that happen to contain the same values, because you've called new twice.
One approach you might use is to create a "holder" class of your own, and define that class's equals and hash methods.

Are you sure you don't want to map Strings to arrays instead of the other way around?
Anyway, to answer your question, the problem is you are creating a new array when you call containsKey(). This returns false between you you have two separately newed arrays that happen to have the same elements and dimension. See Yuval's answer to see the correct way of checking if an array is contained as a key.
An alternative, more advanced, approach is to create your own class that wraps an array and overwrites hashCode() so that two arrays with the same dimension and elements will have equal hash codes.

The two instances of int[] are different and not equal.
A nice approach would be to convert the int array to String using Arrays.toString(arr):
HashMap<String, String> h = new HashMap<>();
int[] a = new int[]{1, 2};
h.put(Arrays.toString(a), "sun");
h.get(Arrays.toString(new int[]{1, 2})); // returns sun

Related

What happens if we override only hashCode() in a class and use it in a Set?

This may not be the real world scenario but just curious to know what happens, below is the code.
I am creating a set of object of class UsingSet.
According to hashing concept in Java, when I first add object which contains "a", it will create a bucket with hashcode 97 and put the object inside it.
Again when it encounters an object with "a", it will call the overridden hashcode method in the class UsingSet and it will get hashcode 97 so what is next?
As I have not overridden equals method, the default implementation will return false. So where will be the Object with value "a" be kept, in the same bucket where the previous object with hashcode 97 kept? or will it create new bucket?
anybody know how it will be stored internally?
/* package whatever; // don't place package name! */
import java.util.*;
import java.lang.*;
import java.io.*;
class UsingSet {
String value;
public UsingSet(String value){
this.value = value;
}
public String toString() {
return value;
}
public int hashCode() {
int hash = value.hashCode();
System.out.println("hashcode called" + hash);
return hash;
}
public static void main(String args[]) {
java.util.Set s = new java.util.HashSet();
s.add(new UsingSet("A"));
s.add(new UsingSet("b"));
s.add(new UsingSet("a"));
s.add(new UsingSet("b"));
s.add(new UsingSet("a"));
s.add(new Integer(1));
s.add(new Integer(1));
System.out.println("s = " + s);
}
}
output is:
hashcode called65
hashcode called98
hashcode called97
hashcode called98
hashcode called97
s = [1, b, b, A, a, a]
HashCode & Equals methods
Only Override HashCode, Use the default Equals:
Only the references to the same object will return true. In other words, those objects you expected to be equal will not be equal by calling the equals method.
Only Override Equals, Use the default HashCode: There might be duplicates in the HashMap or HashSet. We write the equals method and expect{"abc", "ABC"} to be equals. However, when using a HashMap, they might appear in different buckets, thus the contains() method will not detect them each other.
James Large answer is incorrect, or rather misleading (and part incorrect as well). I will explain.
If two objects are equal according to their equals() method, they must also have the same hash code.
If two objects have the same hash code, they do NOT have to be equal too.
Here is the actual wording from the java.util.Object documentation:
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
It is true, that if two objects don't have the same hash then they are not equal. However, hashing is not a way to check equality - so it is wildly incorrect to say that it is a faster way to check equality.
Also, it is also wildly incorrect to say the hashCode function is an efficient way to do anything. This is all up to implementation, but the default implementation for hashCode of a string is very inefficient as the String gets large. It will perform a calculation based on each char of the String, so if you are using large Strings as keys, then this becomes very inefficient; moreso if you have a large number of buckets.
In a Map (HashSet uses a HashMap internally), there are buckets and in each bucket is a linked list. Java uses the hashCode() function to find out which bucket it belongs in (it actually will modify the hash, depending on how many buckets exist). Since two objects may share the same hash, it will iterate through the linked list sequentially next, checking the equals() method to see if the object is a duplicate. Per the java.util.Set documenation:
A collection that contains no duplicate elements.
So, if its hashCode() leads it to a bucket, in which that bucket contains an Object where the .equals() evaluates to true, then the previous Object is overwritten with the new Object. You can probably view here for more information:
How does a Java HashMap handle different objects with the same hash code?
Generally speaking though, it is good practice that if you overwrite the hashCode function, you also overwrite the equals function (if I'm not mistaken, this breaks the contract if you choose not to).
Simply you can Assume hashcode and equals methods as a 2D search like:-
Where Hashcode is the Rows and the object list is the Column.
Consider the following class structure.
public class obj
{
int Id;
String name;
public obj(String name,int id)
{
this.id=id;
this.name=name;
}
}
now if you create the objects like this:-
obj obj1=new obj("Hassu",1);
obj obj2=new obj("Hoor",2);
obj obj3=new obj("Heniel",3);
obj obj4=new obj("Hameed",4);
obj obj5=new obj("Hassu",1);
and you place this objects in map like this :-
HashMap hMap=new HashMap();
1. hMap.put(obj1,"value1");
2. hMap.put(obj2,"value2");
3. hMap.put(obj3,"value3");
4. hMap.put(obj4,"value4");
5. hMap.put(obj5,"value5");
now if you have not override the hashcode and equals then after putting all the objects till line 5 if you put obj5 in the map as By Default HashCode you get different hashCode so the row(Bucket will be different).
So in runtime memory it will be stored like this.
|hashcode | Objects
|-----------| ---------
|000562 | obj1
|000552 | obj2
|000588 | obj3
|000546 | obj4
|000501 | obj5
Now if you create the same object Like :-
obj obj6 = new obj("hassu",1);
And if you search for this value in the map.like
if(hMap.conaints(obj6))
or
hMpa.get(obj 6);
though the key(obj1) with the same content is available you will get false and null respectively.
Now if you override only equals method.
and perform the same content search key will also get the Null as the HashCode for obj6 is different and in that hashcode you wont find any key.
Now if you override only hashCode method.
You will get the same bucket (HashCode row) but the content cant be checked and it will take the reference checked implementation by Super Object Class.
SO here if you search for the key hMap.get(obj6) you will get the correct hashcode:- 000562 but as the reference for both obj1 and obj6 is different you will get null.
Set will behave differently.
Uniqueness wont happen. Because unique will be achieved by both hashcode and equals methods.
output will be liked this s = [A, a, b, 1] instead of early one.
Apart that remove and contains all wont work.
Without looking at your code...
The whole point of hash codes is to speed up the process of testing two objects for equality. It can be costly to test whether two large, complex objects are equal, but it is trivially easy to compare their hash codes, and hash codes can be pre-computed.
The rule is: If two objects don't have the same hash code, that means they are not equal. No need to do the expensive equality test.
So, the answer to the question in your title: If you define an equals() method that says object A is equal to object B, and you define a hashCode() method that says object A is not equal to object B (i.e., it says they have different hash codes), and then you hand those two objects to some library that cares whether they are equal or not (e.g., if you put them in a hash table), then the behavior of the library is going to be undefined (i.e., probably wrong).
Added information: Wow! I really missed seeing the forest for the trees here---thinking about the purpose of hashCode() without putting it in the context of HashMap. If m is a Map with N entries, and k is a key; what is the purpose of calling m.get(k)? The purpose, obviously, is to search the map for an entry whose key is equal to k.
What if hash codes and hash maps had not been invented? Well the best you could do, assuming that the keys have a natural, total order, is to search a TreeMap, comparing the given key for equality with O(log(N)) other keys. In the worst case, where the keys have no order, you would have to compare the given key for equality with every key in the map until you either find a match or tested them all. In other words, the complexity of m.get(k) would be O(N).
When m is a HashMap, the complexity of m.get(k) is O(1), whether the keys can be ordered or not.
So, I messed up by saying that the point of hash codes was to speed up the process of testing two objects for equality. It's really about testing an object for equality with a whole collection of other objects. That's where comparing hash codes doesn't just help a little; It helps by orders of magnitude...
...If the k.hashCode() and k.equals(o) methods obey the rule: j.hashCode()!=k.hashCode() implies !j.equals(k).

Adding arrays with same values to HashSet results in duplicate items

I'm trying to create a set of arrays of ints, the thing is that if I try to do:
HashSet<int[]> s = new HashSet<int[]>();
int a1[] = {1,2,3};
int a2[] = {1,2,3};
s.add(a1);
s.add(a2)
System.out.println(s.size());
Then s has two objects, but there should be only one.
Note: it doesn't matter if it is HashSet< Integer[]>. It just doesn't work.
Now If I try to do this with an ArrayList< Integer>, something like:
HashSet<ArrayList<Integer>> s = new HashSet<ArrayList<Integer>>();
ArrayList<Integer> a1 = new ArrayList<Integer>();
ArrayList<Integer> a2 = new ArrayList<Integer>();
a1.add(1);
a1.add(2);
a1.add(3);
a2.add(1);
a2.add(2);
a2.add(3);
s.add(a1);
s.add(a2)
System.out.println(s.size());
Then s has one object.
I though a way to avoid the error in the first code and was storing hashcodes of each array in a hashset as follows:
int a1[] = {0,10083,10084,1,0,1,10083,0,0,0,0};
int a2[] = {1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,0,2112};
HashSet<Integer> s= new HashSet<Integer>();//hashcodes of each array
s.add(Arrays.hashCode(a1));
s.add(Arrays.hashCode(a2));
System.out.println(Arrays.hashCode(a1));
System.out.println(Arrays.hashCode(a2));
System.out.println(s.size());
It works for the first case(1,2,3) but in cases where there are collisions it doesn't work, so I would have to manage collisions. So, I think that what I am doing is implementing a HashSet by myself.
With HashSet< ArrayList< Integer>> it works perfectly. I suppose that java manage collisions in that case.
My question is why java does not allow to manage a HashSet< int[]> or HashSet< Integer[]> if hashcodes generated are the same as in ArrayList< Integer> and hashcodes of arrays can be calculated simply by calling Arrays.hashCode(...).
And finally, if I want to do a HashSet< int[]>(or HashSet< Integer[]>) I would have to implement it by myself? Or there is a better way to do it?
Thanks.
UPDATE: Ok, finally I think I have came to a complete answer. As #ZiyaoWei and #user1676075 commented it doesn't work because equals returns false and hashcodes are differents. But, why java does not override this methods(with Arrays.equals(), Arrays.hashCode()) so one can do something like HashSet< int[]>? The answer is because an array is a mutable object, and hashcode can not depend on mutable values(each element of array is a mutable value) according to the general contract of hashcode. Mutable objects and hashCode
Here nice explanations of using mutable fields in hashCode http://blog.mgm-tp.com/2012/03/hashset-java-puzzler/ and mutable keys in hashmaps Are mutable hashmap keys a dangerous practice?
My answer is, if you want to use a HashSet< int[]> you have to create a class that has an array and if you want that hashcode and equals to depend on values, override methods equals() and hashCode() with Arrays.equals() and Arrays.hashCode(). If you don't want to violate the contract just make the array final.
Thanks everyone!
It has nothing to do with collision at the end of the day:
a1.equals(a2) == false
Since they are not equal, a Set will treat them as different.
Note Array in Java does not override the equals method from Object.
And since add in Set is defined as
More formally, adds the specified element e to this set if the set contains no element e2 such that (e==null ? e2==null : e.equals(e2))
is seems to be impossible to properly implement a Set that might meet your requirement (compare elements with Arrays.equals) without violating some contracts.
The reason the HashSet> works is because the HashSet will use .equals() comparison to decide if you're inserting the same object twice. In the case of List, two lists of the same base type (e.g. ArrayList) with the same content, in the same order, will compare as equal. Thus you're telling the HashSet to insert the same object twice. It only takes a single instance once.
When you try to do that same operation with an array. See this post: equals vs Arrays.equals in Java for more details about array comparisons in Java. When you insert two arrays, the default .equals() tests if they are the same object, which they are not. Thus it fails.

HashMap with multiple valued keys

Is it possible to create a HashMap whose key is an array of integers?
I´m accustomed to use Python and just recently I began working with Java. In my job I need to create a HashMap with keys like:
map.put([5,2], 1);
map.put([3,2], 0);
and so on. I´m using it to later on test if a pair of those numbers are present in the map, if yes, then do something, if not, continue.
To do so I tried the following:
Map<Array, Boolean> test = new HashMap<Array, Boolean>();
int[] a1 = {5,2};
test.put(a1, true);
Eclipse gives the message ("The arguments are not applicable to int[]..."). But any configuration I´ve done I get some error.
I tried using ArrayList, Objects inside the map, nested HashMap and so on but none worked (in python it´s very easy, I´d just write dict[(5,2)] = 1, so I imagine in Java there´s something simple like that). I was suggested to transform the numbers into Strings and add a colon between then, like:
map.put("5:2", 1);
and later on I break the string again but if this is the solution I´ll go back to Python ;)!!
Probably this is a very simple question but I couldn´t find the answer, hope you can help me.
Thanks in advance!
If you want to check for the existance of your entry, you can use a Set (a useful concrete implementation is HashSet.
final Set<List<Integer>> population;
population = new HashSet<List<Integer>>();
population.add(Arrays.asList(1, 2));
if (population.contains(Arrays.asList(1, 2)))
{
...
}
You can use an List as I have done above - but that doesn't guarantee that all your lists are exactly two elements long (if that is indeed a constraint). To make it a bit more robust, you could create your own class to represent the tuple. If you do, make sure you implement equals() and hashCode() (here's an article explaining good practice).
Arrays.asList() is a useful way of creating a list in-line in the code. A more general list is an ArrayList.
The simplest thing would be a Map<List<Integer>, Boolean> -- or even just a Set<List<Integer>>, since you don't care about the value as much as whether the key is there.
The more java-y solution would be some class that represents the two ints:
public class Coordinate { // or whatever
private final int x;
private final int y;
// constructor and overrides for equals, hashCode and toString
}
Then have a Set<Coordinate>.
This is considered more idiomatically Java because the class name tells you exactly what this set is for -- and enforces that it's used that way. A Set<List<Integer>>, on the other hand, could be lots of things: coordinates, lottery pickings, SSNs for people in a given department, credit cards for a payment... you the programmer have no way of knowing just by looking at the type, and it's easy for a set of numbers that means on thing to be accidentally used in another context. A Set<Coordinate> can't be anything other than a set of coordinates.
This works:
Map<List<Integer>,Boolean> map = new HashMap<>();
map.put( Arrays.asList( new Integer(1), new Integer(2) ), false );
map.put( Arrays.asList( 4, 5 ), true ); // Integer type is inferred and ints are autoboxed
map.get( Arrays.asList( 1, 2 ) ); // gets the Boolean object for "false"
Actually java is a lot less expressive than python so you'll have to write a bit more code.
I think you need to use a map with an ArrayList in the following way:
ArrayList al = map.get("key");
if (al == null) {
al = new ArrayList();
map.put("key", al)
}
al.add(some_object);
You also may use the array as the key (as you asked for), probably you want an immutable array. The key for the hash map to work is use some object that has a good implementation of equals and hashCode.
This is the way that it is done in java, you may use any kind of collection though, Sets are more common.
Cheers!

Use map to check whether contains the array. But does not work

I have a question about Java maps. I use map to contain array, and I want to check whether the map contains the array I want. But it does not work. Is there anyway to check whether the map contains the the array I want?
import java.util.*;
public class testContainKey{
static Map<int[],Integer> map = new HashMap<int[], Integer>();
public static void main(String args[]){
int[] initial={1,2,3,4,5,6,7,8,9};
int[] goal = {1,2,3,4,5,6,7,8,9};
map.put(goal,0);
if(map.containsKey(array)){
System.out.println("OK");
}
else{
System.out.println("Not works");
}
}
}
This is not going to work: Map is based on hash code and equality checks; arrays do not pay attention to their elements when calculating their hash code. That's why the two arrays that you tried to use as keys are considered different.
You can define a class ArrayKey, put an array into it in a constructor, and define equals and hashCode that use array elements.
You are using as a key of the map an array, which is pretty much hard to control, because AFAIK you can not modify the equals() and hashCode() of it.
When you call Map.containsKey(), it is using the array's .equals(), which compares the 2 objects. Since initial and goal are 2 different arrays, initial.equals(goal) will be false, always, even though the contents of the array are the same.
Something you can do is extend Map and override Map.containsKey() to check for int[], and compare each of the elements to determine equality.

Java - Distinct List of Objects

I have a list/collection of objects that may or may not have the same property values. What's the easiest way to get a distinct list of the objects with equal properties? Is one collection type best suited for this purpose? For example, in C# I could do something like the following with LINQ.
var recipients = (from recipient in recipientList
select recipient).Distinct();
My initial thought was to use lambdaj (link text), but it doesn't appear to support this.
return new ArrayList(new HashSet(recipients));
Use an implementation of the interface Set<T> (class T may need a custom .equals() method, and you may have to implement that .equals() yourself). Typically a HashSet does it out of the box : it uses Object.hashCode() and Object.equals() method to compare objects. That should be unique enough for simple objects. If not, you'll have to implement T.equals() and T.hashCode() accordingly.
See Gaurav Saini's comment below for libraries helping to implement equals and hashcode.
Place them in a TreeSet which holds a custom Comparator, which checks the properties you need:
SortedSet<MyObject> set = new TreeSet<MyObject>(new Comparator<MyObject>(){
public int compare(MyObject o1, MyObject o2) {
// return 0 if objects are equal in terms of your properties
}
});
set.addAll(myList); // eliminate duplicates
Java 8:
recipients = recipients.stream()
.distinct()
.collect(Collectors.toList());
See java.util.stream.Stream#distinct.
order preserving version of the above response
return new ArrayList(new LinkedHashSet(recipients));
If you're using Eclipse Collections, you can use the method distinct().
ListIterable<Integer> integers = Lists.mutable.with(1, 3, 1, 2, 2, 1);
Assert.assertEquals(
Lists.mutable.with(1, 3, 2),
integers.distinct());
The advantage of using distinct() instead of converting to a Set and then back to a List is that distinct() preserves the order of the original List, retaining the first occurrence of each element. It's implemented by using both a Set and a List.
MutableSet<T> seenSoFar = Sets.mutable.with();
int size = list.size();
for (int i = 0; i < size; i++)
{
T item = list.get(i);
if (seenSoFar.add(item))
{
targetCollection.add(item);
}
}
return targetCollection;
If you cannot convert your original List into an Eclipse Collections type, you can use ListAdapter to get the same API.
MutableList<Integer> distinct = ListAdapter.adapt(integers).distinct();
Note: I am a committer for Eclipse Collections.
You can use a Set. There's couple of implementations:
HashSet uses an object's hashCode and equals.
TreeSet uses compareTo (defined by Comparable) or compare (defined by Comparator). Keep in mind that the comparison must be consistent with equals. See TreeSet JavaDocs for more info.
Also keep in mind that if you override equals you must override hashCode such that two equals objects has the same hash code.
The ordinary way of doing this would be to convert to a Set, then back to a List. But you can get fancy with Functional Java. If you liked Lamdaj, you'll love FJ.
recipients = recipients
.sort(recipientOrd)
.group(recipientOrd.equal())
.map(List.<Recipient>head_());
You'll need to have defined an ordering for recipients, recipientOrd. Something like:
Ord<Recipient> recipientOrd = ord(new F2<Recipient, Recipient, Ordering>() {
public Ordering f(Recipient r1, Recipient r2) {
return stringOrd.compare(r1.getEmailAddress(), r2.getEmailAddress());
}
});
Works even if you don't have control of equals() and hashCode() on the Recipient class.
Actually lambdaj implements this feature through the selectDistinctArgument method
http://lambdaj.googlecode.com/svn/trunk/html/apidocs/ch/lambdaj/Lambda.html#selectDistinctArgument(java.lang.Object,%20A)

Categories