It looks like the hashCode() and equals() for int[] are poorly implemented, or not implemented at all!
(Tested with Android, but I expect it to be true for any Java environment).
In order to get HashSet.contains() working properly, I had to create a wrapper for int[] (plse, don't criticize my coding style, look at the essence):
public class IntArray {
private int[] value;
public IntArray(int[] value) {
this.value = value;
}
#Override
public int hashCode() {
int sum = 0;
// Integer overflows are cheerfully welcome.
for (int elem: value) sum += elem;
return sum;
}
#Override
public boolean equals(Object o) {
if (o == null) return (value==null);
if (value != null) {
if (o instanceof int[])
return compare((int[])o);
if (o instanceof IntArray)
return compare(((IntArray)o).value);
}
return false;
}
protected boolean compare(int[] other) {
int len = value.length;
if (other.length != len) return false;
for (int i=0; i<len ; i++)
if (value[i] != other[i]) return false;
return true;
}
}
Works OK, but I prefer to avoid a custom wrapper or a third-party library. Is there an option?
Since the standard Java Hashtable does not allow an override of the hash code used for keys, you are out of luck and will need to use a wrapper like you did.
keep in mind that your hashCode implementation is very bad, you can use this (Taken from java.util.Arrays in the standard JDK) to get a better hash distrubtion:
public static int hashCode(int a[]) {
if (a == null)
return 0;
int result = 1;
for (int element : a)
result = 31 * result + element;
return result;
}
An alternative is to use a different Hashtable, which can deal with primitives.
one such option is Banana, which is a primitive collections library I created.
After Omry Yadan's message the hashCode function becomes as simple as that!
#Override
public int hashCode() {
return Arrays.hashCode(value);
}
For a RISC CPU, like ARM, It may be more efficient:
#Override
public int hashCode() {
int code = 0;
if (value != null) {
code++;
for (int elem: value)
code = (code<<5) - code + elem;
}
return code;
}
May be there is also a standard function for comparing arrays, in which case equals() can be simplified too?
Related
I created a HashMap that uses a customed class Location as keys.
After inserting all the entries into the HashMap using put(), I am not able to search the keys.
I have tried to use get() or containsKey() to search, but neither give me positive results. However, I am sure that the keys do exists in the code because I have used HashMap iteration to print out the keys.
Below is the code:
public HashMap<Location, Integer>beenTo = new HashMap<>();
public int uniquePathsIII(int[][] grid) {
for (int i=0; i<grid.length; i++){
for (int j=0; j<grid[0].length; j++){
if (grid[i][j] == 0 || grid[i][j] == 2){
Location newSquare = new Location(i,j);
notBeen.put(newSquare, 1);
}
}
}
Location newSquare = new Location(0,1);
if (notBeen.get(newSquare) != null){
return 10;
}
if (notBeen.isEmpty()){
return -1;
}
}
Below is the class Location:
class Location{
int i; // row
int j; // column
public Location(int _i, int _j){
i = _i;
j = _j;
}
public int getI(){
return i;
}
public int getJ(){
return j;
}
public void setI(int _i){
i = _i;
}
public void setJ(int _j){
j = _j;
}
}
In the above code, I wanted to search for the key Location(0,1). I have made sure that the Hashmap notBeen is not empty, and tried that the key does exist. But I am never able to find it using containsKey() nor get().
You need to implement/override hashCode and equals methods if you want a custom Object to work as a key in a HashMap in Java.
FYI:
_variableName goes against Java naming conventions Oracle Java Naming Convention. It is also not necessary as you can get the same result using:
public Location(int i, int j){
this.i = i;
this.j = j;
}
In order to avoid memory leaks map's key must be immutable object,and improve search key speed,the key object has equals() and hashcode() methods is good practice(first use hashcode judge equals then use equals() method).In your code it is best to make location immutable(not support setter methods).
public class Location {
private final int i;
private final int j;
public Location(int x, int y) {
this.i = x;
this.j = y;
}
public int getI() {
return i;
}
public int getJ() {
return j;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Location)) {
return false;
}
Location point = (Location) o;
return i == point.i &&
j == point.j;
}
#Override
public int hashCode() {
return Objects.hash(i, j);
}
}
I have a Java class containing a 2D int array. I want to implement the hashCode() method so that different objects can be compared using their attributes and not reference. This is what I tried:
public int hashCode() {
int hash = 0;
for(int i=0; i<getMatrix().length; i++) {
hash =+ Arrays.hashCode(getMatrix()[i]);
}
return hash;
}
I also tried using deepHashCode() but didn't work. My problem is that when the get() method of a HashMap object is used, even though both the object have the same matrix, the get() method does not work properly.
EDIT:
Implementation of equals method
public boolean equals(Object o) {
boolean sameBoard = false;
if(o != null && o instanceof Node) {
Node node = (Node) o;
int[][] board1 = this.getMatrix();
int[][] board2 = node.getMatrix();
return Arrays.deepEquals(board1, board2);
}
return sameBoard;
}
I'm writing a method that returns a Set<String>. The set may contain 0, 1, or 2 objects. The string keys are also quite small (maximum 8 characters). The set is then used in a tight loop with many iterations calling contains().
For 0 objects, I would return Collections.emptySet().
For 1 object, I would return Collections.singleton().
For 2 objects (the maximum possible number), a HashSet seems overkill. Isn't there a better structure? Maybe a TreeSet is slightly better? Unfortunately, I'm still using Java 7 :-( so can't use modern things like Set.of().
An array of 2 strings would probably give the best performance, but that's not a Set. I want the code to be self-documenting, so I really want to return a Set as that is the logical interface required.
Just wrap an array with an AbstractSet. You only have to implement 2 methods, assuming you want an unmodifiable set:
class SSet extends AbstractSet<String> {
private final String[] strings;
SSet(String[] strings) {
this.strings = strings;
}
#Override
public Iterator<String> iterator() {
return Arrays.asList(strings).iterator();
}
#Override
public int size() {
return strings.length;
}
}
If you want, you can store the Arrays.asList(strings) in the field instead of a String[]. You can also provide 0, 1 and 2-arg constructors if you want to constrain the array only to be that length.
You can also override contains:
public boolean contains(Object obj) {
for (int i = 0; i < strings.length; ++i) {
if (Objects.equals(obj, strings[i])) return true;
}
return false;
}
If you don't want to create a list simply to create an iterator, you can trivially implement one as an inner class:
class ArrayIterator implements Iterator<String> {
int index;
public String next() {
// Check if index is in bounds, throw if not.
return strings[index++];
}
public boolean hasNext() {
return index < strings.length;
}
// implement remove() too, throws UnsupportedException().
}
The set is then used in a tight loop with many iterations calling contains().
I would probably streamline it for this. Perhaps something like:
public static class TwoSet<T> extends AbstractSet<T> {
T a = null;
T b = null;
#Override
public boolean contains(Object o) {
return o.equals(a) || o.equals(b);
}
#Override
public boolean add(T t) {
if(contains(t)){
return false;
}
if ( a == null ) {
a = t;
} else if ( b == null ) {
b = t;
} else {
throw new RuntimeException("Cannot have more than two items in this set.");
}
return true;
}
#Override
public boolean remove(Object o) {
if(o.equals(a)) {
a = null;
return true;
}
if(o.equals(b)) {
b = null;
return true;
}
return false;
}
#Override
public int size() {
return (a == null ? 0 : 1) + (b == null ? 0 : 1);
}
#Override
public Iterator<T> iterator() {
List<T> list;
if (a == null && b == null) {
list = Collections.emptyList();
} else {
if (a == null) {
list = Arrays.asList(b);
} else if (b == null) {
list = Arrays.asList(a);
} else {
list = Arrays.asList(a, b);
}
}
return list.iterator();
}
}
You can achieve this by
Make a class that implements Set interface
Override add and remove method
Add value upon class initialisation by super.add(E element)
Use that class instead
Is there any other class which and be use like java.util.stream.IntStream (of java 8) in java 6
I wanted to achieve
List<Integer> list = IntStream.of(intArray).boxed().collect(Collectors.toList());
how it is possible in java 6 or below
No, there's no simple way to do this in JDK6, that's exactly why they added it in JDK8.
You can either:
iterate over the int array and add the numbers one by one to the new collection
int[] intArray = { 1,2,3 };
List<Integer> ints = new ArrayList<>(intArray.length);
for (int i : intArray) {
ints.add(i);
}
or you can reach out for a library to do this for you. For example, Guava does this pretty well: Ints.asList()
If all you want is to convert an int[] into an List<Integer>
you can either:
create your own ArrayList and add the elements manually
List<Integer> list = new ArrayList<Integer>(intArray.length);
for(int value : intArray){
list.add(value);
}
Or you can easily write your own class that implements List using an primitive array backing:
public final class IntArrayList extends AbstractList<Integer> implements RandomAccess {
private final int[] array;
public IntArrayList(int[] array) {
this.array = array;
}
#Override
public Integer get(int index) {
return array[index];
}
#Override
public int size() {
return array.length;
}
#Override
public Integer set(int index, Integer element) {
Integer old = array[index];
array[index] = element.intValue();
return old;
}
public int indexOf(Object o) {
if(o==null){
return -1;
}
if(!(o instanceof Integer)){
return -1;
}
int val = ((Integer)o).intValue();
for (int i=0; i<array.length; i++){
if (val==array[i]){
return i;
}
}
return -1;
}
public boolean contains(Object o) {
return indexOf(o) != -1;
}
/**
* Optimization of equals since
* we know we are have an array of ints
* this should reduce boxing/unboxing
* on our end at least.
*/
#SuppressWarnings("rawtypes")
public boolean equals(Object o) {
if (o == this){
return true;
}
if (!(o instanceof List)){
return false;
}
int currentOffset=0;
ListIterator e2 = ((List) o).listIterator();
while(currentOffset<array.length && e2.hasNext()) {
Object o2 = e2.next();
//will return false if o2 is null
if(!(o2 instanceof Integer)){
return false;
}
if(array[currentOffset] !=((Integer)o2).intValue()){
return false;
}
currentOffset++;
}
return !(currentOffset<array.length || e2.hasNext());
}
/**
* Optimization of hashcode since
* we know we have an array of ints.
*/
public int hashCode() {
return Arrays.hashCode(array);
}
}
And then it's just
List<Integer> list = new IntArrayList(intArray);
I have a List (actually a LinkedList) and I add Items to it which implement the equals-method.
The problem is that I add items that are equal, but not identical (like two initialized objects). Now, when I want to get the index of the item I added second, I get, of course, the element of the first item, because indexOf searches for equality and not identity.
I tried to create my own subclass of LinkedList and overwrite the indexOf-method, but this is not possible, because I don't have access to neither the subclass Node nor the Node-Element first.
Here is an example:
public class ExampleObject {
int number;
public ExampleObject(){
number = 0;
}
#Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
ExampleObject other = (ExampleObject) obj;
if (number != other.number) return false;
return true;
}
public static void main(String[] args) {
LinkedList<ExampleObject> list = new LinkedList<ExampleObject>();
ExampleObject one = new ExampleObject();
ExampleObject two = new ExampleObject();
list.add(one);
list.add(two);
System.out.println(list.indexOf(one)); // '0' as expected
System.out.println(list.indexOf(two)); // '0', but I want to get '1'
}
}
My intention: I need a list of objects, where I want to store initialized objects and edit them later.
Do the iteration yourself, indexOf is just a helper method:
static int indexOfById(List<?> list, Object searchedObject) {
int i = 0;
for (Object o : list) {
if (o == searchedObject) return i;
i++;
}
return -1;
}
There are several solutions to this problem.
1) The correct solution: If you need identity comparison, then you should not override the equals method. You told us that you must override it, as you need it in another place. That indicates a design problem in your software, and you really should solve this.
2) Have a look at the class sun.awt.util.IdentityLinkedList. This is nearly a "normal" LinkedList with the identity behavior for the indexOf method. If you do not want to rely on a class in a sun sub-package, you can copy the code into a class that resides in your packages.
3) You could follow a procedural solution with iterating the list yourself:
public static <E> int indexOf(List<E> list, E searchItem) {
int index = 0;
for (E item : list) {
if (item == searchItem)
return index;
index += 1;
}
return -1;
}
4) Write a wrapper for your objects.
public class IdentityWrapper {
public Object item;
#Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
IdentityWrapper other = (IdentityWrapper) obj;
return item == other.item;
}
}
Then use this wrapper in your list: LinkedList<IdentityWrapper>. Note, that I provided a public item field in the wrapper. Normally, I would do it with a constructor and a private field only.