DhiwaTdG
DhiwaTdG

Reputation: 788

Java count occurrence of each element in an integer array

I've written the following snippet to count the number of occurrences of each element. Is it possible to achieve this in a much shorter way?

int[] arr = {1, 6, 2, 8, 5, 4, 7, 7, 5, 7};
Arrays.stream(arr)
        .collect(ArrayList::new, ArrayList::add, ArrayList::addAll)
        .stream()
        .collect(Collectors.groupingBy(s -> s))
        .forEach((k, v) -> System.out.println(k+" "+v.size()));

Also I would like to display only the elements which occur more than 1 time. So I tried modifying as below which resulted in an error.

.forEach((k, v) -> if(v.size() > 1) System.out.println(k+" "+v.size()));

What is the correct way to do this?

Upvotes: 8

Views: 9487

Answers (5)

ASR
ASR

Reputation: 3559

List<Integer> numsList=Arrays.asList(1,2,3,5,2,4,3,1,2,2,4,4,5);
Map<Integer, Long> map=numsList.stream().collect(Collectors.groupingBy(Integer::intValue,Collectors.counting()));
map.forEach((k,v)->{System.out.println(k+" = "+v);});

Upvotes: 2

Donald Raab
Donald Raab

Reputation: 6706

If you are open to using a third-party library, Eclipse Collections has a Bag type which can be used as follows:

Bags.mutable.with(1, 6, 2, 8, 5, 4, 7, 7, 5, 7)
    .selectDuplicates()
    .forEachWithOccurrences((k, count) -> System.out.println(k+" "+count));

If you have to keep int[] arr variable as an int array, then you can use an IntBag as follows:

IntBags.mutable.with(arr)
    .selectDuplicates()
    .forEachWithOccurrences((k, count) -> System.out.println(k+" "+count));

IntBag is a primitive collection so does not box the int values into Integer wrappers.

Note: I am a committer for Eclipse Collections.

Upvotes: 3

Eugene
Eugene

Reputation: 121088

Also might be done using frequency :

 List<Integer> list = ImmutableList.of(1, 2, 3, 4, 5, 6, 3, 4, 5);
 Map<Integer, Integer> result = list.stream().distinct().collect(Collectors.toMap(Function.identity(), token -> Collections.frequency(list, token)));

Upvotes: 0

DhiwaTdG
DhiwaTdG

Reputation: 788

I want to share my solution as well!!!

// Solution 1 [Improved from Eran's solution & suggestion]
int[] arr = {1, 6, 2, 8, 5, 4, 7, 7, 5, 7};
Map<Integer, Long> counts = Arrays.stream(arr)
    .boxed()
    .collect(collectingAndThen(groupingBy(n -> n, counting()),
        map -> map.entrySet().stream()
            .filter(n -> n.getValue() > 1)
            .collect(toMap(Entry::getKey, Entry::getValue))
));
System.out.println(counts.toString());

// Solution 2 [Improved from Dici's suggestion]
int[] arr = {1, 6, 2, 8, 5, 4, 7, 7, 5, 7};
Map<Object, Long> counts = Arrays.stream(arr)
    .collect(ArrayList::new, ArrayList::add, ArrayList::addAll)
    .stream()
    .collect(groupingBy(Function.identity(), counting()));
counts.values().removeIf(count -> count < 2);
System.out.println(counts.toString());  

Upvotes: 0

Eran
Eran

Reputation: 394156

For the latter question, you have to change

.forEach((k, v) -> if(v.size() > 1) System.out.println(k+" "+v.size()));

to

.forEach((k, v) -> {if(v.size() > 1) System.out.println(k+" "+v.size());});

For the first part, it's not clear why you need the first collect followed by a second Stream pipeline. If the purpose was to convert an IntStream to a Stream<Integer>, use boxed():

Arrays.stream(arr)
      .boxed()
      .collect(Collectors.groupingBy(s -> s))
      .forEach((k, v) -> System.out.println(k+" "+v.size()));

As Dici suggested, you can also chain Collectors to group each number with its number of occurrences :

Map<Integer,Integer> occurrences = 
    Arrays.stream(arr)
          .boxed()
          .collect(Collectors.groupingBy(s -> s, Collectors.counting()));

Upvotes: 12

Related Questions