【问题标题】:FindBugs: DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS发现错误:DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS
【发布时间】:2012-08-05 02:00:02
【问题描述】:

我实现了here找到的非常好的排序解决方案:

static <K,V extends Comparable<? super V>> SortedSet<Map.Entry<K,V>>
entriesSortedByValues(Map<K,V> map) {

    SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(

        new Comparator<Map.Entry<K,V>>() {
            @Override
            public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
                int res = e1.getValue().compareTo(e2.getValue());
                return res != 0 ? res : 1;
            }
        }
    );

    sortedEntries.addAll(map.entrySet());
    return sortedEntries;
}

代码似乎运行良好。然而,FindBugs 抱怨这一行:

sortedEntries.addAll(map.entrySet());

投诉是:

Bug:添加条目集的元素可能会由于重用 Map.Entry 对象在 com.local.sem.util.MapUtil.entriesSortedByValues(地图)

entrySet() 方法允许返回底层视图 在其中一个 Entry 对象被重用和返回的 Map 迭代。从 Java 1.6 开始,IdentityHashMap 和 EnumMap 都这样做了。 在遍历这样的 Map 时,Entry 值仅在直到 你前进到下一次迭代。例如,如果您尝试通过 这样一个 entrySet 到一个 addAll 方法,事情就会大错特错。

信心:正常,等级:麻烦 (14)
模式:DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS
类型:DMI,类别:BAD_PRACTICE(不良做法)

谁能告诉我这是什么意思,或者它是否真的与这个特定的代码相关?

【问题讨论】:

    标签: java findbugs


    【解决方案1】:

    这是一个简单的问题示例:

    Map<String,String> map = new IdentityHashMap<String,String>();
    map.put("a", "1");
    map.put("b", "2");
    Iterator<Entry<String,String>> i = map.entrySet().iterator();
    Entry<String,String> e1 = i.next();
    System.out.println("first key is: " + e1.getKey());
    Entry<String,String> e2 = i.next();
    System.out.println("first key is now: " + e1.getKey());
    

    使用 Java 6,打印:

    first key is: a
    first key is now: b
    

    这是因为对 i.next() 的第二次调用返回与第一次相同的条目,但它更改了存储在该条目中的值。

    如果我将 IdentityHashMap 更改为 HashMap,则返回的每个 Entry 都是不同的,因此 e1.getKey() 不会更改。

    【讨论】:

    • 好的。我现在完全理解了这个概念。例如,谢谢。然而,在实践中,我还没有发现这是真的。我使用 HashMap、Hashtable、IdentityHashMap、LinkedHashMap、Properties、TreeMap 和 WeakHashMap 运行您的代码,并且每次我得到两个输出的相同值。我正在使用 Java 7。这会有所不同吗?
    • 是的,我看到你用 Java 7 做的事情是一样的。所以他们一定在 Java 7 中再次更改了 entrySet() 的实现。
    • 如果您查看 IdentityHashMap 的来源,您会发现差异。在 Java 6 中,EntryIterator.next 总是返回相同的对象(迭代器本身)。在 Java 7 中,EntryIterator.next 总是返回下一个 Entry 对象。
    • 感谢您的分析。我在 Java 6 中重新运行了代码并确认结果不同。我现在完全理解 FindBugs 在抱怨什么了。但对于我的具体例子,我不完全确定我应该做什么。看起来我对 Java 7 是安全的,但如果 API 规范允许重新使用 Map.Entry,理想情况下我不希望我的代码与 Java 的某些未来版本或某些替代 JVM 中断。我应该让代码保持原样并希望最好,还是应该以某种方式修改它以防止将来发生灾难?
    • 也许我应该遍历entrySet,而不是使用addAll,使用new AbstractMap.SimpleImmutableEntry&lt;K, V&gt;(entry.getKey(), entry.getValue())为每个条目制作一个防御性副本,然后添加该副本。
    【解决方案2】:

    entrySet() 方法允许返回底层 Map 的视图,其中单个 Entry 对象在迭代期间被重用并返回。从 Java 1.6 开始,IdentityHashMap 和 EnumMap 都这样做了。在遍历这样的 Map 时,Entry 值仅在您进行下一次迭代之前有效。例如,如果您尝试将这样的 entrySet 传递给 addAll 方法,那么事情就会大错特错。

    请阅读链接了解更多详情。 http://findbugs.sourceforge.net/bugDescriptions.html#DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS

    【讨论】:

    • findbug 网站上的一个非常简单和很好的解释,我以为你没有阅读它..
    猜你喜欢
    • 2017-11-18
    • 2013-02-27
    • 1970-01-01
    • 2013-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-07
    相关资源
    最近更新 更多