迭代时在 Java8 中修改流中的对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30778017/
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
Modifying Objects within stream in Java8 while iterating
提问by Tejas Gokhale
In Java8 streams, am I allowed to modify/update objects within?
For eg. List<User> users
:
在 Java8 流中,我可以修改/更新其中的对象吗?例如。List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
回答by Pshemo
Yes, you can modify state of objects inside your stream, but most often you should avoid modifying state of sourceof stream. From non-interferencesection of stream package documentation we can read that:
是的,您可以修改流中对象的状态,但大多数情况下您应该避免修改流源的状态。从流包文档的非干扰部分我们可以读到:
For most data sources, preventing interference means ensuring that the data source is not modified at allduring the execution of the stream pipeline. The notable exception to this are streams whose sources are concurrent collections, which are specifically designed to handle concurrent modification. Concurrent stream sources are those whose
Spliterator
reports theCONCURRENT
characteristic.
对于大多数数据源来说,防止干扰意味着确保数据源在流管道的执行过程中完全不被修改。值得注意的例外是其源是并发集合的流,这些流专门设计用于处理并发修改。并发流源是那些
Spliterator
报告CONCURRENT
特性的源。
So this is OK
所以这没问题
List<User> users = getUsers();
users.stream().forEach(u -> u.setProperty(value));
but this in most cases is not
但这在大多数情况下不是
users.stream().forEach(u -> users.remove(u));
and may throw ConcurrentModificationException
or even other unexpected exceptions like NPE:
并且可能会抛出ConcurrentModificationException
甚至其他意外的异常,例如 NPE:
List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());
list.stream()
.filter(i -> i > 5)
.forEach(i -> list.remove(i)); //throws NullPointerException
回答by MNZ
To do structural modification on the source of the stream, as Pshemo mentioned in his answer, one solution is to create a new instance of a Collection
like ArrayList
with the items inside your primary list; iterate over the new list, and do the operations on the primary list.
要对流的源进行结构修改,正如 Pshemo 在他的回答中提到的,一种解决方案是使用主列表中的项目创建一个新的Collection
类似实例ArrayList
;迭代新列表,并在主列表上执行操作。
new ArrayList<>(users).stream().forEach(u -> users.remove(u));
回答by Krupali_wadekar
To get rid from ConcurrentModificationExceptionUse CopyOnWriteArrayList
要摆脱ConcurrentModificationException使用CopyOnWriteArrayList
回答by Andreas M. Oberheim
The functional way would imho be:
恕我直言,功能方式是:
import static java.util.stream.Collectors.toList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class PredicateTestRun {
public static void main(String[] args) {
List<String> lines = Arrays.asList("a", "b", "c");
System.out.println(lines); // [a, b, c]
Predicate<? super String> predicate = value -> "b".equals(value);
lines = lines.stream().filter(predicate.negate()).collect(toList());
System.out.println(lines); // [a, c]
}
}
In this solution the original list is not modified, but should contain your expected result in a new list that is accessible under the same variable as the old one
在这个解决方案中,原始列表没有被修改,但应该在一个新列表中包含您的预期结果,该列表可在与旧列表相同的变量下访问
回答by Nicolas Zozol
Instead of creating strange things, you can just filter()
and then map()
your result.
而不是创造奇怪的东西,你可以只是filter()
然后map()
你的结果。
This is much more readable and sure. Streams will make it in only one loop.
这更具可读性和确定性。Streams 只会在一个循环中完成。
回答by Vadim
As it was mentioned before - you can't modify original list, but you can stream, modify and collect items into new list. Here is simple example how to modify string element.
正如之前提到的 - 您不能修改原始列表,但您可以将项目流式传输、修改和收集到新列表中。这是如何修改字符串元素的简单示例。
public class StreamTest {
@Test
public void replaceInsideStream() {
List<String> list = Arrays.asList("test1", "test2_attr", "test3");
List<String> output = list.stream().map(value -> value.replace("_attr", "")).collect(Collectors.toList());
System.out.println("Output: " + output); // Output: [test1, test2, test3]
}
}
回答by JoB?N
You can make use of the removeIf
to remove data from a list conditionally.
您可以使用removeIf
有条件地从列表中删除数据。
Eg:- If you want to remove all even numbers from a list, you can do it as follows.
例如:- 如果您想从列表中删除所有偶数,您可以按如下方式进行。
final List<Integer> list = IntStream.range(1,100).boxed().collect(Collectors.toList());
list.removeIf(number -> number % 2 == 0);
回答by Joey587
This might be a little late. But here is one of the usage. This to get the count of the number of files.
这可能有点晚了。但这是其中一种用法。这是为了获取文件数的计数。
Create a pointer to memory (a new obj in this case) and have the property of the object modified. Java 8 stream doesn't allow to modify the pointer itself and hence if you declare just count as a variable and try to increment within the stream it will never work and throw a compiler exception in the first place
创建一个指向内存的指针(在这种情况下是一个新的 obj)并修改对象的属性。Java 8 流不允许修改指针本身,因此如果您仅将 count 声明为变量并尝试在流中递增,它将永远不会工作并首先抛出编译器异常
Path path = Paths.get("/Users/XXXX/static/test.txt");
Count c = new Count();
c.setCount(0);
Files.lines(path).forEach(item -> {
c.setCount(c.getCount()+1);
System.out.println(item);});
System.out.println("line count,"+c);
public static class Count{
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "Count [count=" + count + "]";
}
}