Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java8流-收集与减少_Java_Java 8_Java Stream - Fatal编程技术网

Java8流-收集与减少

Java8流-收集与减少,java,java-8,java-stream,Java,Java 8,Java Stream,什么时候使用collect()vsreduce()?有没有人有好的、具体的例子来说明什么时候走这条路或那条路肯定更好 考虑到这是一个可变的缩减,我假设它需要(内部)同步,这反过来会对性能有害。大概reduce()更容易并行化,代价是在reduce中的每一步之后都必须为return创建一个新的数据结构 但是,上面的语句都是猜测,我希望专家在这里插话。reduce是一个“”操作,它对流中的每个元素应用一个二进制运算符,其中运算符的第一个参数是前一个应用程序的返回值,第二个参数是当前流元素 coll

什么时候使用
collect()
vs
reduce()
?有没有人有好的、具体的例子来说明什么时候走这条路或那条路肯定更好

考虑到这是一个可变的缩减,我假设它需要(内部)同步,这反过来会对性能有害。大概
reduce()
更容易并行化,代价是在reduce中的每一步之后都必须为return创建一个新的数据结构

但是,上面的语句都是猜测,我希望专家在这里插话。

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

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

以下给出了采用两种不同方法的原因:

如果我们想要获取字符串流并将它们连接到 单个长字符串,我们可以通过普通缩减实现这一点:

 String concatenated = strings.reduce("", String::concat)  
我们将得到预期的结果,甚至可以并行工作。 然而,我们可能对这次演出不满意!这样的 实现将执行大量字符串复制,并且运行 时间的字符数为O(n^2)。更有表现力的人 方法是将结果累积到StringBuilder中, 它是用于累积字符串的可变容器。我们可以使用 和我们使用普通方法一样,使用同样的技术来并行可变约简 减少


因此,问题是并行化在两种情况下是相同的,但在
reduce
情况下,我们将函数应用于流元素本身。在
collect
情况下,我们将函数应用于可变容器。

正常缩减意味着将两个不可变的值(如int、double等)组合起来,并生成一个新值;这是一个不可变的缩减。相反,collect方法被设计为变异容器,以累积它应该产生的结果

为了说明这个问题,让我们假设您想要使用一个简单的简化实现
Collectors.toList()
,如

List<Integer> numbers = stream.reduce(
        new ArrayList<Integer>(),
        (List<Integer> l, Integer e) -> {
            l.add(e);
            return l;
        },
        (List<Integer> l1, List<Integer> l2) -> {
            l1.addAll(l2);
            return l1;
        });
List number=stream.reduce(
新建ArrayList(),
(列表l,整数e)->{
l、 加(e);
返回l;
},
(列表l1,列表l2)->{
l1.addAll(l2);
返回l1;
});
这相当于收集器.toList()。但是,在这种情况下,您可以对
列表进行变异。正如我们所知,
ArrayList
不是线程安全的,在迭代时从中添加/删除值也不安全,因此您将获得并发异常或
ArrayIndexOutOfBoundsException
或任何类型的异常(尤其是并行运行时)当您更新列表或组合器尝试合并列表时,因为您正在通过向列表中累积(添加)整数对列表进行变异。如果要使此线程安全,每次都需要传递一个新列表,这会影响性能

相反,
Collectors.toList()
的工作方式类似。但是,当您将值累积到列表中时,它可以保证线程安全。从:

使用收集器对此流的元素执行可变缩减操作。如果流是并行的,而收集器是并发的,则 流无序或收集器无序,则 同时进行还原并行执行时,可以实例化、填充和合并多个中间结果,以保持可变数据结构的隔离。因此,即使与非线程安全数据结构(如ArrayList)并行执行,并行缩减不需要额外的同步。

所以要回答你的问题:

什么时候使用
collect()
vs
reduce()


如果您有不可变的值,例如
ints
double
Strings
,那么正常的缩减效果很好。但是,如果必须
将值减少到
列表中(可变数据结构),则需要使用
收集
方法进行可变减少。

它们在运行时的潜在内存占用方面非常不同。当
collect()
收集所有数据并将其放入集合时,
reduce()
明确要求您指定如何减少通过流的数据

例如,如果您想从文件中读取一些数据,对其进行处理,并将其放入某个数据库中,您可能会得到类似以下内容的java流代码:

streamDataFromFile(file)
            .map(data -> processData(data))
            .map(result -> database.save(result))
            .collect(Collectors.toList());
在本例中,我们使用
collect()
强制java通过流式传输数据,并使其将结果保存到数据库中。如果没有
collect()
,数据将永远不会被读取和存储

如果文件大小足够大或堆大小足够小,此代码会生成
java.lang.OutOfMemoryError:java堆空间
运行时错误。显而易见的原因是,它试图将通过流的所有数据(事实上,已经存储在数据库中)堆叠到结果集合中,这会破坏堆

但是,如果将
collect()
替换为
reduce()
,这将不再是一个问题,因为后者将减少并丢弃所有通过的数据

在本例中,只需将
collect()
替换为
reduce

.reduce(0L, (aLong, result) -> aLong, (aLong1, aLong2) -> aLong1);
你没有
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);
}
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);
}
 For example, given a stream of Person, to calculate the longest last name 
 of residents in each city:

    Comparator<String> byLength = Comparator.comparing(String::length);
    Map<String, String> longestLastNameByCity
        = personList.stream().collect(groupingBy(Person::getCity,
            reducing("", Person::getLastName, BinaryOperator.maxBy(byLength))));
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = list.stream().reduce((x,y) -> {
        System.out.println(String.format("x=%d,y=%d",x,y));
        return (x + y);
    }).get();
x=1,y=2
x=3,y=3
x=6,y=4
x=10,y=5
x=15,y=6
x=21,y=7
28