10

I am aware my question is very similar to Count int occurrences with Java8 , but I still cannot solve my case, which must be easier to solve.

The need is to count how many times integers repeat in a stream of integers (will be coming from a file, may be up to 1000000 integers). I thought it might be useful to create a map, where Integer will be a Key, and number of occurrences will be a value.

The exception is

Error:(61, 66) java: method collect in interface java.util.stream.IntStream cannot be applied to given types;
required: java.util.function.Supplier,java.util.function.ObjIntConsumer,java.util.function.BiConsumer found: java.util.stream.Collector> reason: cannot infer type-variable(s) R (actual and formal argument lists differ in length)

However, in Java 8 there is a Collectors.groupingBy, which should suffice

 Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream) 

The problem is that my code is not compiling and I do not see - why. I simplified it to this:

Map<Integer,Integer> result = IntStream.range(0,100).collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); 

What is the reason for not compiling? Thank you in advance :)

Community
  • 1
  • 1
Nikita Koselev
  • 185
  • 2
  • 10

3 Answers3

19

IntStream has one method collect where the second argument operates on an int not an Object. Using boxed() turns an IntStream into a Stream<Integer>

Also counting() returns a long.

Map<Integer, Long> result = IntStream.range(0, 100).boxed()
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
2

I have solved the task at hand using Peter's idea. I'm posting the solution in case somebody is studying Java 8 and does not want to repeat my mistakes.

The task was to:

  1. read numbers from file
  2. find how often each number occurs
  3. find how many pairs can be found for numbers occurring more than once. For example, if number 3 occurs 4 times, we will have 6 pairs (I used Apache's CombinatoricsUtils.binomialCoefficient for that).

My solution:

long result = Arrays.stream(Files.lines(Paths.get(fileName)).mapToInt(Integer::parseInt).collect(() ->
                new int[BYTE_MAX_VALUE], (array, value) -> array[value] += 1, (a1, a2) ->
                Arrays.setAll(a1, i -> a1[i] + a2[i]))).map((int i) -> combinatorics(i, 2)).sum()
Nikita Koselev
  • 185
  • 2
  • 10
1

If you're open to using a third party library with primitive collections, you can potentially avoid the boxing operations. For example, if you use Eclipse Collections, you can write the following.

IntBag integers = Interval.oneTo(100).collectInt(i -> i % 10).toBag();

Assert.assertEquals(10, integers.occurrencesOf(0));
Assert.assertEquals(10, integers.occurrencesOf(1));
Assert.assertEquals(10, integers.occurrencesOf(9));

An IntHashBag is implemented by using an IntIntHashMap, so neither the keys (your integers) nor the values (the counts) are boxed.

The same can be accomplished if you loop through your file and add your results to an IntHashBag from an IntStream.

MutableIntBag integers = IntBags.mutable.empty();
IntStream.range(1, 101).map(i -> i % 10).forEach(integers::add);

Assert.assertEquals(10, integers.occurrencesOf(0));
Assert.assertEquals(10, integers.occurrencesOf(1));
Assert.assertEquals(10, integers.occurrencesOf(9));

Note: I am a committer for Eclipse Collections.

Donald Raab
  • 6,458
  • 2
  • 36
  • 44