Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stream Vs. Iterator in entrySet of a Map - Java 8

Tags:

To my understanding, the following code should have print true, since both Stream and Iterator are pointing to the first element.

However, when I ran the following code it is printing 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);

What could be the reason for this different behavior?

like image 921
Sachin Sachdeva Avatar asked Jun 22 '17 06:06

Sachin Sachdeva


People also ask

Can we iterate map using stream in Java?

We can use streams in Java 8 and above to iterate a map by passing method reference or lambda expression to forEach() method of Stream interface that performs an action for each element of this stream.

What is the difference between iterator and stream?

Iterators, in Java, are used in Collection Framework to retrieve elements one by one. A stream in Java is a pipeline of objects from an array or a collection data source.

Can iterator be used for map?

Remember that we cannot iterate over map directly using iterators, because Map interface is not the part of Collection. All maps in Java implements Map interface. There are following types of maps in Java: HashMap.

What is the purpose of map method of stream in Java 8?

Java 8 Stream's map method is intermediate operation and consumes single element forom input Stream and produces single element to output Stream. It simply used to convert Stream of one type to another.


2 Answers

Both entries are referring to the same logical entry of your Map (whose key is "A" and value is "B"). However, they are not the same instance.

If you dig deep enough in the implementation of Collections.unmodifiableMap(map) you'll see that iterating over the entrySet of the map returned by Collections.unmodifiableMap(map) returns a new Map.Entry which wraps the original modifiable entry:

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

I'm assuming a new instance Map.Entry instance is also created when you call set.stream().findFirst().get(), so the two methods return different instances.

Even if you'll call the same method twice you'll get difference instances, i.e. the following code will also print false:

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

On the other hand, if you obtain the entry directly from the original HashMap, you will get 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);

If this case the entry is not wrapped by a new instance, so both entrySet ().iterator().next() and entrySet ().stream().findFirst().get() return the same instance.

like image 150
Eran Avatar answered Sep 17 '22 21:09

Eran


The thing is:

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

You are not comparing the values you put into the map. But Entry objects!

In other words: it looks like your code is creating new Entry objects using your code. It is completely up to the internal implementation of that unmodifiable Map/Set what to return when it is asked for an iterator or a stream ... and as Eran was a bit quicker to lookup: the reason is that new Entry objects are created when iterating.

So, when using equals() instead of == ... you get the expected output.

like image 37
GhostCat Avatar answered Sep 19 '22 21:09

GhostCat