java 使用流API根据条件将列表拆分为子列表

kq4fsx7k  于 2023-03-11  发布在  Java
关注(0)|答案(9)|浏览(171)

我有一个具体的问题,有一些类似的问题,但这些问题要么是用Python,而不是用Java,要么是需求不同,即使问题听起来很相似。
我有一个值列表。

List1 = {10, -2, 23, 5, -11, 287, 5, -99}

在一天结束的时候,我想根据列表的值来拆分列表。我的意思是,如果值大于零,它将留在原始列表中,负值列表中相应的索引将设置为零。如果值小于零,它将转到负值列表,原始列表中的负值将替换为零。
生成的列表应该是这样的;

List1 = {10, 0, 23, 5, 0, 287, 5, 0}
List2 = {0, -2, 0, 0, -11, 0, 0, -99}

有没有办法用Java中的Stream API来解决这个问题?

31moq8wy

31moq8wy1#

Map<Boolean, List<Integer>> results=
  List1.stream().collect(Collectors.partitioningBy( n -> n < 0));

我觉得这个更漂亮,更容易阅读。(你可以从Map上得到否定和非否定列表。)

bvpmtnay

bvpmtnay2#

如果要在单个“流”操作中执行此操作,则需要定制收集器:

List<Integer> list = Arrays.asList(10, -2, 23, 5, -11, 287, 5, -99);

List<List<Integer>> result = list.stream().collect(
    () -> Arrays.asList(new ArrayList<>(), new ArrayList<>()),
    (l,i) -> { l.get(0).add(Math.max(0, i)); l.get(1).add(Math.min(0, i)); },
    (a,b) -> { a.get(0).addAll(b.get(0)); a.get(1).addAll(b.get(1)); });

System.out.println(result.get(0));
System.out.println(result.get(1));
omtl5h9j

omtl5h9j3#

从Java 12开始,使用Collectors::teeing就可以非常简单地完成:

var divided = List.of(10, -2, 23, 5, -11, 287, 5, -99)
            .stream()
            .collect(Collectors.teeing(
                    Collectors.mapping(i -> Math.max(0, i), Collectors.toList()),
                    Collectors.mapping(i -> Math.min(0, i), Collectors.toList()),
                    List::of
            ));
u4dcyp6a

u4dcyp6a4#

正如shmosel在评论中指出的,您需要使用流进行两次迭代:

List<Integer> list = Arrays.asList(10, -2, 23, 5, -11, 287, 5, -99);
List<Integer> positives = list.stream().map(i -> i < 0 ? 0 : i).collect(Collectors.toList());
List<Integer> negatives = list.stream().map(i -> i < 0 ? i : 0).collect(Collectors.toList());

如果你的列表是可修改的,那么在一个流中所有的都是可能的。这并不比一个for循环好

List<Integer> list = Arrays.asList(10, -2, 23, 5, -11, 287, 5, -99);
List<Integer> list2 = new ArrayList<>();

IntStream.range(0, list.size()).forEach(i -> {
   int j;
   if ((j = list.get(i)) < 0) {
       list2.add(j);
       list.set(i, 0);
   } else {
       list2.add(0);
   }});
56lgkhnf

56lgkhnf5#

Java-Streams是一种 * 函数式编程 * 特性。
函数式编程的基本模式是将 * 一个 * 集合转换为 * 另一个 * 集合,这意味着你的需求不适合函数式方法,因此java流是第二好的解决方案(在遗留的for(each)循环之后)。
"但是"
当然你可以把这个问题分成两个单独的FP友好操作。
缺点是这需要对输入集合进行额外的循环,对于小的集合(最多大约100000个项)这可能不是问题,但是对于较大的集合,可能会引起性能问题。

**免责声明:**不要因为性能原因而选择或拒绝某个方法,除非您通过 * 使用性能分析工具进行测量 * 证明了您的决定是合理的!

结论:

我认为“legacy循环”是更好的方法,因为从某种意义上说,它可能更具可读性,因为它更好地表达了您的意图(分割集合)。

pod7payv

pod7payv6#

不带流的一般解决方案可能包括根据条件在两个可能的使用者之间进行选择:

private static <T> Consumer<T> splitBy(
        Predicate<T> condition,
        Consumer<T> action1,
        Consumer<T> action2,
        T zero) {
    return n -> {
        if (condition.test(n)) {
            action1.accept(n);
            action2.accept(zero);
        } else {
            action1.accept(zero);
            action2.accept(n);
        }
    };
}

对于您的特定问题,可以使用splitBy方法,如下所示:

List<Integer> list = Arrays.asList(10, -2, 23, 5, -11, 287, 5, -99);

List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();

list.forEach(splitBy(n -> n > 0, list1::add, list2::add, 0));

System.out.println(list1); // [10, 0, 23, 5, 0, 287, 5, 0]
System.out.println(list2); // [0, -2, 0, 0, -11, 0, 0, -99]
hc2pp10m

hc2pp10m7#

每个解决方案都有其优点和缺点。

  • for循环是显而易见的答案,但您的问题明确提到了Streams API。
  • 使用不同的 predicate a)导致代码重复,B)容易出错,c)导致额外的处理时间- 2N
  • 自定义Collector很难实现,给人的印象是冗余工作,而问题看起来如此简单,甚至幼稚。

我还没有看到其他人提到这一点,但是你可以在Map<Boolean,List<Integer>>Map中收集你的数字,其中key对应于你的分组标准,List是匹配标准的项目的选择,例如:

List<Integer> numbers = List.of(10, -2, 23, 5, -11, 287, 5, -99);
Map<Boolean, List<Integer>> numbersByIsPositive = numbers.stream()
    .collect(Collectors.groupingBy(number -> number >= 0));

List<Integer> positiveNumbers = numbersByIsPositive.get(true);
List<Integer> negativeNumbers = numbersByIsPositive.get(false);

在应用此方法时,请考虑自动装箱和自动取消装箱。
输出:

Positive numbers: [10, 23, 5, 287, 5]
Negative numbers: [-2, -11, -99]
ar5n3qh5

ar5n3qh58#

你可以在适当的地方这样做:

List<Integer> left = Arrays.asList(10, -2, 23, 5, -11, 287, 5, -99);
    int[] right = new int[left.size()];

    IntStream.range(0, left.size())
            .filter(i -> left.get(i) < 0)
            .forEach(x -> {
                right[x] = left.get(x);
                left.set(x, 0);
            });
    System.out.println(left);
    System.out.println(Arrays.toString(right));

这是副作用,但据我所知,这是一个安全的副作用。

bqucvtff

bqucvtff9#

我找到了两种基于条件拆分流的解决方案
您的列表为列表1 = {10,-2,23,5,-11,287,5,-99}

List1.stream().collect(Collectors.partitioningBy(num -> num <0))

这将给予具有以下键值的Map

"true" : 10,24,5,287,5
"false": -2,-11,-99

另一个解决方案位于同一行,但不使用partitioningBy,而是使用groupingBy

List2.stream().collect(Collectors.groupingBy(num -> num <0))

相关问题