2017-06-22 2 views
20

À ma connaissance, le code suivant doit imprimer true, puisque les deux Stream et Iterator pointent vers le premier élément.Flux contre les itérateurs dans l'ensemble

Cependant, quand je lance le code suivant il est l'impression 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

Non, ils ne devraient pas, puisque l'entrySet d'un HashMap n'a aucun ordre. –

+3

@ JBNizet-- D'accord, mais j'ai seulement 1 élément dans ma carte Monsieur. –

+14

Les cartes ne garantissent pas non plus qu'elles ne créent pas de nouvelles instances Map.Entry à la volée. Ne comparez pas les objets avec ==. Utilisez égal(). –

Répondre

33

Les deux entrées font référence à la même entrée logique de votre carte (dont la clé est "A" et la valeur "B"). Cependant, ils ne sont pas la même instance.

Si vous creusez assez profond dans la mise en œuvre de Collections.unmodifiableMap(map) vous verrez que sur le itérer entrySet de la carte retournée par Collections.unmodifiableMap(map) retourne une nouvelle Map.Entry qui enveloppe l'entrée modifiable d'origine:

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

Je suis En supposant qu'une nouvelle instance Map.Entry est également créée lorsque vous appelez set.stream().findFirst().get(), les deux méthodes renvoient des instances différentes.

Même si vous appelez la même méthode deux fois, vous obtiendrez des cas de différence, à savoir le code suivant également imprimer false:

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

D'autre part, si vous obtenez directement l'entrée de la d'origine HashMap, vous obtiendrez 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); 

Si ce cas l'entrée est enveloppé par une nouvelle instance, donc à la fois entrySet().iterator().next() et entrySet().stream().findFirst().get() retourner les s ame exemple.

+2

Félicitations pour trouver cette ligne dans next(). J'ai fait quelques recherches aussi, mais j'ai abandonné rapidement. Correction de l'indentation btw salissante ;-) – GhostCat

+2

Grande trouvaille. On dirait que cette question n'a rien à voir avec stream vs itérateur, cependant, elle est liée à l'implémentation de unmodifiableMap. – GaurZilla

12

La chose est:

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

Vous ne comparez pas les valeurs que vous mettez dans la carte. Mais Entrée objets! En d'autres termes: il semble que votre code soit en train de créer new Objets d'entrée utilisant votre code. Il est complètement à la interne mise en œuvre de ce non modifiable Map/Set ce qu'il faut retourner quand il est demandé un itérateur ou un flux ... et comme Eran était un peu plus rapide à rechercher: la raison est que nouveau Entrée les objets sont créés lorsque en cours d'itération. Donc, lorsque vous utilisez equals() au lieu de == ... vous obtenez la sortie attendue.

2

Non, les deux entry1 et entry2 ont la même valeur mais ils ne pointent pas le même objet car chaque fois que vous obtenez l'objet Map.Entry, il en crée un nouveau.
Regardez le code ci-dessous:

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())); 
     } 
    } 
} 

La sortie est:

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() donnera le code de hachage.