简体   繁体   中英

Conditions for a List with Java8 with Consumer or Filter, which way is better

I have tried this

    List<Integer> numbers = Arrays.asList(1, 1, 0, -1, -1);
    List<Integer> positiveNum2 = new ArrayList<>();
    List<Integer> negativeNum2 = new ArrayList<>();
    List<Integer> zeroNumbers2 = new ArrayList<>();
    List<Integer> positiveNumbers = numbers.stream().filter(number -> number > 0).collect(Collectors.toList());
    List<Integer> negativeNumbers = numbers.stream().filter(number -> number < 0).collect(Collectors.toList());
    List<Integer> zeroNumbers = numbers.stream().filter(number -> number.equals(0)).collect(Collectors.toList());
    positiveNumbers.forEach(System.out::println);
    negativeNumbers.forEach(System.out::println);
    zeroNumbers.forEach(System.out::println);
    System.out.println("*********with Consumer******************");
    Consumer<Integer> determineNumber = number -> {
        if (number > 0) {
            positiveNum2.add(number);
        } else if (number < 0) {
            negativeNum2.add(number);
        } else {
            zeroNumbers2.add(number);
        }

    };
    numbers.forEach(determineNumber);
    positiveNum2.forEach(System.out::println);
    negativeNum2.forEach(System.out::println);
    zeroNumbers2.forEach(System.out::println);

but I dont know which one is better, I think the forEach with the Consumer, but the Consumer does three validations, therefore, does not have single responsability

I would generally prefer your Consumer based solution as it at least encapsulates the whole operation in one call/stream. But I think you don't yet take full advantage of the functional approach of streams.

You could use a simple stream/collect to achieve this sorting:

numbers .stream()
        .collect(Collectors.groupingBy(Math::signum));

will result in a map like this:

{1.0=[1], 0.0=[0, 0], -1.0=[-1, -2, -1]}

This approach avoids side effects (ie does not modify lists outside of the stream's scope) and can hence be extracted more easily and is easily run in parallel.

The consumer only takes one iteration, so it's more efficient, and it does have a single responsibility: dividing the numbers over three lists based on their sign.

For this case, I'd probably use a simple for loop though.

If you really want to over-engineer things, you can define an enum to represent the sign, and use a grouping collector to group into a map:

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import static java.util.stream.Collectors.groupingBy;

public class Test {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 1, 0, -1, -1);

        Map<Sign, List<Integer>> map = numbers
                .stream()
                .collect(groupingBy(i -> i > 0
                                    ? Sign.POSITIVE
                                    : i < 0 
                                        ? Sign.NEGATIVE
                                        : Sign.ZERO));

        System.out.println(map);
    }
}

enum Sign {POSITIVE, NEGATIVE, ZERO}

This yields the following output:

{ZERO=[0], POSITIVE=[1, 1], NEGATIVE=[-1, -1]}

Note: If you want peak performance for future lookups in your map, you can use an EnumMap instead. Take a look at this answer to see how.

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