Oracle Mokey
Oracle Mokey

Reputation: 81

Java 8 Streams : get non repeated counts

Here is the SQL version for the input and output :

     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

As 5 is non repeated.How do i implement it using JAVA 8 streams?

I tried below but obviously it is giving wrong result

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);

Upvotes: 6

Views: 3158

Answers (5)

fps
fps

Reputation: 34460

For completeness, here's a Java 8 way which doesn't make use of streams:

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

This uses Map.merge to fill the map with the count of each element. Then, Collection.removeIf is used on the values of the map, in order to remove the entries whose value is greater than 1.

I find this way much more compact and readable than using streams.

Upvotes: 2

Ravindra Ranwala
Ravindra Ranwala

Reputation: 21124

You may do it like so,

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());

First create a map using the value and the number of occurences. Then iterate over the List of numbers, get the occurences from the map for each number. If the number of occurences is 1, then collect it into a separate container.

If you want to do it in one go, here it is.

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())));

Upvotes: 1

Samuel Negri
Samuel Negri

Reputation: 519

Another option for you to consider:

    //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());

Upvotes: 1

assylias
assylias

Reputation: 328598

An alternative is to filter repeated elements out of the list but it's not very efficient so I would only use it if the list is small:

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

Upvotes: 2

Eugene
Eugene

Reputation: 120848

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

Well you collect all the elements to a Map<Integer, Long>, where the key is the value itself, and value is how many times it is repeated. Later we stream the entry set from that resulting map and filter only those entries that that have a count of 1 (meaning they are not repeated), later we map to Entry::getKey - to get that value from the List and count.

Upvotes: 4

Related Questions