jim*_*002 129 java java-8 java-stream
你什么时候使用collect()vs reduce()?有没有人有一个好的,具体的例子,说明哪种方式更好?
鉴于它是可变的减少,我认为它需要同步(内部),这反过来可能对性能有害.据推测reduce(),更容易并行化,代价是必须在reduce中的每个步骤之后创建一个新的数据结构.
上面的陈述是猜测,但我喜欢这里的专家.
Bor*_*der 105
reduce它是一个" 折叠 "操作,它将二元运算符应用于流中的每个元素,其中运算符的第一个参数是前一个应用程序的返回值,第二个参数是当前流元素.
collection是一个聚合操作,其中创建"集合"并将每个元素"添加"到该集合.然后将流的不同部分中的集合加在一起.
如果我们想要获取字符串流并将它们连接成一个长字符串,我们可以通过普通减少来实现:
Run Code Online (Sandbox Code Playgroud)String concatenated = strings.reduce("", String::concat)我们会得到理想的结果,甚至可以并行工作.但是,我们可能对性能不满意!这样的实现将进行大量的字符串复制,并且运行时间将是字符数的O(n ^ 2).更高效的方法是将结果累积到StringBuilder中,StringBuilder是用于累积字符串的可变容器.我们可以使用相同的技术来并行化可变缩减,就像我们使用普通缩减一样.
所以重点是两种情况下的并行化是相同的,但在这种reduce情况下我们将函数应用于流元素本身.在collect我们将函数应用于可变容器的情况下.
San*_*dro 37
原因很简单:
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);
}
Run Code Online (Sandbox Code Playgroud)
collect()与可变"的例子例如,如果你想使用手动计算总和collect()不能一起工作BigDecimal,但只有MutableInt从org.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);
}
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为累加器 container.add(employee.getSalary().intValue());不应该返回带有结果的新对象,而是改变container类型的可变状态MutableInt.
如果你想使用BigDecimal而不是container你不能使用该collect()方法,因为它container.add(employee.getSalary());不会改变,container因为BigDecimal它是不可变的.(除此之外BigDecimal::new不起作用,因为BigDecimal没有空的构造函数)
geo*_*rge 24
正常的缩减是为了组合两个不可变的值,如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; });
Run Code Online (Sandbox Code Playgroud)
这相当于Collectors.toList().但是,在这种情况下你改变了List<Integer>.我们知道这ArrayList不是线程安全的,在迭代时添加/删除它的安全性也是不安全的,因此当你更新列表或者更新列表时,你将获得并发异常或arrayIndexOutBound异常或任何类型的异常(特别是当并行运行时)组合器尝试合并列表,因为您通过累积(添加)整数来改变列表.如果你想使这个线程安全,你需要每次都传递一个新列表,这会影响性能.
相比之下,Collectors.toList()作品以类似的方式.但是,当您将值累积到列表中时,它可以保证线程安全.从collect方法的文档:
使用收集器对此流的元素执行可变减少操作.如果流是并行的,并且收集器是并发的,并且流是无序的或收集器是无序的,则将执行并发减少.当并行执行时,可以实例化,填充和合并多个中间结果,以便保持可变数据结构的隔离. 因此,即使与非线程安全数据结构(例如ArrayList)并行执行,也不需要额外的同步来进行并行缩减. 链接
所以回答你的问题:
你什么时候使用
collect()vsreduce()?
如果你有不可变的值,例如ints,doubles,Strings再进行正常降噪工程就好了.但是,如果你必须将reduce你的值称为List(可变数据结构),那么你需要使用该collect方法进行可变缩减.
小智 8
让流为< - b < - c < - d
在减少,
你会有((a#b)#c)#d
其中#是你想做的有趣的操作.
在收集中,
你的收藏家会有某种收集结构K.
K消耗了一个.然后K消耗b.然后K消耗c.K然后消耗d.
最后,你问K最终结果是什么.
K然后给你.
| 归档时间: |
|
| 查看次数: |
75007 次 |
| 最近记录: |