Java 8 流 - 收集与减少

2022-08-31 07:13:27

你什么时候会使用 vs ?有没有人有很好的,具体的例子来说明什么时候走一条路或另一条路肯定更好?collect()reduce()

Javadoc 提到 collect() 是一种可变的缩减

鉴于这是一种可变的减少,我认为它需要同步(内部),这反过来又会对性能造成不利影响。据推测,它更容易并行化,但代价是必须在减少的每一步后创建一个新的数据结构来返回。reduce()

然而,上述陈述是猜测,我希望专家在这里插话。


答案 1

reduce是一个“fold”操作,它对流中的每个元素应用二元运算符,其中运算符的第一个参数是前一个应用程序的返回值,第二个参数是当前流元素。

collect是一个聚合操作,其中创建一个“集合”,并将每个元素“添加”到该集合中。然后将流不同部分中的集合相加。

您链接的文档给出了采用两种不同方法的原因:

如果我们想获取一个字符串流并将它们连接成一个长字符串,我们可以通过普通的归约来实现这一点:

 String concatenated = strings.reduce("", String::concat)  

我们将得到预期的结果,它甚至可以并行工作。但是,我们可能不满意性能!这样的实现将执行大量的字符串复制,并且运行时的字符数将为 O(n^2)。一种更高性能的方法是将结果累积到StringBuilder中,StringBuilder是用于累积字符串的可变容器。我们可以使用与普通约简相同的技术来并行化可变约简。

所以关键是两种情况下的并行化是相同的,但是在这种情况下,我们将函数应用于流元素本身。在这种情况下,我们将函数应用于可变容器。reducecollect


答案 2

原因很简单:

  • collect() 只能使用可变结果对象。
  • reduce()旨在处理不可变的结果对象。

"reduce()与不可变“示例

public class Employee {
  private Integer salary;
  public Employee(String aSalary){
    this.salary = new Integer(aSalary);
  }
  public Integer getSalary(){
    return this.salary;
  }
}

@Test
public void testReduceWithImmutable(){
  List<Employee> list = new LinkedList<>();
  list.add(new Employee("1"));
  list.add(new Employee("2"));
  list.add(new Employee("3"));

  Integer sum = list
  .stream()
  .map(Employee::getSalary)
  .reduce(0, (Integer a, Integer b) -> Integer.sum(a, b));

  assertEquals(Integer.valueOf(6), sum);
}

"collect()与可变“示例

例如,如果您想手动计算总和,则无法使用,而只能使用来自。看:collect()BigDecimalMutableIntorg.apache.commons.lang.mutable

public class Employee {
  private MutableInt salary;
  public Employee(String aSalary){
    this.salary = new MutableInt(aSalary);
  }
  public MutableInt getSalary(){
    return this.salary;
  }
}

@Test
public void testCollectWithMutable(){
  List<Employee> list = new LinkedList<>();
  list.add(new Employee("1"));
  list.add(new Employee("2"));

  MutableInt sum = list.stream().collect(
    MutableInt::new, 
    (MutableInt container, Employee employee) -> 
      container.add(employee.getSalary().intValue())
    , 
    MutableInt::add);
  assertEquals(new MutableInt(3), sum);
}

这是有效的,因为累加器不应该返回带有结果的新对象,而是要更改类型可变对象的状态。container.add(employee.getSalary().intValue());containerMutableInt

如果你想改用,你不能使用这个方法,因为它是不可变的。(除此之外,这不会工作,因为没有空的构造函数)BigDecimalcontainercollect()container.add(employee.getSalary());containerBigDecimalBigDecimal::newBigDecimal


推荐