2

Any suggestion to simplify the following code block using Java 8 features?

int[] ans = new int[2];
list.forEach(i -> {
    if (i > 0) {
        ans[0] += 1;
    } else if (i < 0) {
        ans[1] += 1;
    }
});

P.S. Not sure if I should post this here

1

5 Answers 5

4

If you don't want to count zeros, you code is as simple as it can get. If you wanted to count zeros as positive, however then you could shorten it to this.

int[] ans = new int[2];
for (int i : list) ans[i < 0 ? 1 : 0] += 1;
Sign up to request clarification or add additional context in comments.

2 Comments

@Eugene There is no <<< perhaps you meant >>> If you don't know the type of i you can use i >>> -1 which works for int and long.
@PeterLawrey ha! had to go to the specification to understand if this is specified or a hack, nice. thank u
2
 ans[0] = (int)list.stream().filter(x -> x < 0).count();
 ans[1] = (int)list.stream().filter(x -> x > 0).count();

but I hardly think this is a simplification as such, your solution is already as simple as it can get.

In case you do want to count zeros, this could be simplified to:

 list.forEach(x -> ++ans[x >>> 31])

10 Comments

You're actually doing the iteration work twice here aren't you? I agree there's unlikely to be a simpler solution than the one provided
@LoztInSpace you are absolutely correct, as said this is hardly a simplification...
@Eugene, you don't need second iteration. ans[1] = list.size() - ans[0]
@HadiJ I thought about that, but in such a case we don't take 0 into account and the OP's code does
Yes I was thinking that too. In C you could go something like ++ans[i>>31] but those pesky 0s got in the way,
|
1

I'd suggest

int[] ans = new int[2];
list.forEach(i -> ans[i >>> 31] += i==0 ? 0 : 1);

where i >>> 31 discards all but the sign bit (i.e., is the same as i<0 ? 1 : 0) and the second conditional handles zero.

I don't claim, it's really better than the original.

3 Comments

You can do it entirely branch-free with .forEach(i -> ans[i >>> 31] += i>>>31|-i>>>31)
@Holger Nice and simple. I guess, I can hope for my conditional being translated using cmov, but your expression can be even then faster because of the common subexpression.
@maaartinus that is almost the trick behind Integer::signum btw, very good answer still, even without this simplification...1+
1

To distinguish two cases like i > 0 and i < 0 we can use Stream.partition:

Map<Boolean, List<Integer>> partitioned = list.stream()
  .filter(i -> i != 0)
  .collect(Collectors.partitioningBy(i -> i > 0));
ans[0] = partitioned.get(true).size();
ans[1] = partitioned.get(false).size();

Is it simplified? At least it's still readable and easy to understand.

EDIT
Or as @saka1029 suggests:

Map<Boolean, Long> partitioned = list.stream()
  .filter(i -> i != 0)
  .collect(Collectors.partitioningBy(i -> i > 0, Collectors.counting()));
ans[0] = partitioned.get(true);
ans[1] = partitioned.get(false);

EDIT
And a further Stream solution which returns the desired array. But I would argue it's not simpler. So it's about to compare.

int[] ans = list.stream().filter(i -> i != 0).collect(
  () -> new int[2], 
  (arr, i) -> arr[i > 0 ? 0 : 1]++, 
  (l, r) -> {  l[0] += r[0]; l[1] += r[1]; });

2 Comments

You can collect counts by .collect(Collectors.partitioningBy(i -> i > 0, Collectors.counting()))
@saka1029 Thank you for your suggestion. I included it in my answer.
0

Here's a way that counts zeroes as well:

int[] ans = new int[3];
list.forEach(i -> ans[Integer.signum(i) + 1] += 1);

The ans array now holds 3 values: index 0 for negative count, index 1 for zero count and index 2 for positive count.

i.e. for the following input: [1, 2, 3, 0, -4, -5], the output would be: [2, 1, 3].


A more functional way, excluding zeroes:

Map<Integer, Long> bySignum = list.stream()
    .filter(i -> i != 0)
    .collect(Collectors.groupingBy(Integer::signum, Collectors.counting()));

Which produces this output: {-1=2, 1=3}.

2 Comments

well, if you happen to know how Integer::signum is implemented, i >> 31 | -i >>> 31 - which is sort of crazy how smart it is, you could say ans[i >>> 31] = i >>> 31 | -i >>> 31 as Holger has shown above. The explanation for this is interesting too. if i is negative, i >>> 31 will give 1 (index), if i is positive i >>> 31 will give 0. that is the index computation. and the part i >>> 31 | -i >>> 31 is even funner!!
@Eugene Well, yeah, it's clever, I guess. I wouldn't sacrifice code readability for this, it's OK if we're just having fun (this is the modern equivalent of playing crosswords), And it is ans[i >>> 31] += i >>> 31 | -i >>> 31 (you forgot the +sign)

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.