Java流 - 查找唯一元素

4

我有一个 List<Person> persons = new ArrayList<>();,我想列出所有的独特姓名。我的意思是,如果有"John"、"Max"、"John"、"Greg",那么我只想列出"Max"和"Greg"。是否有一种使用Java流实现的方式?


1
先尝试进行一些基本研究:https://www.google.co.uk/search?q=java%20stream%20unique。 - Oliver Charlesworth
将其流式收集到一个集合中 :) 如果您不关心顺序,只需创建一个集合即可。 - 9000
2
你有任何代码尝试吗? - MaxZoom
2
@9000使用集合并不能解决问题,因为他想要从结果中删除John,因为它是一个重复项! - Nir Alfasi
@alfasin:那么distinct也无济于事。正确的方法是将其缩减为一对映射(名称,计数),然后过滤掉计数等于1的那些。 - 9000
显示剩余2条评论
8个回答

14
我们可以使用流和 Collectors.groupingBy 来计算每个名称出现的次数,然后过滤掉出现超过一次的任何名称:
    List<String> res = persons.stream()
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
            .entrySet()
            .stream()
            .filter(e -> e.getValue() == 1)
            .map(e -> e.getKey())
            .collect(Collectors.toList());

    System.out.println(res); // [Max, Greg]

8
List persons = new ArrayList();
    persons.add("Max");
    persons.add("John");
    persons.add("John");
    persons.add("Greg");

    persons.stream()
             .filter(person -> Collections.frequency(persons, person) == 1)
             .collect(Collectors.toList());

1
如果列表中的元素是对象,则该对象应覆盖equals和hashcode方法以使频率工作。 - SUMIT
此解决方案仅考虑元素频率,而不考虑唯一性(即无重复项)。 - Максим Шатов
Collections.frequency 遍历整个列表并对流的每个元素进行操作,以查找出现次数,因此它的时间复杂度为 O(n^2),非常低效。 - fps

7

这是我的解决方案:

List<String> persons = new ArrayList<>();
persons.add("John");
persons.add("John");
persons.add("MAX");
persons.add("Greg");
persons.stream()
          .distinct()
          .sorted()
          .collect(Collectors.toList());

5

首先猜测解决方案。

persons.stream()
       .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
       .entrySet()
       .stream()
       .filter(entry -> entry.getValue() == 1)
       .map(Map.Entry::getKey)
       .collect(Collectors.toList())

2
这是一篇旧文章,但我想基于自定义收集器提出另一种方法:
public static <T> Collector<T, ?, List<T>> excludingDuplicates() {
    return Collector.<T, Map<T, Boolean>, List<T>>of(
        LinkedHashMap::new,
        (map, elem) -> map.compute(elem, (k, v) -> v == null),
        (left, right) -> {
            right.forEach((k, v) -> left.merge(k, v, (o, n) -> false));
            return left;
        },
        m -> m.keySet().stream().filter(m::get).collect(Collectors.toList()));
}

在这里,我使用Collector.of来创建一个自定义收集器,它将在LinkedHashMap上累积元素:如果元素不存在于键中,则其值将为true,否则将为false。合并函数仅应用于并行流,它将right映射合并到left映射中,通过尝试将right映射的每个条目放入left映射中,并将已存在键的值更改为false。最后,完成函数返回一个列表,其中包含值为true的映射键。

该方法可按以下方式使用:

List<String> people = Arrays.asList("John", "Max", "John", "Greg");

List<String> result = people.stream().collect(excludingDuplicates());

System.out.println(result); // [Max, Greg]

这里有比使用自定义收集器更简单的方法:

Map<String, Boolean> duplicates = new LinkedHashMap<>();
people.forEach(elem -> duplicates.compute(elem, (k, v) -> v != null));
duplicates.values().removeIf(v -> v);

Set<String> allUnique = duplicates.keySet();

System.out.println(allUnique); // [Max, Greg]

2
您可以尝试以下代码。
    List<Person> uniquePersons = personList.stream()
            .collect(Collectors.groupingBy(person -> person.getName()))
            .entrySet().stream().filter(stringListEntry -> stringListEntry.getValue().size()==1)
            .map(stringListEntry -> { return stringListEntry.getValue().get(0); })
            .collect(Collectors.toList());

1

这将删除所有重复的元素。

List<String> persons = new ArrayList<>();

        persons.add("John");
        persons.add("John");
        persons.add("MAX");
        persons.add("Greg");

        Set<String> set = new HashSet<String>();

        Set<String> duplicateSet = new HashSet<String>();

        for (String p : persons) {

            if (!set.add(p)) {
                duplicateSet.add(p);
            }
        }

        System.out.println(duplicateSet.toString());
        set.removeAll(duplicateSet);
        System.out.println(set.toString());

1
您可以使用Collections.frequency来检查列表中元素的出现次数,如下所示,以过滤重复项:
List<String> listInputs = new ArrayList<>();
//add your users
List<String> listOutputs = new ArrayList<>();
for(String value : listInputs) {
     if(Collections.frequency(listInputs, value) ==1) {
         listOutputs.add(value);
     }
}
System.out.println(listOutputs);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接