[JAVA] Pour faire Stream.distinct avec les propriétés de champ, etc.

Pour effectuer Stream.distinct avec des champs, des propriétés, des résultats de calcul, etc.

Stream.distinct de java Stream API ne peut pas prendre une expression lambda comme argument.

class Item{
	String name,shop;
	int price;
	Item(String n,int p,String s){ name=n; price=p; shop=s; }
	public String toString(){ return name+", "+price+", "+shop; }
}
Item[] items = {
	new Item("item-1",1000,"shop-A"),
	new Item("item-2",1100,"shop-B"),
	new Item("item-3",1200,"shop-C"),
	new Item("item-4",2000,"shop-A"),
	new Item("item-5",2100,"shop-B"),
	new Item("item-6",2200,"shop-C"),
	new Item("item-7",3000,"shop-A"),
	new Item("item-8",3100,"shop-B"),
	new Item("item-9",3200,"shop-C"),
};

Pour récupérer un produit par boutique à partir du tableau ci-dessus, si distinct pouvait prendre une expression lambda comme argument, ce serait:

Stream.of(items)
	.distinct(item->item.shop)
	...

Mais je ne peux pas l'écrire comme ça, je vais donc utiliser une combinaison de filtre et Set à la place.

Set<String> unique = new HashSet<>();
Stream.of(items)
	.filter(item->unique.add(item.shop))
	.forEach(System.out::println);
	
> item-1, 1000, shop-A
> item-2, 1100, shop-B
> item-3, 1200, shop-C

La raison pour laquelle cela équivaut à distinct est que Set.add se comporte comme suit.

Si l'élément spécifié n'est pas inclus dans l'ensemble, il est ajouté à l'ensemble et renvoie true. Si l'élément spécifié est déjà inclus dans l'ensemble, il renvoie false sans modifier l'ensemble.

En d'autres termes, il peut être utilisé comme une expression conditionnelle qui renvoie true pour le premier des éléments dupliqués.

En flux parallèle

Cependant, HashSet n'est pas thread-safe et est dangereux pour les flux parallèles.

Set unique = new HashSet <> (); // Non thread-safe! Stream.of(items) .parallel () // Traité dans plusieurs threads! .filter(item->unique.add(item.shop)) .forEach(System.out::println);

item-7,3000, shop-A // Dupliquer! item-5,2100,shop-B item-1,1000, shop-A // Dupliquer! item-6,2200,shop-C

A une vitesse d'environ 1/100, le résultat ci-dessus a été obtenu. Au lieu de cela, utilisez ConcurrentHashMap, qui prend en charge la concurrence, en le convertissant en un ensemble avec Collections.newSetFromMap.

Set<String> unique = Collections.newSetFromMap(new ConcurrentHashMap<>());
Stream.of(items)
	.parallel()
	.filter(item->unique.add(item.shop))
	.forEach(System.out::println);

> item-6, 2200, shop-C
> item-5, 2100, shop-B
> item-7, 3000, shop-A

Ok

Recommended Posts

Pour faire Stream.distinct avec les propriétés de champ, etc.
Comment faire un contrôle basé sur l'API avec cancancan
Comment obtenir JDK etc. depuis Oracle avec CLI