gag*_*nbm 21 java arrays hashmap
我使用布尔数组作为HashMap的键.但问题是当一个不同的数组作为键传递时,HashMap无法获取键,尽管元素是相同的.(因为它们是不同的对象).
如何使用数组作为键?这是代码:
public class main {
public static HashMap<boolean[], Integer> h;
public static void main(String[] args){
boolean[] a = {false, false};
h = new HashMap<boolean[], Integer>();
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");
}
}
Run Code Online (Sandbox Code Playgroud)
无论是阵列a
和t
包含相同的元素,但HashMap的不返回任何东西t
.
我如何使其工作?
Viv*_*ath 24
你不能这样做.都t
和a
将有不同的hashCode()
值,因为该java.lang.Array.hashCode()
方法是从继承Object
,它使用参考来计算哈希码(默认实现).因此,对于阵列的哈希码是参照相关的,这意味着你会得到不同的散列码值t
和a
.此外,equals
对两个数组不起作用,因为它也基于引用.
您可以这样做的唯一方法是创建一个自定义类,将boolean
数组保留为内部成员.然后,您需要覆盖equals
并hashCode
确保包含具有相同值的数组的实例相等且具有相同的哈希代码.
一个更简单的选择可能是List<Boolean>
用作关键.根据文档,hashCode()
实现List
定义为:
int hashCode = 1;
Iterator<E> i = list.iterator();
while (i.hasNext()) {
E obj = i.next();
hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
}
Run Code Online (Sandbox Code Playgroud)
如您所见,它取决于列表中的值而不是引用,因此这应该适合您.
使用数组是不可能的,因为任何两个不同的数组都不会比较equals
,即使它们具有相同的元素.
例如,您需要从容器类映射ArrayList<Boolean>
(或简单地说List<Boolean>
.也许BitSet
更合适.
Map
实现依赖于密钥equals
和hashCode
方法。在Java数组都是直接从延伸Object
,他们使用默认的equals
和hashCode
的Object
,只有比较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;
}
}
Run Code Online (Sandbox Code Playgroud)
之后,您可以使用您的密钥Map
:
Map<Key, Integer> 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
Run Code Online (Sandbox Code Playgroud)
参考: 来自一个字段的 Java 哈希码
问题
正如其他人所说,Java数组继承.hashcode()
自Object,它使用数组或对象的地址.equals()
的散列,完全忽略其内容。解决此问题的唯一方法是将数组包装在一个对象中,该对象根据数组的内容实现这些方法。这就是 Joshua Bloch 写下第 25 条:“比起数组更喜欢列表”的原因之一。Java 提供了几个执行此操作的类,或者您可以使用它们编写自己的类,其中包含这些方法的正确且有效的实现。太糟糕了,它们不是默认实现!Arrays.hashCode()
Arrays.equals()
只要可行,请使用深度不可修改(或不可变)的类作为任何基于哈希的集合的键。如果将数组(或其他可变对象)作为键存储在哈希表中后对其进行修改,则几乎肯定会在该哈希表中的未来.get()
或.contains()
测试中失败。另请参阅可变哈希映射键是危险的做法吗?
具体解决方案
// Also works with primitive: (boolean... items)
public static List<Boolean> bList(Boolean... items) {
List<Boolean> mutableList = new ArrayList<>();
for (Boolean item : items) {
mutableList.add(item);
}
return Collections.unmodifiableList(mutableList);
}
Run Code Online (Sandbox Code Playgroud)
ArrayList 根据其内容实现.equals()
and .hashCode()
(正确且高效),以便每个 都bList(false, false)
具有相同的哈希码,并且将等于每个其他bList(false, false)
。
将其包裹起来Collections.unmodifiableList()
可以防止修改。
修改示例以使用 bList() 只需更改一些声明和类型签名。它和你原来的一样清晰,几乎一样简短:
public class main {
public static HashMap<List<Boolean>, Integer> h;
public static void main(String[] args){
List<Boolean> a = bList(false, false);
h = new HashMap<>();
h.put(a, 1);
if(h.containsKey(a)) System.out.println("Found a");
List<Boolean> t = bList(false, false);
if(h.containsKey(t)) System.out.println("Found t");
else System.out.println("Couldn't find t");
}
}
Run Code Online (Sandbox Code Playgroud)
通用解决方案
public <T> List<T> bList(T... items) {
List<T> mutableList = new ArrayList<>();
for (T item : items) {
mutableList.add(item);
}
return Collections.unmodifiableList(mutableList);
}
Run Code Online (Sandbox Code Playgroud)
上述解决方案的其余部分保持不变,但这将利用 Java 的内置类型推断来处理任何基元或对象(尽管我建议仅使用不可变类)。
图书馆解决方案
而不是bList()
使用Google Guava ImmutableList.of()
或我自己的Paguro vec()
或其他提供此类预先测试方法的库(加上不可变/不可修改的集合等)。
劣质解决方案
这是我在 2017 年的原始答案。我把它留在这里是因为有人觉得它很有趣,但我认为它是二流的,因为 Java 已经包含了 ArrayList 和 Collections.unmodifyingList() 来解决这个问题。使用 .equals() 和 .hashCode() 方法编写自己的集合包装器比使用内置方法需要更多工作,更容易出错,更难以验证,因此更难阅读。
这应该适用于任何类型的数组:
class ArrayHolder<T> {
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);
}
}
Run Code Online (Sandbox Code Playgroud)
这是转换为使用 ArrayHolder 的具体示例:
// boolean[] a = {false, false};
ArrayHolder<Boolean> a = new ArrayHolder<>(false, false);
// h = new HashMap<boolean[], Integer>();
Map<ArrayHolder<Boolean>, 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<Boolean> 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)));
Run Code Online (Sandbox Code Playgroud)
我使用 Java 8,但我认为 Java 7 拥有您所需的一切。我使用TestUtils测试了 hashCode 和 equals 。
归档时间: |
|
查看次数: |
27395 次 |
最近记录: |