在HashMap中访问隐藏的getEntry(Object键)

时间:2010-03-04 16:40:56

标签: java map

我遇到过与here讨论的问题类似的问题,但具有更强的实际用途。

例如,我有一个Map<String, Integer>,我有一些函数,给定一个键,如果映射的整数值为负,则将NULL放到地图上:

Map<String, Integer> map = new HashMap<String, Integer>();

public void nullifyIfNegative(String key) {
    Integer value = map.get(key);

    if (value != null && value.intValue() < 0) {
        map.put(key, null);
    }
}

在这种情况下,查找(以及密钥的hashCode计算)完成两次:一次用于查找,一次用于替换。如果有另一种方法(已经在HashMap中)并且允许使其更有效,那将是很好的:

public void nullifyIfNegative(String key) {
    Map.Entry<String, Integer> entry = map.getEntry(key);

    if (entry != null && entry.getValue().intValue() < 0) {
        entry.setValue(null);
    }
}

同样关注的情况,当你想操纵不可变对象时,可以是映射值:

  • Map<String, String>:我想在字符串值附加一些内容。
  • Map<String, int[]>:我想在数组中插入一个数字。

所以这种情况很常见。解决方案,可能有用,但不适合我:

  • 反射。很好,但我不能仅仅因为这个不错的功能而牺牲性能。
  • 使用org.apache.commons.collections.map.AbstractHashedMap(至少有protected getEntry()方法),但不幸的是,commons-collections不支持泛型。
  • 使用generic commons-collections,但此库(AFAIK)已过时(与Apache的最新库版本不同步),并且(重要的)在中央maven存储库中不可用。
  • 使用值包装器,这意味着“使值可变”(例如,使用可变整数[例如org.apache.commons.lang.mutable.MutableInt]或集合而不是数组)。这个解决方案会导致内存丢失,我想避免这种情况。
  • 尝试使用自定义类实现扩展java.util.HashMap(应该放在java.util包中)并将其放到endorsed folder(因为java.lang.ClassLoader将拒绝加载{ {1}},请参阅资料来源),但我不想修补JDK,看起来像可以认可的软件包列表,不包括Class<?> defineClass(String name, byte[] b, int off, int len)

类似的问题已在sun.com bugtracker上提出,但我想知道,社区的意见是什么,以及可以考虑最大记忆和出路的方法。绩效。

如果您同意,这是好的和受益的功能,请投票给这个错误!

3 个答案:

答案 0 :(得分:3)

作为一个逻辑问题,你是对的,因为单个getEntry会为你节省哈希查找。实际上,除非你有一个特定的用例,你有理由担心性能损失(这似乎不太可能,哈希查找很常见,O(1),并且很好地优化)你担心的是可能微不足道。

你为什么不写一个考试?创建一个哈希表,其中包含数百万个对象的数量,或者比应用程序可能创建的数量级更大的数量级,并且平均get()的时间超过一百万次左右(提示:它将会是一个非常小的数字。)

你正在做的更大的问题是同步。您应该知道,如果您在地图上进行条件更改,即使您使用的是同步地图,也可能会遇到问题,因为您必须锁定对覆盖两者的范围的密钥的访问权限( )和set()操作。

答案 1 :(得分:1)

不漂亮,但您可以使用轻量级对象来保存对实际值的引用,以避免第二次查找。

HashMap<String, String[]> map = ...;

// append value to the current value of key
String key = "key";
String value = "value";

// I use an array to hold a reference - even uglier than the whole idea itself ;)
String[] ref = new String[1]; // lightweigt object
String[] prev = map.put(key, ref);
ref[0] = (prev != null) ? prev[0] + value : value;

我不会过分担心哈希查找性能(Steve B's answer指出原因非常好)。特别是对于String键,我不会过于担心hashCode(),因为它的结果是缓存的。您可能会担心equals(),因为每次查询可能会多次调用它。但对于短字符串(通常用作键),这也可以忽略不计。

答案 2 :(得分:-2)

此提案没有性能提升,因为平均情况下Map的性能为O(1)。但是在这种情况下允许访问原始条目会引发另一个问题。可以更改条目中的键(即使它只能通过反射),因此可以中断内部数组的顺序。