迭代时在 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 10:09:25  来源:igfitidea点击:

Modifying Objects within stream in Java8 while iterating

javajava-8java-stream

提问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 Spliteratorreports the CONCURRENTcharacteristic.

对于大多数数据源来说,防止干扰意味着确保数据源在流管道的执行过程中完全不被修改。值得注意的例外是其源是并发集合的流,这些流专门设计用于处理并发修改。并发流源是那些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 ConcurrentModificationExceptionor 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 Collectionlike ArrayListwith 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 removeIfto 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 + "]";
        }



    }