为什么我们需要在编码时避免突变?什么是突变?

时间:2015-06-05 11:40:22

标签: java lambda java-8 java-stream mutation

为什么第二个代码(带有流的代码)比第一个代码更好?

第一:

public static void main(String [] args) {
   List<Integer> values = Arrays.asList(1,2,3,4,5,6);
   int total = 0;
   for(int e : values) {
       total += e * 2;

   }

第二:

   System.out.println(total);
   System.out.println(
           values.stream()
           .map(e-> e*2)
           .reduce(0, (c, e)-> c + e)); 

3 个答案:

答案 0 :(得分:8)

变异正在改变一个对象,是编程语言中常见的副作用。

具有功能合同的方法将始终将相同的值返回到相同的参数,并且没有其他副作用(如存储文件,打印,读取)。因此,即使你在函数中改变临时值,它仍然是纯粹的外部。通过将第一个示例放在函数中演示它:

public static int squareSum(const List<Integer> values)
{
    int total = 0;
    for(int e : values) {
        total += e * 2;  // mutates a local variable
    }
    return total;
}

纯功能方法甚至不更新局部变量。如果你把第二个版本放在一个函数中它将是纯粹的:

public static int squareSum(const List<Integer> values)
{
    return values.stream()
           .map(e-> e*2)
           .reduce(0, (c, e)-> c + e);
}

对于一个知道其他语言的人来说,长期以来一直偏爱功能样式mapreducelambda非常自然。两个版本都易于阅读和易于测试,这是最重要的部分。

Java有功能类。 java.lang.String就是其中之一。

答案 1 :(得分:1)

Mutation正在改变对象的状态,无论是列表还是某个自定义对象。

您的特定代码不会导致列表突变,因此使用lambdas而不是普通的旧迭代没有实际的好处。并且,责备我,但在这种情况下我会使用迭代方法。

有些方法表示,无论何时需要修改对象/集合,都需要使用修改后的数据返回新的对象/集合,而不是更改原始对象/集合。这对于集合很有用,例如,当您同时访问集合并且正在从另一个线程更改它时。

当然这可能导致内存泄漏,因此有一些算法用于管理内存和收集的可变性,即只有更改的节点存储在内存中的另一个位置。

答案 2 :(得分:1)

虽然Royal Bg是对的,但在任何一种情况下都不会改变你的数据,但第二版没有优势是不对的。第二个版本可以很多多线程而没有歧义。

由于我们不期望迭代列表,我们可以将操作放入一个高度多线程的上下文中并在gpu上解决它。在后者中,集合中的每个数据点乘以2.然后减少(这意味着每个元素被加在一起),这可以通过减少来完成。

后者在前者中没有看到许多潜在的优势。虽然这两个代码元素实际上都没有变异,但在第二个代码元素中,我们得到了非常明确的契约,即项在发生时不能变异。因此,我们知道,如果我们向前,向后迭代列表或将其应用于多线程等,这并不重要。实现细节可以在以后填写。但是,只有当我们知道突变不会发生并且流只是不允许它们时。