Map的entrySet中,Stream和Iterator有何区别?- Java 8

20
根据我理解,以下代码应该会打印true,因为StreamIterator都指向第一个元素。但是,当我运行以下代码时,它打印出了false:
final HashMap<String, String> map = new HashMap<>();
map.put("A", "B");
final Set<Map.Entry<String, String>> set = Collections.unmodifiableMap(map).entrySet();
Map.Entry<String, String> entry1 = set.iterator().next();
Map.Entry<String, String> entry2 = set.stream().findFirst().get();
System.out.println(entry1 == entry2);

这种不同的行为可能是由什么原因引起的?


6
不应该这样做,因为HashMap的entrySet没有顺序。 - JB Nizet
3
@JBNizet--同意,但我的地图中只有一个元素,先生。 - T-Bag
15
地图也不能保证它们不会在运行时创建新的Map.Entry实例。不要使用“==”比较对象,应该使用equals()函数。 - JB Nizet
我同意@JBNizet的观点。使用"entry1.equals(entry2)"。 - jchips12
@Eran-- 你也是英语专家吗... :D 非常好的编辑...(Y) - T-Bag
3个回答

33

这两个条目都指向您的Map的相同逻辑条目(其键为“A”,值为“B”)。但是,它们不是同一个实例。

如果您深入到Collections.unmodifiableMap(map)的实现中,您会发现迭代Collections.unmodifiableMap(map)返回的映射的entrySet将返回一个包装原始可修改条目的新Map.Entry

public Map.Entry<K,V> next() {
  return new UnmodifiableEntry<>(i.next());
}

我认为当你调用set.stream().findFirst().get()时,也会创建一个新的Map.Entry实例,因此这两个方法返回不同的实例。

即使你两次调用相同的方法,也会得到不同的实例,例如下面的代码也会打印false

Map.Entry<String, String> entry1 = set.iterator().next();
Map.Entry<String, String> entry2 = set.iterator().next();
System.out.println(entry1 == entry2);

另一方面,如果您直接从原始的HashMap中获取条目,您将会获得true

Map.Entry<String, String> entry1 = map.entrySet ().iterator().next();
Map.Entry<String, String> entry2 = map.entrySet ().stream().findFirst().get();
System.out.println (entry1==entry2);

如果情况是这样,该条目没有被新实例包装,因此entrySet().iterator().next()entrySet().stream().findFirst().get()都会返回相同的实例。


2
太棒了,你找到了next()函数中的那一行。我也做了一些搜索,但很快就放弃了。顺便修复了混乱的缩进;-) - GhostCat
2
很好的发现。看起来这个问题与流与迭代器无关,但它与unmodifiableMap实现有关。 - GaurZilla

12

情况是这样的:

Map.Entry<String, String> entry1 = set.iterator().next();
Map.Entry<String, String> entry2 = set.stream().findFirst().get();

你没有比较放入地图中的,而是比较Entry对象!

换句话说,看起来你的代码正在使用你的代码创建新的Entry对象。当被要求迭代或流处理时,不可修改的Map/Set的内部实现将返回什么完全由它决定...正如Eran更快地查找到的原因:在迭代时会创建新的Entry对象

因此,当使用equals()而不是==时...你会得到预期的输出。


3

两个 entry1entry2 的值相同,但它们不指向同一个对象,因为每次获取 Map.Entry 对象时都会创建一个新的对象。
看下面的代码:

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Test1 {

    public static void main(String[] args) {
        final HashMap<String, String> map = new HashMap<>();
        map.put("A", "B");
        final Set<Map.Entry<String, String>> set = Collections.unmodifiableMap(map).entrySet();
        Map.Entry<String, String> entry1 = set.iterator().next();
        Map.Entry<String, String> entry2 = set.stream().findFirst().get();
        System.out.println("entry1 : " + System.identityHashCode(entry1));
        System.out.println("entry2 : " + System.identityHashCode(entry2));
        for (int i = 0; i < 5; i++) {
            System.out.println("directly for set " + i + " : " + System.identityHashCode(set.stream().findFirst().get()));
        }
    }
}

输出结果为:
entry1 : 1283928880
entry2 : 295530567
directly for set 0 : 2003749087
directly for set 1 : 1324119927
directly for set 2 : 990368553
directly for set 3 : 1096979270
directly for set 4 : 1078694789

System.identityHashCode()会返回哈希码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接