java 为什么我们需要在编码时避免突变?什么是突变?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30665622/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why do we need to avoid mutations while coding? What is a mutation?
提问by codeme
Why is the second code (the one with the stream) a better solution than the first?
为什么第二个代码(带有流的代码)比第一个更好?
First :
第一的 :
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;
}
Second :
第二 :
System.out.println(total);
System.out.println(
values.stream()
.map(e-> e*2)
.reduce(0, (c, e)-> c + e));
回答by Sylwester
Mutation is changing an object and is one common side effect in programming languages.
变异正在改变一个对象,是编程语言中一种常见的副作用。
A method that has a functional contractwill always return the same value to the same arguments and have no other side effects (like storing file, printing, reading). Thus even if you mutate temporary values inside your function it's still pure from the outside. By putting your first example in a function demonstrates it:
具有功能契约的方法将始终为相同的参数返回相同的值,并且没有其他副作用(如存储文件、打印、读取)。因此,即使您在函数内部改变临时值,它在外部仍然是纯的。通过将你的第一个例子放在一个函数中来演示它:
public static int squareSum(const List<Integer> values)
{
int total = 0;
for(int e : values) {
total += e * 2; // mutates a local variable
}
return total;
}
A purely functional method doesn't even update local variables. If you put the second version in a function it would be pure:
纯函数式方法甚至不更新局部变量。如果将第二个版本放在函数中,它将是纯函数:
public static int squareSum(const List<Integer> values)
{
return values.stream()
.map(e-> e*2)
.reduce(0, (c, e)-> c + e);
}
For a person that knows other languages that has long been preferring a functional style map
and reduce
with lambda
is very natural. Both versions are easy to read and easy to test, which is the most important part.
对于一个人说知道,长期以来一直倾向于多功能风格等语言map
,并reduce
用lambda
是很自然的。这两个版本都易于阅读和易于测试,这是最重要的部分。
Java has functional classes. java.lang.String
is one of them.
Java 有函数式类。java.lang.String
是其中之一。
回答by Royal Bg
Mutation is changing the state of an object, either the list or some custom object.
变异正在改变对象的状态,无论是列表还是某个自定义对象。
Your particular code does not cause a mutation of the list either way, so there's no practical benefit here of using lambdas instead of plain old iteration. And, blame me, but I would use the iteration approach in this case.
无论哪种方式,您的特定代码都不会导致列表发生变化,因此使用 lambdas 而不是普通的旧迭代在这里没有实际好处。而且,怪我,但在这种情况下我会使用迭代方法。
Some approaches say that whenever you need to modify an object/collection, you need to return a new object/collection with the modified data instead of changing the original one. This is good for collection for example when you concurrently access a collection and it's being changed from another thread.
有些方法说,每当您需要修改对象/集合时,您都需要返回一个带有修改后数据的新对象/集合,而不是更改原始对象/集合。这对集合很有用,例如当您同时访问一个集合并且它正在从另一个线程更改时。
Of course this could lead to memory leaks, so there are some algorithms for managing memory and mutability for collection i.e. only the changed nodes are stored in another place in memory.
当然,这可能导致内存泄漏,因此有一些算法用于管理内存和收集的可变性,即只有更改的节点存储在内存中的另一个位置。
回答by Tatarize
While Royal Bg is right you're not mutating your data in either case, it's not true that there's no advantage to the second version. The second version can be heavily multithreaded without ambiguity.
虽然 Royal Bg 是对的,但在任何一种情况下都不会改变您的数据,但第二个版本没有优势并不是真的。第二个版本可以毫无歧义地进行大量多线程处理。
Since we're not expecting to iterate the list we can put the operations into a heavily multi-threaded context and solve it on a gpu. In the latter one each data point in the collection is multiplied by 2. Then reduced (which means every element is added together), which can be done by a reduction.
由于我们不期望迭代列表,因此我们可以将操作放入一个多线程的上下文中并在 gpu 上解决它。在后者中,集合中的每个数据点都乘以 2。然后减少(这意味着每个元素都加在一起),这可以通过减少来完成。
There are a number of potential advantages to the latter code not seen in the former. And while neither code element actually mutates, in the second one we are given the very clear contract that the items cannotmutate while that is happening. So we know that it doesn't matter if we iterate the list forwards, backwards, or apply it multithreaded etc. The implementation details can be filled in later. But, only if we know mutation can't happen and streams simply don't allow them.
后者的代码有许多在前者中没有的潜在优势。虽然两个代码元素实际上都没有发生变异,但在第二个元素中,我们得到了非常明确的约定,即项目在发生这种情况时不能发生变异。所以我们知道,向前、向后迭代列表,或者多线程应用等等都没有关系。实现细节可以稍后填写。但是,只有当我们知道突变不可能发生并且流根本不允许它们时。