reduce()方法在Java 8中如何工作?

时间:2019-05-03 12:47:50

标签: java java-8 java-stream reduce

我尝试了解中的reduce()方法是如何工作的。

例如,我有以下代码:

public class App {

    public static void main(String[] args) {
        String[] arr = {"lorem", "ipsum", "sit", "amet"};
        List<String> strs = Arrays.asList(arr);

        int ijk = strs.stream().reduce(0, 
            (a, b) -> { 
                System.out.println("Accumulator, a = " + a + ", b = " + b);
                return a + b.length();
            },
            (a, b) -> {
                System.out.println("Combiner");
                return a * b;
            });
        System.out.println(ijk); 
    }
}

输出为:

Accumulator, a = 0, b = lorem
Accumulator, a = 5, b = ipsum
Accumulator, a = 10, b = sit
Accumulator, a = 13, b = amet
17

是这些字符串长度的总和。而且我看到未访问组合器,因此它不会与数字相乘,只会将数字相加。

但是,如果我有这些信息流:

int ijk = strs.parallelStream().reduce(0, 
    (a, b) -> { 
        System.out.println("Accumulator, a = " + a + ", b = " + b);
        return a + b.length();
    },
    (a, b) -> {
        System.out.println("Combiner");
        return a * b;
    });

System.out.println(ijk); 

这是输出:

Accumulator, a = 0, b = ipsum
Accumulator, a = 0, b = lorem
Accumulator, a = 0, b = sit
Combiner
Accumulator, a = 0, b = amet
Combiner
Combiner
300

我看到累加器和合并器都被访问了,但是只返回了乘法。那么总和会怎样?

4 个答案:

答案 0 :(得分:16)

您应该阅读reduce的说明文件:

  

此外,组合器功能必须与累加器功能兼容;对于所有u和t,必须满足以下条件:

     

combiner.apply(u,accumulator.apply(identity,t))== accumulator.apply(u,t)

在您的情况下,您正在违反该定律(在accumulator中执行 sum ,在combiner中进行乘法),因此结果您会发现这种操作实际上是未定义的,并且取决于基础源的Spliterator的实现方式(不要这样做!)。

此外,combiner仅被 称为并行流。

当然,您的整个方法可以简化为:

Arrays.asList("lorem", "ipsum", "sit", "amet")
      .stream()
      .mapToInt(String::length)
      .sum();

如果您只是出于学习目的而这样做,那么正确的reduce将是(获取sum):

strs.parallelStream()
    .reduce(0,
            (a, b) -> {
                  System.out.println("Accumulator, a = " + a + ", b = " + b);
                  return a + b.length();
            },
            (a, b) -> {
                  System.out.println("Combiner");
                  return a + b;
            });

答案 1 :(得分:8)

关键概念:身份,累加器和组合器

Stream.reduce()操作:将操作的参与者元素分解为单独的块。这样,我们将更容易理解每​​个人扮演的角色

  • 身份 –一个元素,它是归约运算的初始值,如果流为空,则为默认结果
  • itemAccumulator –一个带有两个参数的函数:归约运算的部分结果和流的下一个元素
  • Combiner –具有两个参数的函数:归约运算的部分结果和流的下一个元素 合并器– 用于归约并行化时,或者当累加器参数的类型与累加器实现的类型不匹配时用于归约操作的部分结果的函数
  

当流并行执行时,Java运行时将流拆分为多个子流。在这种情况下,我们需要使用一种函数将子流的结果合并为一个。这就是组合器的作用

案例1:如您的示例所示,合并器可与parallelStream一起使用

案例2:具有不同类型参数的示例累加器

在这种情况下,我们有一个User对象流,累加器参数的类型为Integer和User。但是,累加器实现是整数的总和,因此编译器无法推断用户参数的类型。

List<User> users = Arrays.asList(new User("John", 30), new User("Julie", 35));
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge());

编译错误

The method reduce(User, BinaryOperator<User>) in the type Stream<User> is not applicable for the arguments (int, (<no type> partialAgeResult, <no type> user) -> {})

我们可以使用Combiner(方法引用Integer::sum或lambda表达式(a,b)->a+b

来解决此问题。
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(),Integer::sum);

简单地说,如果我们使用顺序流,并且累加器参数的类型及其实现的类型匹配,则无需使用组合器。

答案 2 :(得分:8)

有3种减少使用的方法。简而言之,for it in tablewidget.selectedItems(): print(it.row(), it.column()) 以两个后续项(或与第一项相同的标识值)开始,并执行操作以使它们产生新的折减值。对于每个下一项,都会发生相同的情况,并以减小的值执行操作。

假设您有from PyQt5 import QtCore, QtGui, QtWidgets class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.table = QtWidgets.QTableWidget(6, 6) self.setCentralWidget(self.table) self.table.selectionModel().selectionChanged.connect( self.on_selectionChanged ) @QtCore.pyqtSlot(QtCore.QItemSelection, QtCore.QItemSelection) def on_selectionChanged(self, selected, deselected): print("=====Selected=====") for ix in selected.indexes(): print(ix.row(), ix.column()) print("=====Deselected=====") for ix in deselected.indexes(): print(ix.row(), ix.column()) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) w = MainWindow() w.show() sys.exit(app.exec_()) Stream::reduce'a''b'的信息流。减少执行以下操作序列:

  1. 'c'-'d'可以是任何东西(输入长度​​的总和。)
  2. result = operationOn('a', 'b')
  3. operationOn
  4. result = operationOn(result, 'c')

方法是:

答案 3 :(得分:1)

我假设您选择像演示一样进行加法和乘法以查看实际发生的情况。

正如您已经注意到的那样,正如已经提到的,仅在并行流上调用组合器。

简而言之,在并行strams上,一部分流(分别是基础的分隔符)被截断并由其他线程处理。处理了几个部分后,将它们的结果与组合器合并。

在您的情况下,所有四个元素都由不同的线程处理,然后按元素进行组合。这就是为什么您看不到任何加法(除0 +之外)而只应用乘法的原因。

但是,为了获得有意义的结果,您应该从*切换到+,而进行更有意义的输出。