Java:使用流API查找嵌套列表中的公共项
假设我有一张动物名单。此嵌套列表表示一个位置列表,其中每个位置都包含一个动物列表 我需要找出至少出现在两个不同地方的动物种类列表。我知道我可以做正常的循环,也可以做到这一点。是否有任何方法可以通过流API实现这一点 例如:Java:使用流API查找嵌套列表中的公共项,java,java-8,java-stream,Java,Java 8,Java Stream,假设我有一张动物名单。此嵌套列表表示一个位置列表,其中每个位置都包含一个动物列表 我需要找出至少出现在两个不同地方的动物种类列表。我知道我可以做正常的循环,也可以做到这一点。是否有任何方法可以通过流API实现这一点 例如: List<List<Animal>> animals = new ArrayList<>(); animals.add(Arrays.asList(new Dog(), new Cat())); animals.add(Arrays.asL
List<List<Animal>> animals = new ArrayList<>();
animals.add(Arrays.asList(new Dog(), new Cat()));
animals.add(Arrays.asList(new Dog(), new Bird()));
animals.add(Arrays.asList(new Bird()));
使现代化
在没有流API的情况下执行此操作的代码:
final List<List<Animal>> animals = new ArrayList<>();
animals.add(Arrays.asList(new Dog(), new Cat()));
animals.add(Arrays.asList(new Dog(), new Bird()));
animals.add(Arrays.asList(new Bird()));
final Map<Class<? extends Animal>, Integer> count = new HashMap<>();
for (final List<Animal> place : animals) {
final Set<Class<? extends Animal>> uniqueTypes = new HashSet<>();
for (final Animal animal : place) {
uniqueTypes.add(animal.getClass());
}
for (final Class<? extends Animal> type : uniqueTypes) {
if (!count.containsKey(type))
{
count.put(type, 1);
}
else
{
count.put(type, count.get(type).intValue() + 1);
}
}
}
final List<Class<? extends Animal>> typesAppearingAtLeastAtTwoPlaces = new ArrayList<>();
for (final Class<? extends Animal> type : count.keySet()) {
if (count.get(type).intValue() >= 2) {
typesAppearingAtLeastAtTwoPlaces.add(type);
}
}
System.out.println(typesAppearingAtLeastAtTwoPlaces);
首先,数一数所有动物,然后选择出现多次的动物:
import static java.util.stream.Collectors.*;
.....
Map<Class<? extends Animal>, Long> animalCounts = animals.stream()
.flatMap(
lst -> lst.stream()
.map(a -> a.getClass())
.distinct() // in case several of the same animal are in the same place
)
.collect(groupingBy(x -> x, counting()));
List<Class<? extends Animal>> animalTypes = animalCounts.entrySet().stream()
.filter(e -> e.getValue() > 1)
.map(Map.Entry::getKey)
.collect(toList());
首先,数一数所有动物,然后选择出现多次的动物:
import static java.util.stream.Collectors.*;
.....
Map<Class<? extends Animal>, Long> animalCounts = animals.stream()
.flatMap(
lst -> lst.stream()
.map(a -> a.getClass())
.distinct() // in case several of the same animal are in the same place
)
.collect(groupingBy(x -> x, counting()));
List<Class<? extends Animal>> animalTypes = animalCounts.entrySet().stream()
.filter(e -> e.getValue() > 1)
.map(Map.Entry::getKey)
.collect(toList());
首先也是最重要的一点是,在您的尝试中,可能应该使用flatMap而不是map animals.stream.mapplace->place.stream.mapanimal->animal.getClass.collectCollectors.toSet 第二,实际上我们可以使用外部ConcurrentHashMap来实现这一点,这将使我们能够在需要时使用parallel
ConcurrentHashMap<Class, AtomicLong> theCounterMap = new ConcurrentHashMap<>();
animals.stream().flatMap(list -> list.stream().map(animal -> animal.getClass()).distinct())
.forEach(clazz -> theCounterMap.computeIfAbsent(clazz, k -> new AtomicLong()).getAndIncrement());
List<Class> classList = theCounterMap.entrySet().stream()
.filter(entry -> entry.getValue().get() > 1)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
但是如果您需要跟踪源列表作为两个不同的位置,那么您需要进一步修改上面的解决方案
更新
根据@shmosel的建议,您可以直接使用一种更简单的方法来实现以下目的:
Map<Class, Long> theCounterMap = animals.stream().flatMap(list -> list.stream().map(animal -> animal.getClass()).distinct())
.collect(Collectors.groupingBy(e -> e, Collectors.counting()));
List<Class> classList = theCounterMap.entrySet().stream()
.filter(entry -> entry.getValue() > 1)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
首先也是最重要的一点是,在您的尝试中,可能应该使用flatMap而不是map animals.stream.mapplace->place.stream.mapanimal->animal.getClass.collectCollectors.toSet 第二,实际上我们可以使用外部ConcurrentHashMap来实现这一点,这将使我们能够在需要时使用parallel
ConcurrentHashMap<Class, AtomicLong> theCounterMap = new ConcurrentHashMap<>();
animals.stream().flatMap(list -> list.stream().map(animal -> animal.getClass()).distinct())
.forEach(clazz -> theCounterMap.computeIfAbsent(clazz, k -> new AtomicLong()).getAndIncrement());
List<Class> classList = theCounterMap.entrySet().stream()
.filter(entry -> entry.getValue().get() > 1)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
但是如果您需要跟踪源列表作为两个不同的位置,那么您需要进一步修改上面的解决方案
更新
根据@shmosel的建议,您可以直接使用一种更简单的方法来实现以下目的:
Map<Class, Long> theCounterMap = animals.stream().flatMap(list -> list.stream().map(animal -> animal.getClass()).distinct())
.collect(Collectors.groupingBy(e -> e, Collectors.counting()));
List<Class> classList = theCounterMap.entrySet().stream()
.filter(entry -> entry.getValue() > 1)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
我想你也可以试试。它使您有机会编写更简洁、可读性更好的代码:
StreamEx.of(animals)
.flatMap(e -> e.stream().map(Animal::getClass).distinct())
.distinct(2).toList();
我想你也可以试试。它使您有机会编写更简洁、可读性更好的代码:
StreamEx.of(animals)
.flatMap(e -> e.stream().map(Animal::getClass).distinct())
.distinct(2).toList();
为什么要修改外部映射而不是使用收集器?你为什么不使用普通的Long?@shmosel使计数器更便于携带,因为我并不确切知道OP的用途——只是两次或更多。作为另一种方式,Long是可以的,但我个人更喜欢AtomicLong。什么计数器?如何携带?两次什么?你还在不必要地使用forEach。Just.collectCollectors.toList.现在它基本上是@Misha答案的复制品:为什么要修改外部地图而不是使用收集器?你为什么不使用普通的Long?@shmosel使计数器更便于携带,因为我并不确切知道OP的用途——只是两次或更多。作为另一种方式,Long是可以的,但我个人更喜欢AtomicLong。什么计数器?如何携带?两次什么?你还在不必要地使用forEach。Just.collectCollectors.toList.现在它基本上是@Misha答案的副本:@Radiodef Updated.@Radiodef Updated.谢谢。看起来我需要看看类中的所有这些方法。谢谢。看起来我需要查看Collectors类中的所有方法。您需要使用Animal::getClass来获取类您需要使用Animal::getClass来获取类