如何使HashMap与数组一起使用?
我使用布尔数组作为HashMap的键。 但问题是当一个不同的数组作为键传递时,HashMap无法获取键,尽管元素是相同的。 (因为它们是不同的对象)。
如何使用数组作为键? 这是代码:
public class main { public static HashMap h; public static void main(String[] args){ boolean[] a = {false, false}; h = new HashMap(); h.put(a, 1); if(h.containsKey(a)) System.out.println("Found a"); boolean[] t = {false, false}; if(h.containsKey(t)) System.out.println("Found t"); else System.out.println("Couldn't find t"); } }
数组a
和t
都包含相同的元素,但HashMap不会为t
返回任何内容。
我如何使其工作?
你不能这样做。 t
和a
都将具有不同的hashCode()
值,因为java.lang.Array.hashCode()
方法inheritance自Object
,后者使用引用来计算哈希码(默认实现)。 因此,数组的哈希码是依赖于引用的,这意味着您将获得t
和a
的不同哈希码值。 此外, equals
不适用于两个数组,因为它也基于引用。
您可以这样做的唯一方法是创建一个自定义类,将boolean
数组保持为内部成员。 然后,您需要以这样的方式覆盖equals
和hashCode
,以确保包含具有相同值的数组的实例相等并且还具有相同的哈希代码。
更简单的选择可能是使用List
作为键。 根据文档 , List
的hashCode()
实现定义为:
int hashCode = 1; Iterator i = list.iterator(); while (i.hasNext()) { E obj = i.next(); hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode()); }
如您所见,它取决于列表中的值而不是引用,因此这应该适合您。
使用数组是不可能的,因为任何两个不同的数组都不会比较equals
,即使它们具有相同的元素。
您需要从容器类映射,例如ArrayList
(或简单地List
。也许BitSet
会更合适。
Map
实现依赖于key的equals
和hashCode
方法。 java中的数组直接从Object
扩展,它们使用Object
default equals
和hashCode
,它只比较identity
。
如果我是你,我会创建一个类Key
class Key { private final boolean flag1; private final boolean flag2; public Key(boolean flag1, boolean flag2) { this.flag1 = flag1; this.flag2 = flag2; } @Override public boolean equals(Object object) { if (!(object instanceof Key)) { return false; } Key otherKey = (Key) object; return this.flag1 == otherKey.flag1 && this.flag2 == otherKey.flag2; } @Override public int hashCode() { int result = 17; // any prime number result = 31 * result + Boolean.valueOf(this.flag1).hashCode(); result = 31 * result + Boolean.valueOf(this.flag2).hashCode(); return result; } }
之后,您可以将您的密钥用于Map
:
Map map = new HashMap<>(); Key firstKey = new Key(false, false); map.put(firstKey, 1); Key secondKey = new Key(false, false) // same key, different instance int result = map.get(secondKey); // --> result will be 1
参考: 来自一个字段的Java哈希代码
可能是因为Array返回的equals()方法与您期望的不同。 您应该考虑实现自己的收集和覆盖equals()和hashCode()。
boolean[] t; t = a;
如果你给这个,而不是boolean[] t = {false, false};
,那么你将获得所需的输出。
这是因为Map
将reference
存储为key
,在您的情况下,虽然t
具有相同的值,但它没有与a
相同的引用。
因此,当你给t=a
,它会起作用。
它非常相似: –
String a = "ab"; String b = new String("ab"); System.out.println(a==b); // This will give false.
a
和b
保持相同的值,但具有不同的引用。 因此,当您尝试使用==
比较引用时,它会给出false
。
但如果你给, a = b;
然后尝试比较reference
,你会得到true
。
您可以创建一个包含该数组的类。 根据值实现该类的hashCode()和equals()方法:
public class boolarray { boolean array[]; public boolarray( boolean b[] ) { array = b; } public int hashCode() { int hash = 0; for (int i = 0; i < array.length; i++) if (array[i]) hash += Math.pow(2, i); return hash; } public boolean equals( Object b ) { if (!(b instanceof boolarray)) return false; if ( array.length != ((boolarray)b).array.length ) return false; for (int i = 0; i < array.length; i++ ) if (array[i] != ((boolarray)b).array[i]) return false; return true; } }
然后你可以使用:
boolarray a = new boolarray( new boolean[]{ true, true } ); boolarray b = new boolarray( new boolean[]{ true, true } ); HashMap map = new HashMap(); map.put(a, 2); int c = map.get(b); System.out.println(c);
Map使用equals()
来测试您的密钥是否相同。
Object
测试中的该方法的默认实现==
,即引用相等。 因此,由于您的两个数组不是同一个数组,因此equals
总是返回false。
您需要使映射在两个数组上调用Arrays.equals
以检查是否相等。
您可以创建一个使用Arrays.equals
的数组包装器类,然后这将按预期工作:
public static final class ArrayHolder { private final T[] t; public ArrayHolder(T[] t) { this.t = t; } @Override public int hashCode() { int hash = 7; hash = 23 * hash + Arrays.hashCode(this.t); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final ArrayHolder other = (ArrayHolder ) obj; if (!Arrays.equals(this.t, other.t)) { return false; } return true; } } public static void main(String[] args) { final Map, Integer> myMap = new HashMap<>(); myMap.put(new ArrayHolder<>(new Boolean[]{true, true}), 7); System.out.println(myMap.get(new ArrayHolder<>(new Boolean[]{true, true}))); }
您可以使用接受外部散列和比较策略的库(特洛伊)。
class MyHashingStrategy implements HashingStrategy { @Override public int computeHashCode(boolean[] pTableau) { return Arrays.hashCode(pTableau); } @Override public boolean equals(boolean[] o1, boolean[] o2) { return Arrays.equals(o1, o2); } } Map map = new TCustomHashMap(new MyHashingStrategy());
这适用于任何类型的数组:
class ArrayHolder { private final T[] array; @SafeVarargs ArrayHolder(T... ts) { array = ts; } @Override public int hashCode() { return Arrays.hashCode(array); } @Override public boolean equals(Object other) { if (array == other) { return true; } if (! (other instanceof ArrayHolder) ) { return false; } //noinspection unchecked return Arrays.equals(array, ((ArrayHolder) other).array); } }
以下是您转换为使用ArrayHolder的具体示例:
// boolean[] a = {false, false}; ArrayHolder a = new ArrayHolder<>(false, false); // h = new HashMap(); Map, Integer> h = new HashMap<>(); h.put(a, 1); // if(h.containsKey(a)) System.out.println("Found a"); assertTrue(h.containsKey(a)); // boolean[] t = {false, false}; ArrayHolder t = new ArrayHolder<>(false, false); // if(h.containsKey(t)) System.out.println("Found t"); assertTrue(h.containsKey(t)); assertFalse(h.containsKey(new ArrayHolder<>(true, false)));
我使用的是Java 8,但我认为Java 7拥有您需要的一切。 我使用TestUtils测试了hashCode和equals。
另一个想法是Joshua Bloch的第25项:“首选列表到arrays”。