Suppose I have a HashMap<K, V>
and two objects of type K
that are equal to each other but not the same object, and the map has an entry for key k1
.
Given k2
, can I get a reference to k1
using only methods from HashMap
(no external data structures) that executes in constant time, ie O(1) time complexity?
In code:
K k1, k2;
k1.equals(k2) // true
k1.hashCode() == k2.hashCode() // true
k1 == k2 // false
myMap.put(k1, someValue);
K existingKey = getExistingKey(myMap, k2);
existingKey == k1 // true <- this is the goal
<K> K getExistingKey(HashMap<K, V> map, K k) {
// What impl goes here?
}
I was hoping to use one of the various methods added with java 8, such as compute()
to "sniff" the existing key within the lambda, but they all (seem to) pass the new key object to the lambda, not the existing key.
Iterating through the entrySet()
would find the existing key, but not in constant time.
I could use a Map<K, K>
to store the keys and I could keep it in sync, but that doesn't answer the question.
答案 0 :(得分:1)
You are looking for something like
Map.Entry<K,V> getEntry(K key)
At first I thought it would be easy to make a custom subclass of HashMap
to return this, as get(K key)
is just
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
where Node
implements Map.Entry
. This would look like:
public class MyHashMap<K,V> extends HashMap<K,V>
{
public MyHashMap() {}
// Other constructors as needed
public Map.Entry<K, V> getEntry(K key)
{
Map.Entry<K, V> e = getNode(hash(key),key);
return e;
}
}
Unfortunately, getNode()
and hash()
are both package private and therefore not visible to subclasses.
The next step was to put the class in java.util
but this fails in Java 9 with
The package java.util conflicts with a package accessible from another module: java.base
I think you're out of luck here.
I actually think a getEntry()
method would be a useful addition to the API, you might consider filing an enhancement request.
答案 1 :(得分:0)
我不知道您对内存使用情况的限制,但是如果您可以使用LinkedHashMap
代替HashMap
(LinkedHashMap
使用额外引用来保持广告订单顺序),那么你可以利用它的removeEldestEntry
方法:
public class HackedMap<K, V> extends LinkedHashMap<K, V> {
K lastKey;
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
lastKey = eldest.getKey();
return false;
}
K getLastKey() {
return lastKey;
}
}
我认为代码是不言自明的。我们保留对原始密钥的引用,我们从removeEldestEntry
方法的参数中获取该密钥。
关于removeEldestEntry
方法的返回值,它是false
,因此我们不允许删除最年长的条目(毕竟,我们不想要地图作为缓存工作。)
现在,使用常见的广告订单LinkedHashMap
,removeEldestEntry
和put
(来自putAll
方法文档)会自动调用removeEldestEntry
方法:
在将新条目插入地图后,put和putAll将调用此方法。
所以我们现在需要做的就是实现你的getExistingKey
方法,使其调用put
而不修改地图,你可以按照以下方式执行:
<K, V> K getExistingKey(HackedMap<K, V> map, K k) {
if (k == null) return null;
V v = map.get(k);
if (v == null) return null;
map.put(k, v);
return map.getLastKey();
}
这是有效的,因为当地图已经包含映射到给定键的条目时,put
方法会替换该值而不会触及该键。
我不确定我做过的空检查,也许你需要改进它。当然,HackedMap
并不支持并发访问,但HashMap
和LinkedHashMap
也不支持。
您可以安全地使用HackedMap
代替HashMap
。这是测试代码:
Key k1 = new Key(10, "KEY 1");
Key k2 = new Key(10, "KEY 2");
Key k3 = new Key(10, "KEY 3");
HackedMap<Key, String> myMap = new HackedMap<>();
System.out.println(k1.equals(k2)); // true
System.out.println(k1.equals(k3)); // true
System.out.println(k2.equals(k3)); // true
System.out.println(k1.hashCode() == k2.hashCode()); // true
System.out.println(k1.hashCode() == k3.hashCode()); // true
System.out.println(k2.hashCode() == k3.hashCode()); // true
System.out.println(k1 == k2); // false
System.out.println(k1 == k3); // false
System.out.println(k2 == k3); // false
myMap.put(k1, "k1 value");
System.out.println(myMap); // {Key{k=10, d='KEY 1'}=k1 value}
myMap.put(k3, "k3 value"); // Key k1 (the one with its field d='KEY 1') remains in
// the map but value is now 'k3 value' instead of 'k1 value'
System.out.println(myMap); // {Key{k=10, d='KEY 1'}=k3 value}
Key existingKey = getExistingKey(myMap, k2);
System.out.println(existingKey == k1); // true
System.out.println(existingKey == k2); // false
System.out.println(existingKey == k3); // false
// Just to be sure
System.out.println(myMap); // {Key{k=10, d='KEY 1'}=k3 value}
以下是我使用的Key
课程:
public class Key {
private final int k;
private final String d;
Key(int k, String d) {
this.k = k;
this.d = d;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key key1 = (Key) o;
return k == key1.k;
}
@Override
public int hashCode() {
return Objects.hash(k);
}
@Override
public String toString() {
return "Key{" +
"k=" + k +
", d='" + d + '\'' +
'}';
}
}