2

I got a group of Integers and I want to count the amount of max() integers my stream contains. The max() method is from within the Stream API.

I was going for something like this

int count = Arrays.stream(myIntArray)
            .filter(i -> i == max())
            .count();
System.out.printf("Count: %d", count);

I can't call the max() method from within my forEach() method since that's not how Streams function – so what can I do to make this work?

5
  • what does max() return? Commented Apr 14, 2017 at 18:35
  • @m_callens max() returns the largest Integer in the array Stream. Commented Apr 14, 2017 at 18:37
  • do you want to call the max method which belongs to the stream or your own custom max method? Commented Apr 14, 2017 at 18:39
  • I am trying to use the max() method within the Stream API, sorry. That was not clear enough. Commented Apr 14, 2017 at 18:41
  • 2
    @Aphex do you know you cannot use "count" inside the lambda because it has to be "final" for it to be allowed and you cannot change the value of "count" when declared as "final". Commented Apr 14, 2017 at 18:43

5 Answers 5

10

You can't do anything like this, not without a lot of hassle. The simplest way of writing what you want would be two stages:

int max = Arrays.stream(array).max().getAsInt();
int count = (int) Arrays.stream(array).filter(i -> i == max).count();

If you insist on doing it in one pass, I'd write something like

int[] maxAndCount = Arrays.stream(array).collect(
    () -> new int[2], // first max, then count
    (maxAndCount, i) -> {
      if (i > maxAndCount[0] || maxAndCount[1] == 0) {
        maxAndCount[0] = i;
        maxAndCount[1] = 1;
      } else if (i == maxAndCount[0]) {
        maxAndCount[1]++;
      }
    },
    (maxAndCount1, maxAndCount2) -> {
      if (maxAndCount1[0] < maxAndCount2[0]) {
        maxAndCount1[0] = maxAndCount2[0];
        maxAndCount1[1] = maxAndCount2[1];
      } else if (maxAndCount1[0] == maxAndCount2[0]) {
        maxAndCount1[1] += maxAndCount2[1];
      }
    });
  int count = maxAndCount[1];

...but honestly, the simple two-stage version is hard to beat. (And frankly I'd expect it to perform better.)

Sign up to request clarification or add additional context in comments.

10 Comments

@JacobG. Read the return type of Arrays.stream(int[])
Using getAsInt() means that an empty array will fail with NoSuchElementException, rather than result in a max count of 0 (there are no max elements, so the count of them is 0). Since OP is only interested in the count, not the max value itself, using orElse(0) would be better.
Curious: Why do you expect that iterating the array twice is faster than collecting max and count in a single iteration? If your answer is "because of index access of the int[2] array", then I'd say that using a class instead would fix that. See my answer to another question.
Not sure what you mean by "No heap variables to mess with", since your code uses heap when calling the filter lambda expression. Calling the filter() predicate or the collect() accumulator is about the same thing, but the double-streaming means that you have the overhead of sending the iterated values down-stream twice, which is using the heap. --- Anyway, kinda moot point, because if you truly wanted fast, you wouldn't be using stream for this, just a regular for loop, so I agree that the two-liner is better / simpler / easier to understand, but performance is not why.
Up-voted, but I still think you should use orElse(0).
|
1

If you want to use streams for this, you have to use an object that holds the state - you could use a custom collector for this task:

public static void main(String[] args) {
    final int[] arr = {1, 2, 3, 4, 5, 1, 2, 3, 5};
    class MaxCollector {

        int max = Integer.MIN_VALUE;
        int count = 0;

        public void accept(int value) {
            if (max < value) {
                max = value;
                count = 1;
            } else if (max == value) {
                count++;
            }
        }

        public void combine(MaxCollector other) {
            if (max == other.max) {
                count += other.count;
            } else if (max < other.max) {
                max = other.max;
                count = other.count;
            }
        }
    }
    final MaxCollector m = Arrays.stream(arr).collect(
            MaxCollector::new, MaxCollector::accept, MaxCollector::combine);
    System.out.println(m.max + " : " + m.count);
}

Comments

1

You can use the below streams code (with inline comments) as well which gives the number of occurrences (of the maximum number):

int maxValueCount = Arrays.stream(myIntArray).
      collect(Collectors.groupingBy(Function.identity(), 
      Collectors.counting())).//group by numbers and counts first
      entrySet().stream().//get Elements from group Map
       max((entry1, entry2) -> 
           (int)(entry1.getValue() - entry2.getValue())).//find max number in map
       get().getValue();//get the count of maximum number

Comments

0

You could get the max first, then stream it.

int max = Arrays.stream(myIntArray).max().getAsInt(); 

int count = Arrays.stream(myIntArray)
            .filter(i -> i == max)
            .count();
System.out.printf("Count: %d", count);

Comments

0

Just use the frequency method in Collections class. It exists exactly for this purpose.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.