简体   繁体   中英

Java 8 Stream Api Filter specific range

I have a list like this:

int[] counting = new int[]{1,0,-1,-2,-1,-2,-1,0,1,2,1,0,-1,0,1,2};

I want to filter if there is a negative value between 0 and 0 numbers.

For above list, result should return 2 because between 0 and 0 there is a negative number and it also valid for third range.

List<Integer> result = Arrays.stream(counting).filter(x ->  ).collect(Collectors.toList());

How can i collect range values? After that how can I check there is a negative number or not?

In other words I want to count how many pairs of (0, 0) there are with a negative value between them. So since there's a negative value in -1,-2,-1,-2,-1 and one in -1, the count of such pairs is 2.

Don't use a stream pipeline for that. Streams are well suited for independent processing and filtering of elements. They are not suited for processing that depends on the presence or absence of other elements in the stream (here 0 earlier and later).

Use a loop. Before entering the loop, check that the first element in the list is 0 as required, and throw an exception if not (so your example list from the question won't pass this test). Declare some variables that we need during looping:

   int negativeCount = 0;
   boolean hasSeenNegativeSinceLastZero = false;

In your loop, each time you see a negative number, set hasSeenNegativeSinceLastZero to true. Each time you see a 0, if hasSeenNegativeSinceLastZero is true, then add 1 to the count and set hasSeenNegativeSinceLastZero to false.

Happy coding.

Here's a stream-based solution. I doubt it's more readable than a simple for loop.

int[] zeroIndices = IntStream.range(0, counting.length)
                .filter(i -> 0 == counting[i])
                .toArray();

long count = IntStream.range(0, zeroIndices.length)
        .mapToObj(i -> new int[] { i, zeroIndices[i] })
        .skip(1)
        .filter(arr -> Arrays.stream(Arrays.copyOfRange(counting, 
                                                        zeroIndices[arr[0] - 1], 
                                                        arr[1]))
                             .anyMatch(i -> i < 0))
        .count();

I personally don't think stream solution will be readable, you should use a simple looping, but you can mix a bit. Here what I think it should be like

int[] counting = new int[] {1, 0, -1, -2, -1, -2, -1, 0, 1, 2, 1, 0, -1, 0, 1, 2};

List<Integer> indices = new ArrayList<>();
for (var i = 0; i < counting.length; i++) {
  if (counting[i] == 0) {
    indices.add(i);
  }
}

if (indices.size() % 2 == 0) {
  System.out.println("You got " + indices.size() / 2 + " pairs");
} else {
  System.err.println(
      "You don't have complete pairs, you have only " + indices.size() / 2 + " pairs");
  System.exit(-1);
}

for (var i = 0; i < indices.size(); i = i + 2) {
  System.out.println("-----Pair----");
  stream(copyOfRange(counting, indices.get(i), indices.get(i + 1)))
      .filter(v -> v < 0)
      .forEach(System.out::println);
}

It will print each pair output like this

You got 2 pairs
-----Pair----
-1
-2
-1
-2
-1
-----Pair----
-1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM