Java 8 Streams:获得非重复计数

时间:2018-08-16 14:29:00

标签: java java-8 java-stream

这是输入和输出的SQL版本:

     with tab1 as (

        select 1 as id from dual union all
        select 1 as id from dual union all
        select 2 as id from dual union all
        select 2 as id from dual union all
        select 5 as id from dual 
        )

    select id from tab1 group by id having count(id)=1;

Output is Id=5 and count is 1

5不重复,如何使用JAVA 8流实现它?

我在下面尝试过,但是显然给出了错误的结果

List<Integer> myList = new ArrayList<Integer>();
                myList.add(1);
                myList.add(1);
                myList.add(2);
                myList.add(2);
                myList.add(5);

                Long f =  myList.stream()
                          .distinct().count();

                System.out.println(f);

5 个答案:

答案 0 :(得分:4)

long result = myList.stream()
         .collect(Collectors.groupingBy(
                   Function.identity(),
                  Collectors.counting()))
         .entrySet()
         .stream()
         .filter(entry -> entry.getValue() == 1)
         .map(Entry::getKey)
         .count();

好吧,您将所有元素收集到Map<Integer, Long>中,其中的键是值本身,值是其重复的次数。稍后,我们从结果映射中流式传输条目集,然后filter仅流传计数为1的条目(这意味着它们不会重复),之后我们将map到{{1} }-从列表中获取该值并计数。

答案 1 :(得分:2)

一种替代方法是从列表中过滤出重复的元素,但效率不高,因此仅在列表较小时才使用它:

List<Integer> result = myList.stream()
                             .filter(i -> Collections.frequency(myList, i) == 1)
                             .collect(toList());

答案 2 :(得分:2)

为完整起见,这是一种Java 8方式,它不使用流:

Map<Integer, Long> frequencyMap = new LinkedHashMap<>(); // preserves order

myList.forEach(element -> frequencyMap.merge(element, 1L, Long::sum)); // group, counting

frequencyMap.values().removeIf(count -> count > 1); // remove repeated elements

Set<Integer> nonRepeated = frequencyMap.keySet(); // this is your result

这使用Map.merge用每个元素的计数填充地图。然后,对映射的值使用Collection.removeIf,以删除其值大于1的条目。

我发现这种方式比使用流更紧凑,更易读。

答案 3 :(得分:1)

您可以这样做

Map<Integer, Long> countByNumber = myList.stream()
    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
List<Integer> uniqueNumbers = myList.stream()
    .filter(n -> countByNumber.get(n) == 1)
    .collect(Collectors.toList());

首先使用值和出现次数创建一个地图。然后遍历数字List,从map获取每个数字的出现次数。如果出现的次数为1,则将其收集到一个单独的容器中。

如果想一次完成,就在这里。

List<Integer> uniqueNumbers = myList.stream()
    .collect(Collectors.collectingAndThen(Collectors.groupingBy(Function.identity(), 
        Collectors.counting()),
    m -> myList.stream().filter(n -> m.get(n) == 1).collect(Collectors.toList())));

答案 4 :(得分:1)

可供您考虑的另一种选择:

    //Create a map where the key is the element you want to count and the value is the actual count
    Map<Integer, Long> countByItem = myList.stream()
        .collect(Collectors.groupingBy(
                Function.identity(), //the function that will return the key
                Collectors.counting())); //the function that will actually count the occurences

    //Once you have that map, all you need to do is filter for the registers with count == 1
    List<Integer> result = countByItem.entrySet().stream()
        .filter(entry -> entry.getValue() == 1)
        .map(Map.Entry::getKey)
        .collect(Collectors.toList());