假设我有一个List<List<Animal>> animals
。此嵌套列表表示一个地方列表,每个地方都包含动物列表。
我需要找出至少出现在两个不同位置的动物类型列表。我知道我可以进行普通循环并执行该操作。有什么方法可以通过Stream API完成吗?
示例:
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()));
预期(等于):
List<Class<? extends Animal>> animalTypes = Arrays.asList(Dog.class, Bird.class);
至于尝试,我只设法将内部列表转换为一组类:
animals.stream().map(place -> place.stream().map(animal -> animal.getClass()).collect(Collectors.toSet()));
无需Stream 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);
输出:
[class Test$Dog, class Test$Bird]
答案 0 :(得分:5)
首先,对所有动物进行计数,然后选择出现一次以上的动物:
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());
答案 1 :(得分:2)
我认为您也可以尝试StreamEx。它使您有机会编写更简洁,更易读的代码:
StreamEx.of(animals)
.flatMap(e -> e.stream().map(Animal::getClass).distinct())
.distinct(2).toList();
答案 2 :(得分:0)
首先,也许您应该尝试使用 flatMap 而不是 map 。
animals.stream()。map(place-> place.stream()。map(animal-> animal.getClass())。collect(Collectors.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());