Spliterator на модифицированной базовой коллекции - PullRequest
0 голосов
/ 28 октября 2019

Я знаю, что это никогда не должно происходить в производстве, но я пытаюсь понять некоторые запутанные детали о Сплитераторах и натолкнулся на следующую «загадку» (по крайней мере, загадку для меня):

(Фрагмент1)

List<Integer> list = new ArrayList<>() {{ add(1); add(2); add(3); add(4); }};
Spliterator<Integer> spl1 = list.spliterator();
list.add(5);
list.add(6);
Spliterator<Integer> spl2 = s1.trySplit();
s1.forEachRemaining(System.out::print);
s2.forEachRemaining(System.out::print);

Этот код печатает 456123, как и ожидалось ( кашель Я уже ожидал ConcurrentModificationException, но я понимаю поведение кашель ),то есть, он создает Spliterator в списке, который будет разделен, если в списке будет 6 элементов и т. д. Пока все хорошо.

Что я не понимаю, так это следующее:

(фрагмент 2)

List<Integer> list = new ArrayList<>() {{ add(1); add(2); add(3); add(4); }};
Spliterator<Integer> spl1 = list.spliterator();
Spliterator<Integer> spl2 = s1.trySplit();
list.add(5);
list.add(6);
s1.forEachRemaining(System.out::print);
s2.forEachRemaining(System.out::print);

Я ожидаю, что этот код даст сбой, и это произойдет, с ConcurrentModificationException в строке для s1.forEachRemaining, однако будет print 34 на выход тоже. Если его изменить на System.err::println, то увидим, что значения 3 и 4 находятся в этом соответствующем порядке и помещены в PrintStream до Исключения.

Теперь сумасшедшийpart:

(фрагмент 3)

List<Integer> list = new ArrayList<>() {{ add(1); add(2); add(3); add(4); }};
Spliterator<Integer> spl1 = list.spliterator();
Spliterator<Integer> spl2 = s1.trySplit();
list.add(5);
list.add(6);
s2.forEachRemaining(System.out::print);
s1.forEachRemaining(System.out::print);

Обратите внимание, что единственное изменение между фрагментами 2 и 3 - это порядок, в котором мы получаем доступ к s1 и s2. Фрагмент 3 по-прежнему завершается с ошибкой ConcurrentModificationException, но печатаются значения 1 и 2. Это потому, что теперь исключение происходит в строке для s2.forEachRemaining!

Если я правильно понимаю, что происходит:

  • Spliterator инициализируется
  • разделение выполняется
  • итерация
    • в течение итерация, в которой произошла модификация базовой коллекции, после последний сплит был сделан

Означает ли это, что Spliterator также "ленив", как потоки? Тем не менее, этот аргумент на самом деле не работает, когда экспериментирует с несколькими разбиениями, то есть

(фрагмент 4)

List<Integer> list = new ArrayList<>() {{ add(1); add(2); add(3); add(4); add(5); add(6); add(7); add(8); add(9); add(10); }};
Spliterator<Integer> s1 = list.spliterator();
Spliterator<Integer> s2 = s1.trySplit();
list.add(11);
list.add(12);
Spliterator<Integer> s3 = s2.trySplit();
s1.forEachRemaining(s -> System.err.println("1 " + s));
s2.forEachRemaining(s -> System.err.println("2 " + s));
s3.forEachRemaining(s -> System.err.println("3 " + s));

Затем следует без проблем оценить s1 и выдать исключениево время обработки s2, но уже выдает и исключение во время обработки s1!

Любая помощь или указатели приветствуются.

Детали: я запускаю сниппеты на AdoptOpenJDK 11.0.4 + 11 (64-разрядная версия) в Windows в Eclipse 2019-06 (4.12.0), если это имеет значение.

...