代码之家  ›  专栏  ›  技术社区  ›  Jai

java:使用stream api在嵌套列表中查找公共项

  •  1
  • Jai  · 技术社区  · 6 年前

    假设我有一个 List<List<Animal>> animals 是的。这个嵌套列表表示一个包含动物列表的位置列表。

    我需要找出至少出现在两个不同地方的动物类型列表。我知道我可以做正常的循环。有没有什么方法可以通过流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()));
    

    更新

    在没有流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]
    
    3 回复  |  直到 6 年前
        1
  •  5
  •   Misha    6 年前

    首先,数一数所有的动物,然后选择出现多次的动物:

    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());
    
        2
  •  2
  •   123-xyz    6 年前

    我想你也可以试试 StreamEx 是的。它使您有机会编写更简洁、可读性更好的代码:

    StreamEx.of(animals)
        .flatMap(e -> e.stream().map(Animal::getClass).distinct())
        .distinct(2).toList();
    
        3
  •  0
  •   Hearen    6 年前

    首先,也许你应该使用 平面图 而不是 地图 在你的尝试中。

    动物.stream().map(place->place.stream().map(animal->animal.getClass()).collect(collectors.toset());

    第二,实际上我们可以使用外部 并发哈希映射 使我们能够使用 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());