10

有什么方法可以有效地HashMap从一个(或另一个合适的Map)获取密钥(或整个条目) ?

在有人说之前,我从不需要它:我需要。我有一个这样的循环

for (long i=0; i<1e12; ++i) {
    Key key = new Key(i);
    Value value = map.get(key);
    if (something(key, value)) list.add(key);
}

如果我可以用类似的东西 替换(新实例将有资格获得 GC),那么我key不必要地占用了可以保存的内存。虽然它们是相等的,但重用旧实例会节省内存。list.add(key)list.add(map.getKey(key))

我知道我可以将密钥嵌入到值中或使用 Guava 的Interner; 两者都有帮助,但都需要一些内存。


解决一些误解评论:如果效率不是问题,则可以执行以下操作

Key getKeyFromMap(Key key, Map<Key, ?> map) {
    for (Key key2 : map.keySet()) {
        if (key.equals(key2)) return key2;
    }
    return null;
}

接受的答案中描述的最有效的解决方案:

public static <K, V> K getKey(K key, HashMap<K, V> map) {
    final Entry<K, V> entry = map.getEntry(key);
    return entry==null ? null : entry.getKey();
}

问题是它必须放入package java.util,因为它使用包私有方法。使用这种方法可能很危险,但在我的“运行一次”用例中没有问题。

4

8 回答 8

9

为了做到这一点,你准备犯下多少邪恶?

Map界面不允许您检索密钥或条目。Set界面也不行。HashMap的公共接口也没有。

但是HashMap接口可以(至少在 Sun JDK 中)。看看源代码;在第 355 行,有一个名为的方法getEntry,其开头如下:

  /**
   * Returns the entry associated with the specified key in the
   * HashMap.  Returns null if the HashMap contains no mapping
   * for the key.
   */
  final Entry<K,V> getEntry(Object key) {

我相信这正是您所需要的。您可以通过反射调用它,或者将您自己的类偷偷放入java.util包中。Java 维护人员将来可能会取消此方法,并且它可能不会出现在所有平台上,但如果您准备好忍住并承担风险,这是一个简单的解决方案。

于 2012-10-07T15:23:24.217 回答
3

NavigableMaphas ceilingKey,这将让您equals在返回的 Key 上使用以查看是否有该确切键的条目。但是您不能使用 HashMap,您需要 TreeMap 或其他东西,这可能会抵消任何内存性能优势(并且您的密钥需要实现Comparable)。此外,javadoc 没有说明返回的键是否与 Map 中使用的对象完全相同,因此它可能是特定于实现的。

于 2012-10-07T15:39:36.337 回答
2

我找到了另一种解决方法,利用集合(或映射)中元素(或键)比较的实现。

java.util 中的所有集合(或映射)实现(据我所知)都是通过用户搜索对象的o.equals(e)时间来比较元素。o所以我想出了以下解决方案:

public static <K> K getOriginalKey(final Map<K, ?> map, final K key) {
    OriginalElementExtractor extractor = new OriginalElementExtractor(key);
    map.containsKey(extractor);
    return (K) extractor.result;
}

public static <E> E getOriginalElement(final Collection<E> coll, final E element) {
    OriginalElementExtractor extractor = new OriginalElementExtractor(element);
    coll.contains(extractor);
    return (E) extractor.result;
}

private static class OriginalElementExtractor {

    final Object key;
    final int hash;
    Object result;

    OriginalElementExtractor(final Object key) {
        this.key = key;
        hash = key.hashCode();
    }

    @Override
    public boolean equals(final Object o) {
        boolean eq;
        if (eq = key.equals(o))
            result = o;
        return eq;
    }

    @Override
    public int hashCode() {
        return hash;
    }
}

Collection(或映射)将调用该OriginalElementExtractor.equals(Object)方法来检查是否相等,并且原始元素将被传递。

于 2016-06-02T22:23:10.687 回答
1

编辑:我认为你想要做的是测试内存效率,如果 akey-Object是你的关键Map

Key getKeyFromMap(Key key, Map<Key, ?> map) {
  if(map.containsKey(key){
    return key;
  }else{
    return null;
  }
}

老答案:

Map<String, Value> map = new HashMap<>();//Java 7 diamond operator.
Set<String> keySet = map.keySet();

for(String key: keySet){
  System.out.println(key + " is key in the map");
}
Object[] keyArray = keySet.toArray();

Map#keySet()返回一个Set包含映射中所有使用的键的值。您可以遍历您的 Set。一套不像你Map的那样订购,也是。但是您可以将其转换为数组并Set#toArray()返回一个对象数组,该数组可以转换为Set.

于 2012-10-07T14:22:02.093 回答
0

我建议你这样处理它。

    for (Iterator i = map.keySet().iterator(); i.hasNext();) {
        String key = (String) i.next();
        if (key.equals(value)) {
            ....
        }
    }
于 2012-10-07T14:25:14.760 回答
0

你想要它看起来的条目集。

for (Entry<Key,Value> entry : map.entrySet()) {
    if (something(entry.getKey(), entry.getValue())) {
        list.add(entry.getKey());
    }
}

如果不利用有关您所获得数据的一些信息,您不会变得比这更好。

此外,API 文档是您的朋友。

于 2012-10-07T14:39:17.660 回答
0

如果您需要Key给定的Value,有三种可能性:

  1. 遍历键集并找到您的值的键(错误的运行时)。
  2. 除了原始映射(Value -> Key)之外保存逆映射(双倍空间消耗)
  3. HashBiMap使用有方法的番石榴inverse。它将返回您的地图的倒数。然后,您可以简单地使用inverse.get(value)来获取您的密钥。
于 2012-10-07T14:58:43.120 回答
0

我还没有找到一个好的解决方案,所以完成将密钥嵌入到值中,就像你说的那样:

    Map<K, Entry<K, V>> map = new HashMap<K, Entry<K, V>>();
    K key = new K(...);
    map.put(key, new AbstractMap.SimpleEntry<K, V>(key, value));

    //then I can retrieve the reference of original key, given another instance of de key
    Entry<K, V> keyObj = map.get(new K(...));
于 2020-02-12T14:58:40.320 回答