Поведение потока при изменении источника потока после создания потока - PullRequest
0 голосов
/ 25 сентября 2019

Рассмотрим этот код ниже:

     List<Integer> l=new ArrayList<>();
     l.add(23);l.add(45);l.add(90);

     Stream<Integer> str=l.stream();     // mark A

     l.add(111);
     l=null;

     System.out.println(str.collect(Collectors.toList())); // mark B

ВЫХОД IS:

[23, 45, 90, 111]

Здесь я предполагаю, что когда терминальная операция вызывается на отметке B, тогда оценивается RHS метки A, котораяозначает, что недавний список (с элементом "111") выбирается, но вопрос в том, почему мы не получаем NullPointerException здесь ... если мы не получаем исключение, то мы не должны получать "111" ввывод тоже .. помогите пожалуйста.

Ответы [ 2 ]

4 голосов
/ 25 сентября 2019

Это поведение явно указано в документации :

Для потоковых источников с хорошим поведением источник может быть изменен до начала работы терминала, и эти изменения будут отраженыв покрытых элементах.Например, рассмотрим следующий код:

List<String> l = new ArrayList(Arrays.asList("one", "two"));
Stream<String> sl = l.stream();
l.add("three");
String s = sl.collect(joining(" "));

Сначала создается список, состоящий из двух строк: «one»;и два".Затем создается поток из этого списка.Далее список модифицируется добавлением третьей строки: «три».Наконец элементы потока собираются и объединяются.Поскольку список был изменен до начала операции терминала collect, результатом будет строка «один два три».Все потоки, возвращенные из коллекций JDK и большинства других классов JDK, хорошо себя ведут таким образом;о потоках, сгенерированных другими библиотеками, см. Построение низкоуровневого потока , где указаны требования для построения потоков с хорошим поведением.

1 голос
/ 25 сентября 2019

l.stream() создает Stream, который, вероятно, хранит ссылку на источник List (я говорю, вероятно, потому что это детали реализации).

В то время, когда вы потребляете StreamList уже имеет 4 элемента, поэтому используются 4 элемента.

Изменение ссылки l на null не влияет на ссылку, сохраненную в экземпляре Stream.

если мы не получим исключение, то мы не должны также получать "111" в выводе.

Это было бы верно, только если реализация Streamсоздал копию источника List вместо того, чтобы просто хранить ссылку на оригинал List.Поскольку это было бы расточительно с точки зрения использования памяти, неудивительно, что это не так.

Глядя на реализацию Collection stream() (по крайней мере, в Java 8), я вижу:

default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

spliterator() переопределяется на ArrayList:

public Spliterator<E> spliterator() {
    return new ArrayListSpliterator<>(this, 0, -1, 0);
}

и ArrayListSpliterator сохраняет ссылку на источник List, как и ожидалось:

static final class ArrayListSpliterator<E> implements Spliterator<E> {
    ....
    private final ArrayList<E> list;
    private int index; // current index, modified on advance/split
    private int fence; // -1 until used; then one past last index
    private int expectedModCount; // initialized when fence set

    /** Create new spliterator covering the given  range */
    ArrayListSpliterator(ArrayList<E> list, int origin, int fence,
                         int expectedModCount) {
        this.list = list; // OK if null unless traversed
        this.index = origin;
        this.fence = fence;
        this.expectedModCount = expectedModCount;
    }
    ....
}
...