如何使用 Lambda 表达式 .reduce() 方法减少给定列表

Posted

技术标签:

【中文标题】如何使用 Lambda 表达式 .reduce() 方法减少给定列表【英文标题】:How to reduce given list by using Lambda expression .reduce() method 【发布时间】:2015-05-18 07:16:58 【问题描述】:
List<Integer> integers = Arrays.asList(1, 2, 3, 5, 6, 8, 9, 10);
integers.stream().filter((integer) -> integer % 2 == 0).collect(Collectors.toList());

如上图integers是一个List,我们只需要从中过滤偶数。我可以通过使用.filter() 方法来实现。但是,有没有可能使用.reduce() 方法实现相同的目标。希望,.reduce() 方法通过执行给定的 BynaryOperation 过滤掉所有其他元素并返回缩减列表。

如果我对.reduce() 方法的理解不正确,请告诉我这个方法到底是做什么的。

【问题讨论】:

我不明白。你有什么要求?您想减少List 中的所有偶数吗?您可以链接filterreduce,因为filter 是一个中间操作。 是的,以某种方式可以做到这一点,但reduce 是错误的工具!按照您的建议使用filter 我只想在reduce()方法的帮助下从列表中删除所有奇数并返回偶数列表。 @ParameshKorrakuti 当需要螺丝刀时,您正在尝试使用锤子。不要让事情变得更困难和潜在的错误(例如:当你的流并行时)。只需使用filtercollect... 请问您选择reduce 而不是collect 有什么好处?还是只是一种心理锻炼? 【参考方案1】:

你可以使用Stream.reduce(U identity, BiFunction&lt;U,? super T,U&gt; accumulator, BinaryOperator&lt;U&gt; combiner)方法,它接受三个参数:

identity:identity 元素既是归约的初始值,也是流中没有元素时的默认结果。在您的情况下,它将是一个空列表accumulator:accumulator 函数有两个参数:归约的部分结果和流的下一个元素(在本例中为整数)。它对模 2 进行检查,然后返回一个新的部分结果。 combiner:其目的是合并正在并行处理的流批次的内部临时收集器-累加器。

例如:

BinaryOperator<ArrayList<Integer>> combiner = (x, y) ->  x.addAll(y); return x; ;
BiFunction<ArrayList<Integer>, Integer, ArrayList<Integer>> accumulator = (x, y) -> 
    if (y % 2 == 0) 
        x.add(y);
    
    return x;
;
List<Integer> list = Stream.of(1, 2, 3, 5, 6, 8, 9, 10).reduce(new ArrayList<Integer>(),
                                                               accumulator,
                                                               combiner);
System.out.println(list);

请注意,此解决方案可能不适用于并行流。此外,坚持.filter() 方法太容易了,所以我强烈建议您这样做。

【讨论】:

您使用了错误的 reduce 方法,Stream.of(integers) 无论如何都会给您一个带有单个 List 元素的流。不提使用reduce 会导致并行完全混乱......简而言之,我不认为使用reduce 是正确的工具。只要坚持collect(toList())... 试试Stream.of(1, 2, 3, 5, 6, 8, 9, 10).parallel().reduce(...) ;-) 尽管接受了这个答案,但如果管道并行运行,它会给出错误的结果或异常。您可能需要明确说明并对其进行编辑。 您对collect 的使用是正确的,但对于reduce 是不正确的。 @kocko,感谢您的回复。知道reduce() 会做什么。【参考方案2】:

您对还原的理解是错误的。 reduce 将重复对所有元素应用一个函数以获得一个单一的结果

你好像觉得reduce喜欢做

1, 2, 3, 5, 6, 8, 9, 10
│  │  │  │  │  │  │  │
└op┘  └op┘  └op┘  └op┘
  │     │     │     │
    result list

其实是这样的

1, 2, 3, 5, 6, 8, 9, 10
│  │  │  │  │  │  │  │
└op┘  └op┘  └op┘  └op┘
  │    │      │    │
  └─op─┘      └─op─┘
     │          │
     └────op────┘
           │
   final result value

虽然这是一个概念视图,但操作的确切顺序是未指定的。顺序执行类似于(((1 op 2) op 3) op 4)…,而并行执行将混合执行上面的树和部分顺序执行。


如果您首先将每个元素转换为List,然后使用连接每个列表的列表操作,则可以滥用reduce 创建结果列表,但是这样做有两个问题:

它没有提供所需的“跳过(原始列表的)第二个元素”逻辑;如果您查看上面的树,应该会很清楚,不可能制定一个正确的 op 函数来在每个可能的执行场景中执行此操作 创建临时列表并将它们连接起来效率非常低

后一点可以通过使用collect 来解决,这是一个可变缩减,因此,允许您使用可以添加项目的可变列表,但是,它不解决第一点,包括所需的过滤器将违反合同,并且只能在顺序执行中工作。

所以解决方案是为源列表范围内的所有元素定义一个filter,然后使用collect 进行可变归约以创建结果列表,而且,令人惊讶的是,这正是您的原始代码确实:

… .filter(integer -> integer % 2 == 0).collect(Collectors.toList());

【讨论】:

以上是关于如何使用 Lambda 表达式 .reduce() 方法减少给定列表的主要内容,如果未能解决你的问题,请参考以下文章

lambda,map,filter,reduce

lambda 表达式中的返回类型,Reduce 函数

python Python map,reduce,过滤lambda表达式

如何使用 reduce() 计算合并值?

Python中特殊函数和表达式 filter,map,reduce,lambda

Python lambda map filter reduce