Java 8中的非干扰示例

时间:2018-08-27 06:38:43

标签: java java-8 java-stream

根据this question,我们可以修改源,这不称为干扰:

  

您可以修改流元素本身,不应将其称为“干扰”。

根据this question,代码

List<String> list = new ArrayList<>();
  list.add("test");
  list.forEach(x -> list.add(x));

将抛出ConcurrentModificationException

但是我的代码,

Employee[] arrayOfEmps = {
                new Employee(1, "Jeff Bezos"),
                new Employee(2, "Bill Gates"),
                new Employee(3, "hendry cavilg"),
                new Employee(4, "mark cuban"),
                new Employee(5, "zoe"),
                new Employee(6, "billl clinton"),
                new Employee(7, "ariana") ,
                new Employee(8, "cathre"),
                new Employee(9, "hostile"),
                new Employee(10, "verner"),
            };
        Employee el=new Employee(1, "Jeff Bezos");
        List<Employee> li=Arrays.asList(arrayOfEmps);
        li.stream().map(s->{s.setName("newname");return s;}).forEach(System.out::print);

即使实际上更改了源,也不会抛出ConcurrentModificationException

这段代码

Employee[] arrayOfEmps = {
                new Employee(1, "Jeff Bezos"),
                new Employee(2, "Bill Gates"),
                new Employee(3, "hendry cavilg"),
                new Employee(4, "mark cuban"),
                new Employee(5, "zoe"),
                new Employee(6, "billl clinton"),
                new Employee(7, "ariana") ,
                new Employee(8, "cathre"),
                new Employee(9, "hostile"),
                new Employee(10, "verner"),
            };
        Employee el=new Employee(1, "Jeff Bezos");
        List<Employee> li=Arrays.asList(arrayOfEmps);
        li.stream().map(s->{s.setName("newname");li.add(s);return s;}).limit(10).forEach(System.out::print);

抛出

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(Unknown Source)
    at java.util.AbstractList.add(Unknown Source)
    at java8.Streams.lambda$0(Streams.java:33)
    at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Unknown Source)
    at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)
    at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
    at java.util.stream.ReferencePipeline.forEach(Unknown Source)

因此,我不完全了解允许对源代码进行哪些类型的修改,而哪些则不允许。看到一个示例,该示例会产生干扰,并产生有状态和副作用的流,并正确指示出哪个是非常有帮助的。

6 个答案:

答案 0 :(得分:12)

执行此操作时:

li.stream().map(s->{s.setName("newname");return s;})

您并没有更改列表本身,而是更改了列表中的一个元素;因此它不会触发您期望的ConcurrentModificationException

在最后一个代码段中,您正在使用Array.asList
您必须知道Array.asList在数组(特定的内部ArrayList 类)上仅返回只读包装器,解释了为什么add不是支持的。

实际上,此内部类不会按设计覆盖 AbstractList#add 方法;导致UnsupportedOperationException;仍然不像您期望的那样ConcurrentModificationException

以下是与您的上一片段类似的示例,该示例确实抛出了ConcurrentModificationException

public static void main(String[] args) {
    Employee[] arrayOfEmps = {
      new Employee(1, "Jeff Bezos")
    };
    Employee el = new Employee(11, "Bill Gates");
    List<Employee> li = new ArrayList<>(Arrays.asList(arrayOfEmps)); // to avoid read-only restriction
    li.stream().peek(s -> li.add(el)).forEach(System.out::print);
} 

请注意,我将Arrays.List的返回值包装为“ true” ArrayList,允许编写,因为该实现实现了add方法;)

答案 1 :(得分:5)

您的第一个示例更改了Stream的现有元素,但是没有从源中添加或删除元素。因此,这不是干扰。

您的第二个示例尝试通过在Stream管道中向源添加元素来进行干扰。但是,由于尝试将元素添加到固定大小的UnsupportedOperationException(由ConcurrentModificationException返回)中,因此您得到的是List而不是Arrays.asList

将第二个示例更改为:

List<Employee> li=new ArrayList<>(Arrays.asList(arrayOfEmps));
li.stream().map(s->{s.setName("newname");li.add(s);return s;}).limit(10).forEach(System.out::print);

您应该得到ConcurrentModificationException

答案 2 :(得分:4)

Stream的来源发生非结构性更改时,这称为结构性。例如ArrayList文档说:

  

仅设置元素的值不是结构上的修改...

因此,在您的示例中,这意味着每秒更改Employee本身不会更改List本身(不会删除或添加元素)。但是更改List本身将失败,并显示ConcurrentModificationException

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);

list.stream().forEach(x -> list.remove(x));

但是有些来源的干扰大于OK,称为弱一致性遍历,例如ConcurrentHashMap

 ConcurrentHashMap<Integer, String> chm = new ConcurrentHashMap<>();
 chm.put(1, "one");
 chm.put(2, "two");
 chm.put(3, "three");

 chm.entrySet().stream()
         .forEach(x -> chm.remove(x.getKey()));

这不会失败,ConcurrentModificationException

答案 3 :(得分:2)

您无法更改正在处理的列表的大小。

在第一个示例中,您正在列表中更改Employee对象的值。

第二秒钟,您将在列表中添加一个项目。

那是不同的!

答案 4 :(得分:2)

您的代码正在修改流元素,而不是列表本身:

s->{s.setName("newname")更改流元素中的字段名称,这不会干扰流或其源。

java.lang.UnsupportedOperationException也不相关,这是由于您尝试在固定大小的列表上调用add(这是Arrays.asList的结果)引起的。 每当有人根据add的结果调用removeArrays.asList等时,都会产生不支持的操作异常,因为收集的大小是固定的。

答案 5 :(得分:2)

您的第一个示例不会修改初始列表。

流只是列表上的视图,因此初始列表完全不受的影响。 >

第二个示例显式使用了list.add()

这就是全部。