【问题标题】:Why do we need to avoid mutations while coding? What is a mutation?为什么我们需要在编码时避免突变?什么是突变?
【发布时间】:2015-08-20 08:24:40
【问题描述】:

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

第一:

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)); 

【问题讨论】:

  • 对我来说第一个更容易理解。
  • “为什么代码的第二部分(带有流)比第一部分更好?” 根据 whom?
  • 似乎 lambda 表达式开始让人们感到困惑
  • 对于经验丰富的 C++ 程序员来说,lambda 是新的,因此不熟悉。当然,如果您已经使用 C++ 编程了一段时间,那么第一个更容易。你习惯了第一个胡言乱语,而不是第二个。如果您一直在编写一些函数式语言,尽管第二个语言也很容易阅读,因为 mapreduce (fold) 是主场。
  • “功能”版本无法比较,因为您已将乘法和加法拆分为单独的步骤,而“命令式”则在一个步骤中完成。此外,您使用不明确的变量c 而不是total。功能版本应该是:values.stream().reduce(0, (total, e)-&gt; total + e*2))

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


【解决方案1】:

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

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

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 就是其中之一。

【讨论】:

  • 如何保证操作的顺序?
【解决方案2】:

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

无论哪种方式,您的特定代码都不会导致列表发生突变,因此在这里使用 lambdas 而不是普通的旧迭代没有实际好处。而且,怪我,但在这种情况下我会使用迭代方法。

有些方法说,每当您需要修改对象/集合时,您需要返回一个包含修改后数据的新对象/集合,而不是更改原始数据。这对于集合很有用,例如当您同时访问一个集合并且它正在从另一个线程进行更改时。

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

【讨论】:

    【解决方案3】:

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

    由于我们不希望迭代列表,我们可以将操作放入大量多线程的上下文中并在 gpu 上解决它。在后者中,集合中的每个数据点都乘以 2。然后归约(这意味着每个元素都相加),这可以通过归约来完成。

    后一种代码有许多前一种代码所没有的潜在优势。虽然两个代码元素实际上都没有发生变异,但在第二个代码元素中,我们得到了非常明确的合同,即在发生这种情况时项目不能发生变异。所以我们知道,无论是向前、向后迭代列表,还是多线程应用它等等,都没有关系。实现细节可以稍后填写。但是,只有当我们知道突变不会发生并且流根本不允许它们发生时。

    【讨论】:

      猜你喜欢
      • 2014-08-03
      • 2017-05-05
      • 2017-04-28
      • 2014-06-30
      • 2011-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多