Интерфейс Spliterator<T>
- Параметры типа:
-
T- тип элементов, возвращаемых этим Spliterator
- Все известные подинтерфейсы:
- Spliterator.OfDouble, Spliterator.OfInt, Spliterator.OfLong, Spliterator.OfPrimitive<T,T_CONS,T_SPLITR>
- Все известные реализующие классы:
- Spliterators.AbstractDoubleSpliterator, Spliterators.AbstractIntSpliterator, Spliterators.AbstractLongSpliterator, Spliterators.AbstractSpliterator
public interface Spliterator<T>
Объект для обхода и разбиения элементов источника. Источником элементов, покрываемых Spliterator, может быть, например, массив, Collection, канал ввода-вывода или функция генерации.
Spliterator может обходить элементы по одному (tryAdvance()) или последовательно в блоках (forEachRemaining()).
Spliterator также может разбить некоторые свои элементы (используя trySplit()) в другой Spliterator, который может использоваться в потенциально параллельных операциях. Операции с Spliterator, который не может разделиться или делает это очень несбалансированно или неэффективно, вряд ли будут извлекать выгоду из параллелизма. Обход и разделение исчерпывают элементы; каждый Spliterator полезен только для одного объёмного вычисления.
Spliterator также сообщает набор characteristics() своей структуры, источника и элементов из ORDERED, DISTINCT, SORTED, SIZED, NONNULL, IMMUTABLE, CONCURRENT и SUBSIZED. Эти данные могут быть использованы клиентами Spliterator для управления, специализации или упрощения вычислений. Например, Spliterator для Collection сообщит SIZED, Spliterator для Set сообщит DISTINCT, а Spliterator для SortedSet также сообщит SORTED. Характеристики сообщаются как простой объединённый набор битов. Некоторые характеристики дополнительно ограничивают поведение метода; например, если ORDERED, методы обхода должны соответствовать документированному порядку. В будущем могут быть определены новые характеристики, поэтому разработчики не должны присваивать значения неописанным значениям.
Ожидается, что Spliterator, который не сообщает IMMUTABLE или CONCURRENT, будет иметь документированную политику относительно того, когда Spliterator связывается с источником элементов, и обнаружения структурных вмешательств в источник элементов, обнаруженных после связывания. Позднесвязываемый Spliterator связывается с источником элементов в момент первого обхода, первого разделения или первого запроса оценки размера, а не в момент создания Spliterator. Spliterator, который не является позднесвязываемым, связывается с источником элементов в момент создания или первого вызова любого метода. Изменения, внесённые в источник до связывания, отражаются при обходе Spliterator. После связывания Spliterator должен по возможности выбрасывать ConcurrentModificationException, если обнаружено структурное вмешательство. Такие Spliterators называются быстродействующими. Метод объёмного обхода (forEachRemaining()) Spliterator может оптимизировать обход и проверить структурные вмешательства после обхода всех элементов, а не проверять каждый элемент и немедленно завершать.
Spliterator может предоставить оценку количества оставшихся элементов через метод estimateSize(). В идеале, как отражено в характеристике SIZED, это значение точно соответствует количеству элементов, которые будут встречены при успешном обходе. Однако, даже когда оно не известно точно, оценочное значение может быть полезным для операций, выполняемых над источником, например, для определения того, предпочтительнее ли дальнейшее разделение или последовательное обход оставшихся элементов.
Несмотря на очевидную полезность в параллельных алгоритмах, Spliterators не ожидаются быть потокобезопасными; вместо этого реализации параллельных алгоритмов, использующие Spliterators, должны гарантировать, что Spliterator используется только одной нитью за раз. Это, как правило, легко достигается с помощью сериального потокового ограничения, что часто является естественным следствием типичных параллельных алгоритмов, которые работают путём рекурсивного разложения. Нить, вызывающая trySplit(), может передать возвращённый Spliterator другой нити, которая, в свою очередь, может обходить или дополнительно разделять этот Spliterator. Поведение разделения и обхода не определено, если две или более нити одновременно работают с одним Spliterator. Если исходная нить передаёт Spliterator другой нити для обработки, лучше, если эта передача произойдёт до потребления любых элементов с помощью tryAdvance(), так как определённые гарантии (такие как точность estimateSize() для SIZED Spliterators) действительны только до начала обхода.
Предоставляются специализированные подтипы Spliterators для int, long и double значений. Подтипы по умолчанию реализуют tryAdvance(java.util.function.Consumer) и forEachRemaining(java.util.function.Consumer) для упаковки примитивных значений в экземпляры соответствующего обертывающего класса. Такое преобразование может подорвать любые преимущества производительности, полученные при использовании примитивных специализаций. Для избежания упаковки следует использовать соответствующие примитивные методы. Например, Spliterator.OfInt.tryAdvance(java.util.function.IntConsumer) и Spliterator.OfInt.forEachRemaining(java.util.function.IntConsumer) следует использовать вместо Spliterator.OfInt.tryAdvance(java.util.function.Consumer) и Spliterator.OfInt.forEachRemaining(java.util.function.Consumer). Обход примитивных значений с помощью методов упаковки tryAdvance() и forEachRemaining() не влияет на порядок, в котором значения, преобразованные в упакованные значения, встречаются.
- Примечание к API:
-
Spliterators, подобно
Iterators, предназначены для обхода элементов источника. APISpliteratorбыл разработан для поддержки эффективного параллельного обхода в дополнение к последовательному обходу, поддерживая разложение, а также итерацию по одному элементу. Кроме того, протокол доступа к элементам через Spliterator предназначен для наложения меньшей накладных расходов на элемент, чемIterator, и для избежания присущей гонки, возникающей при наличии отдельных методов дляhasNext()иnext().Для изменяемых источников произвольное и недетерминированное поведение может произойти, если источник подвергнется структурному вмешательству (добавление, замена или удаление элементов) между временем связывания Spliterator с его источником данных и концом обхода. Например, такое вмешательство приведёт к произвольным и недетерминированным результатам при использовании фреймворка
java.util.stream.Структурное вмешательство в источник можно управлять следующими способами (в приблизительном порядке убывания желательности):
- В источник невозможно внести структурные изменения.
Например, экземплярCopyOnWriteArrayListявляется неизменяемым источником. Spliterator, созданный из источника, сообщает характеристикуIMMUTABLE. - Источник управляет одновременными модификациями.
Например, набор ключейConcurrentHashMapявляется конкуретным источником. Spliterator, созданный из источника, сообщает характеристикуCONCURRENT. - Изменяемый источник предоставляет Spliterator с поздним связыванием и быстрым отказом.
Позднее связывание сужает окно, в течение которого вмешательство может повлиять на вычисление; быстрый отказ обнаруживает, по возможности, что структурное вмешательство произошло после начала обхода и выбрасываетConcurrentModificationException. Например,ArrayListи многие другие неконкурентныеCollectionклассы в JDK предоставляют Spliterator с поздним связыванием и быстрым отказом. - Изменяемый источник предоставляет Spliterator с не-поздним связыванием, но быстрым отказом.
Источник увеличивает вероятность выбросаConcurrentModificationExceptionпоскольку окно потенциальных вмешательств больше. - Изменяемый источник предоставляет Spliterator с поздним связыванием и не-быстрым отказом.
Источник рискует произвольным недетерминированным поведением после начала обхода, поскольку вмешательство не обнаруживается. - Изменяемый источник предоставляет Spliterator с не-поздним связыванием и не-быстрым отказом.
Источник увеличивает риск произвольного недетерминированного поведения, поскольку не обнаруженное вмешательство может произойти после создания.
Пример. Вот класс (не очень полезный, кроме как для иллюстрации), который поддерживает массив, в котором фактические данные хранятся в чётных позициях, а несвязанные данные тега хранятся в нечётных позициях. Его Spliterator игнорирует теги.
class TaggedArray<T> { private final Object[] elements; // immutable after construction TaggedArray(T[] data, Object[] tags) { int size = data.length; if (tags.length != size) throw new IllegalArgumentException(); this.elements = new Object[2 * size]; for (int i = 0, j = 0; i < size; ++i) { elements[j++] = data[i]; elements[j++] = tags[i]; } } public Spliterator<T> spliterator() { return new TaggedArraySpliterator<>(elements, 0, elements.length); } static class TaggedArraySpliterator<T> implements Spliterator<T> { private final Object[] array; private int origin; // current index, advanced on split or traversal private final int fence; // one past the greatest index TaggedArraySpliterator(Object[] array, int origin, int fence) { this.array = array; this.origin = origin; this.fence = fence; } public void forEachRemaining(Consumer<? super T> action) { for (; origin < fence; origin += 2) action.accept((T) array[origin]); } public boolean tryAdvance(Consumer<? super T> action) { if (origin < fence) { action.accept((T) array[origin]); origin += 2; return true; } else // cannot advance return false; } public Spliterator<T> trySplit() { int lo = origin; // divide range in half int mid = ((lo + fence) >>> 1) & ~1; // force midpoint to be even if (lo < mid) { // split out left half origin = mid; // reset this Spliterator's origin return new TaggedArraySpliterator<>(array, lo, mid); } else // too small to split return null; } public long estimateSize() { return (long)((fence - origin) / 2); } public int characteristics() { return ORDERED | SIZED | IMMUTABLE | SUBSIZED; } } }В качестве примера того, как фреймворк параллельных вычислений, такой как пакет
java.util.stream, будет использовать Spliterator в параллельном вычислении, вот один способ реализации связанного параллельного forEach, который иллюстрирует основной способ использования – разделение подзадач до тех пор, пока объём работы не станет достаточно малым, чтобы выполнение было последовательным. Здесь мы предполагаем, что порядок обработки в подзадачах не имеет значения; разные (разветвлённые) задачи могут дополнительно разделяться и обрабатывать элементы конкурирующим образом в неопределённом порядке. В этом примере используетсяCountedCompleter; аналогичные примеры применимы к другим построениям параллельных задач.static <T> void parEach(TaggedArray<T> a, Consumer<T> action) { Spliterator<T> s = a.spliterator(); long targetBatchSize = s.estimateSize() / (ForkJoinPool.getCommonPoolParallelism() * 8); new ParEach(null, s, action, targetBatchSize).invoke(); } static class ParEach<T> extends CountedCompleter<Void> { final Spliterator<T> spliterator; final Consumer<T> action; final long targetBatchSize; ParEach(ParEach<T> parent, Spliterator<T> spliterator, Consumer<T> action, long targetBatchSize) { super(parent); this.spliterator = spliterator; this.action = action; this.targetBatchSize = targetBatchSize; } public void compute() { Spliterator<T> sub; while (spliterator.estimateSize() > targetBatchSize && (sub = spliterator.trySplit()) != null) { addToPendingCount(1); new ParEach<>(this, sub, action, targetBatchSize).fork(); } spliterator.forEachRemaining(action); propagateCompletion(); } } - В источник невозможно внести структурные изменения.
- Примечание к реализации:
- Если булевая системная переменная
org.openjdk.java.util.stream.tripwireустановлена в значениеtrue, то будут сообщены диагностические предупреждения, если произойдёт упаковка примитивных значений при работе с примитивными специализациями. - С:
- 1.8
- См. также:
Collection
Вложенные классы
| Модификатор и тип | Интерфейс и описание |
|---|---|
static interface |
Spliterator.OfDouble Специализированный Spliterator для значений |
static interface |
Spliterator.OfInt Специализированный Spliterator для значений |
static interface |
Spliterator.OfLong Специализированный Spliterator для значений |
static interface |
Spliterator.OfPrimitive<T,T_CONS,T_SPLITR extends Spliterator.OfPrimitive<T,T_CONS,T_SPLITR>> Специализированный Spliterator для примитивных значений. |
Поля
| Модификатор и тип | Поле и описание |
|---|---|
static int |
CONCURRENT Значение характеристики, указывающее, что источник элементов может быть безопасно изменён параллельно (разрешая добавления, замены и/или удаления) несколькими потоками без внешней синхронизации. |
static int |
DISTINCT Значение характеристики, указывающее, что для каждой пары встреченных элементов |
static int |
IMMUTABLE Значение характеристики, указывающее, что источник элементов не может быть структурно изменён; то есть, элементы не могут быть добавлены, заменены или удалены, поэтому такие изменения не могут произойти во время обхода. |
static int |
NONNULL Значение характеристики, указывающее, что источник гарантирует, что встреченные элементы не будут |
static int |
ORDERED Значение характеристики, указывающее, что порядок встречи элементов определён. |
static int |
SIZED Значение характеристики, указывающее, что значение, возвращённое из |
static int |
SORTED Значение характеристики, указывающее, что порядок встречи следует определённому порядку сортировки. |
static int |
SUBSIZED Значение характеристики, указывающее, что все Spliterator, полученные из |
Методы
| Модификатор и тип | Метод и описание |
|---|---|
int |
characteristics() Возвращает набор характеристик этого Spliterator и его элементов. |
long |
estimateSize() Возвращает оценку количества элементов, которые будут встречены при обходе с помощью |
default void |
forEachRemaining(Consumer<? super T> action) Выполняет заданное действие для каждого оставшегося элемента последовательно в текущем потоке, пока все элементы не будут обработаны или действие не выбросит исключение. |
default Comparator<? super T> |
getComparator() Если источник этого Spliterator |
default long |
getExactSizeIfKnown() Удобный метод, который возвращает |
default boolean |
hasCharacteristics(int characteristics) Возвращает |
boolean |
tryAdvance(Consumer<? super T> action) Если существует оставшийся элемент, выполняет заданное действие над ним, возвращая |
Spliterator<T> |
trySplit() Если этот spliterator может быть разделён, возвращает Spliterator, охватывающий элементы, которые, по возвращении из этого метода, не будут охватываться этим Spliterator. |
Поля
ORDERED
static final int ORDERED
Характеристическое значение, указывающее, что порядок встречи элементов для элементов определён. В таком случае этот Spliterator гарантирует, что метод trySplit() разделяет строгий префикс элементов, что метод tryAdvance(java.util.function.Consumer<? super T>) переходит к одному элементу в порядке префикса, и что forEachRemaining(java.util.function.Consumer<? super T>) выполняет действия в порядке встречи.
У Collection есть порядок встречи, если соответствующий Collection.iterator() документирует порядок. В таком случае порядок встречи совпадает с документированным порядком. В противном случае у коллекции нет порядка встречи.
- Примечание API:
- Порядок встречи гарантированно является восходящим порядком индексов для любого
List. Но порядок не гарантируется для основанных на хэшировании коллекций, таких какHashSet. Клиенты Spliterator, который сообщаетORDERED, должны сохранять ограничения порядка в некоммутативных параллельных вычислениях. - См. также:
- Значения постоянных полей
DISTINCT
static final int DISTINCT
Характеристическое значение, указывающее, что для каждой пары встреченных элементов x, y, !x.equals(y). Это относится, например, к Spliterator, основанному на Set.
- См. также:
- Значения постоянных полей
SORTED
static final int SORTED
Характеристическое значение, указывающее, что порядок встречи соответствует определённому порядку сортировки. В таком случае метод getComparator() возвращает связанный Comparator, или null если все элементы являются Comparable и отсортированы по их естественному порядку.
Spliterator, который сообщает SORTED должен также сообщать ORDERED.
- Примечание API:
- Spliterators для
Collectionклассов в JDK, которые реализуютNavigableSetилиSortedSetсообщаютSORTED. - См. также:
- Значения постоянных полей
SIZED
static final int SIZED
Характеристическое значение, указывающее, что возвращаемое значение из estimateSize() перед обходом или разделением представляет конечный размер, который, в отсутствие структурных изменений источника, представляет точный подсчёт числа элементов, которые будут встречены при полном обходе.
- Примечание API:
- Большинство Spliterators для коллекций, которые покрывают все элементы коллекции
Collection, сообщают об этой характеристике. Под-Spliterators, такие как дляHashSet, которые покрывают подмножество элементов и приближают свой размер, не сообщают. - См. также:
- Значения постоянных полей
NONNULL
static final int NONNULL
Характеристическое значение, указывающее, что источник гарантирует, что встречаемые элементы не будут null. (Это относится, например, к большинству конкурентных коллекций, очередям и картам.)
- См. также:
- Значения постоянных полей
IMMUTABLE
static final int IMMUTABLE
Характеристическое значение, указывающее, что источник элементов не может быть структурно изменён; то есть элементы нельзя добавить, заменить или удалить, поэтому такие изменения не могут произойти во время обхода. Spliterator, который не сообщает IMMUTABLE или CONCURRENT должен иметь документированную политику (например, выбрасывать ConcurrentModificationException) в случае обнаружения структурного вмешательства во время обхода.
- См. также:
- Значения постоянных полей
CONCURRENT
static final int CONCURRENT
Характеристическое значение, указывающее, что источник элементов может быть безопасно изменён одновременно (разрешая добавление, замену и/или удаление) несколькими потоками без внешней синхронизации. В таком случае от Spliterator ожидается документированная политика относительно влияния модификаций во время обхода.
Уровневый Spliterator не должен сообщать как CONCURRENT, так и SIZED, так как конечный размер, если известен, может измениться, если источник изменяется одновременно во время обхода. Такой Spliterator несогласован, и никакие гарантии не могут быть даны относительно любого вычисления, использующего этот Spliterator. Под-Spliterators могут сообщать SIZED если размер под-разбиения известен и добавление или удаление в источнике не отражается при обходе.
- Примечание API:
- Большинство конкурирующих коллекций поддерживают политику согласованности, гарантирующую точность в отношении элементов, присутствующих в момент создания Spliterator, но, возможно, не отражающих последующие добавления или удаления.
- См. также:
- Значения постоянных полей
SUBSIZED
static final int SUBSIZED
Характеристическое значение, указывающее, что все Spliterators, полученные из trySplit(), будут и SIZED, и SUBSIZED. (Это означает, что все дочерние Spliterators, прямые или косвенные, будут SIZED.)
Spliterator, который не сообщает SIZED как требуется SUBSIZED, несогласован, и никакие гарантии не могут быть даны относительно любого вычисления, использующего этот Spliterator.
- Примечание API:
- Некоторые Spliterators, такие как основной Spliterator для примерно сбалансированного двоичного дерева, будут сообщать
SIZED, но неSUBSIZED, так как часто известно количество элементов всего дерева, но не точные размеры поддеревьев. - См. также:
- Значения постоянных полей
Методы
tryAdvance
boolean tryAdvance(Consumer<? super T> action)
Если оставшийся элемент существует, выполняет заданное действие над ним, возвращая true; в противном случае возвращает false. Если этот Spliterator является ORDERED, действие выполняется над следующим элементом в порядке встречи. Исключение, брошенное действием, передаётся вызывающему объекту.
- Параметры:
-
action- Действие - Возвращает:
-
falseесли в методе не было оставшихся элементов при входе, иначеtrue. - Исключения:
-
NullPointerException- если указанное действие равно null
forEachRemaining
default void forEachRemaining(Consumer<? super T> action)
Выполняет заданное действие для каждого оставшегося элемента последовательно в текущем потоке, до тех пор, пока все элементы не будут обработаны или действие не выбросит исключение. Если этот Spliterator является ORDERED, действия выполняются в порядке встречи. Исключение, брошенное действием, передаётся вызывающему объекту.
- Требования к реализации:
- По умолчанию реализация многократно вызывает
tryAdvance(java.util.function.Consumer<? super T>), пока не вернётfalse. Она должна быть переопределена, когда это возможно. - Параметры:
-
action- Действие - Исключения:
-
NullPointerException- если указанное действие равно null
trySplit
Spliterator<T> trySplit()
Если этот Spliterator может быть разделён, возвращает Spliterator, охватывающий элементы, которые после возврата из этого метода не будут охватываться этим Spliterator.
Если этот Spliterator является ORDERED, возвращаемый Spliterator должен охватывать строгий префикс элементов.
Если этот Spliterator не охватывает бесконечное число элементов, повторные вызовы trySplit() должны в конечном итоге вернуть null. После возврата не нулевого значения:
- значение, отчётённое для
estimateSize()до разделения, должно после разделения быть больше или равноestimateSize()для этого и возвращаемого Spliterator; и - если этот Spliterator является
SUBSIZED, тогдаestimateSize()для этого Spliterator до разделения должен быть равен суммеestimateSize()для этого и возвращаемого Spliterator после разделения.
Этот метод может возвратить null по любой причине, включая пустоту, невозможность разделения после начала обхода, ограничения структуры данных и соображения эффективности.
- Примечание API:
- Идеальный метод
trySplitэффективно (без обхода) делит свои элементы точно пополам, что позволяет сбалансированное параллельное вычисление. Многие отклонения от этого идеала остаются высокоэффективными; например, только приблизительное разделение приблизительно сбалансированного дерева или для дерева, в котором листья могут содержать либо один, либо два элемента, не пытаясь дополнительно разделять эти узлы. Однако большие отклонения в балансе и/или чрезмерно неэффективные механизмыtrySplitобычно приводят к плохой параллельной производительности. - Возвращает:
- Spliterator, охватывающий часть элементов, или
nullесли этот Spliterator не может быть разделён
estimateSize
long estimateSize()
Возвращает оценку количества элементов, которые будут встречены при обходе с помощью forEachRemaining(java.util.function.Consumer<? super T>), или возвращает Long.MAX_VALUE если бесконечно, неизвестно или слишком дорого вычислить.
Если этот Spliterator является SIZED и ещё не был частично пройден или разделён, или этот Spliterator является SUBSIZED и ещё не был частично пройден, эта оценка должна быть точным количеством элементов, которые будут встречены при полном обходе. В противном случае эта оценка может быть произвольно неточной, но должна уменьшаться, как указано, при вызовах trySplit().
- Примечание API:
- Даже приблизительная оценка часто полезна и недорога в вычислении. Например, под-разделитель приблизительно сбалансированного бинарного дерева может возвращать значение, которое оценивает число элементов, равное половине от числа элементов родительского элемента; если корневой разделитель не поддерживает точный счёт, он может оценить размер как степень двойки, соответствующую максимальной глубине.
- Возвращает:
- оценённый размер или
Long.MAX_VALUE, если бесконечен, неизвестен или слишком дорого вычисляется.
getExactSizeIfKnown
default long getExactSizeIfKnown()
Удобный метод, возвращающий estimateSize(), если этот разделитель SIZED, иначе -1.
- Требования к реализации:
- По умолчанию реализация возвращает результат
estimateSize(), если разделитель сообщает о характеристикеSIZED, и-1в противном случае. - Возвращает:
- точный размер, если известен, иначе
-1.
characteristics
int characteristics()
Возвращает набор характеристик этого разделителя и его элементов. Результат представлен как побитовое ИЛИ значений из ORDERED, DISTINCT, SORTED, SIZED, NONNULL, IMMUTABLE, CONCURRENT, SUBSIZED. Повторные вызовы characteristics() для данного разделителя до или между вызовами trySplit должны всегда возвращать тот же результат.
Если разделитель сообщает о несогласованном наборе характеристик (как при единственном вызове, так и при нескольких вызовах), никакие гарантии о вычислениях с использованием этого разделителя не могут быть даны.
- Примечание API:
- Характеристики данного разделителя перед разделением могут отличаться от характеристик после разделения. Для конкретных примеров см. значения характеристик
SIZED,SUBSIZEDиCONCURRENT. - Возвращает:
- представление характеристик
hasCharacteristics
default boolean hasCharacteristics(int characteristics)
Возвращает true, если характеристики этого разделителя characteristics() содержат все заданные характеристики.
- Требования к реализации:
- По умолчанию реализация возвращает true, если соответствующие биты заданных характеристик установлены.
- Параметры:
-
characteristics- проверяемые характеристики - Возвращает:
-
trueесли все указанные характеристики присутствуют, иначеfalse
getComparator
default Comparator<? super T> getComparator()
Если источник этого разделителя SORTED по Comparator, возвращает этот Comparator. Если источник SORTED в естественном порядке, возвращает null. В противном случае, если источник не SORTED, выбрасывает IllegalStateException.
- Требования к реализации:
- По умолчанию реализация всегда выбрасывает
IllegalStateException. - Возвращает:
- Comparator или
null, если элементы отсортированы в естественном порядке. - Выбрасывает:
-
IllegalStateException- если разделитель не сообщает о характеристикеSORTED.
© 1993, 2020, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.