Интерфейс 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>
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, если обнаружено структурное вмешательство. Такие Spliterator называются быстропроверками. Метод объёмного обхода (forEachRemaining()) Spliterator может оптимизировать обход и проверять структурное вмешательство после того, как все элементы были пройдены, а не проверять по элементу и сразу же не удаваться.
Spliterator может предоставить оценку количества оставшихся элементов через метод estimateSize(). В идеале, как отражено в характеристике SIZED, это значение точно соответствует числу элементов, которые встретятся при успешном обходе. Однако, даже если оно неизвестно точно, приблизительное значение может быть полезно операциям, выполняемым над источником, например, для определения того, предпочтительнее ли дальнейшее разделение или последовательный обход оставшихся элементов.
Несмотря на их очевидную полезность в параллельных алгоритмах, spliterators не должны быть потокобезопасными; вместо этого реализации параллельных алгоритмов, использующих spliterators, должны гарантировать, что spliterator используется только одной нитью за раз. Это обычно легко достижимо посредством последовательного потокового ограничения, что часто является естественным следствием типичных параллельных алгоритмов, работающих путём рекурсивного разложения. Нить, вызывающая trySplit(), может передать возвращаемый Spliterator другой нити, которая в свою очередь может пройти или дополнительно разбить этот Spliterator. Поведение разбиения и обхода не определено, если две или более нити работают одновременно над одним spliterator. Если исходная нить передаёт spliterator другой нити для обработки, лучше, если эта передача произойдёт до того, как любые элементы будут израсходованы с помощью tryAdvance(), так как определённые гарантии (например, точность estimateSize() для SIZED spliterators) действительны только до начала обхода.
Существуют специализации примитивных подтипов Spliterator для int, long и double значений. Реализации подтипов по умолчанию tryAdvance(java.util.function.Consumer) и forEachRemaining(java.util.function.Consumer) упаковывают примитивные значения в экземпляры соответствующего обертывающего класса. Такая упаковка может подорвать любые преимущества производительности, полученные при использовании примитивных специализаций. Чтобы избежать упаковки, следует использовать соответствующие примитивные методы. Например, Spliterator.OfPrimitive.tryAdvance(java.util.function.IntConsumer) и Spliterator.OfPrimitive.forEachRemaining(java.util.function.IntConsumer) следует использовать вместо Spliterator.OfInt.tryAdvance(java.util.function.Consumer) и Spliterator.OfInt.forEachRemaining(java.util.function.Consumer). Обход примитивных значений с помощью методов упаковки tryAdvance() и forEachRemaining() не влияет на порядок встреч значений, преобразованных в упакованные значения.
- Примечание API:
-
Итераторы Spliterator, как
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
- См. также:
Краткое описание вложенных классов
| Модификатор и тип | Интерфейс | Описание |
|---|---|---|
static interface |
Spliterator.OfDouble |
Spliterator, специализированный для значений double. |
static interface |
Spliterator.OfInt |
Spliterator, специализированный для значений int. |
static interface |
Spliterator.OfLong |
Spliterator, специализированный для значений long. |
static interface |
Spliterator.OfPrimitive<T, |
Spliterator, специализированный для примитивных значений. |
Краткое описание полей
| Модификатор и тип | Поле | Описание |
|---|---|---|
static final int |
CONCURRENT |
Значение характеристики, указывающее, что источник элементов может безопасно изменятся параллельно (дозволяя добавление, замену и/или удаление) несколькими потоками без внешней синхронизации. |
static final int |
DISTINCT |
Значение характеристики, указывающее, что для каждой пары встреченных элементов x, y, !x.equals(y). |
static final int |
IMMUTABLE |
Значение характеристики, указывающее, что источник элементов не может быть структурно изменен; то есть элементы не могут быть добавлены, заменены или удалены, поэтому такие изменения не могут произойти во время обхода. |
static final int |
NONNULL |
Значение характеристики, указывающее, что источник гарантирует, что встреченные элементы не будут null. |
static final int |
ORDERED |
Значение характеристики, указывающее, что определён порядок встречи элементов. |
static final int |
SIZED |
Значение характеристики, указывающее, что возвращаемое значение от estimateSize() перед обходом или разделением представляет конечный размер, который, в отсутствие структурной модификации источника, представляет точный подсчёт количества элементов, которые встретит полный обход. |
static final int |
SORTED |
Значение характеристики, указывающее, что порядок встречи следует определённому порядку сортировки. |
static final int |
SUBSIZED |
Краткое описание методов
| Модификатор и тип | Метод | Описание |
|---|---|---|
int |
characteristics() |
Возвращает набор характеристик этого Spliterator и его элементов. |
long |
estimateSize() |
Возвращает оценку количества элементов, которые были бы встречены при обходе forEachRemaining(java.util.function.Consumer<? super T>), или возвращает Long.MAX_VALUE, если бесконечно, неизвестно или слишком дорого вычисляется. |
default void |
forEachRemaining |
Выполняет заданное действие для каждого оставшегося элемента последовательно в текущем потоке, до тех пор, пока все элементы не будут обработаны или действие не выбросит исключение. |
default Comparator<? super T> |
getComparator() |
|
default long |
getExactSizeIfKnown() |
Удобный метод, который возвращает estimateSize(), если этот Spliterator SIZED, в противном случае -1. |
default boolean |
hasCharacteristics |
Возвращает true , если характеристики этого Spliterator (characteristics()) содержат все заданные характеристики. |
boolean |
tryAdvance |
Если существует оставшийся элемент, выполняет заданное действие над ним, возвращая true; в противном случае возвращает false. |
Spliterator<T> |
trySplit() |
Если этот Spliterator может быть разделен, возвращает Spliterator, покрывающий элементы, которые, по возвращении из этого метода, не будут покрыты этим Spliterator. |
Подробное описание полей
ORDERED
static final int ORDERED
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:
- Большинство Spliterator для коллекций, которые охватывают все элементы
Collection, сообщают об этой характеристике. Под-Spliterators, такие как те дляHashSet, которые охватывают подмножество элементов и приближают свой сообщаемый размер, этого не делают. - См. также:
NONNULL
static final int NONNULL
null. (Это относится, например, к большинству конкурирующих коллекций, очередям и картам.)- См. также:
IMMUTABLE
static final int IMMUTABLE
IMMUTABLE или CONCURRENT, должен иметь документированную политику (например, выбрасывание ConcurrentModificationException) относительно обнаруженного структурного вмешательства во время обхода.- См. также:
CONCURRENT
static final int CONCURRENT
Основной Spliterator не должен сообщать как CONCURRENT, так и SIZED, так как конечный размер, если он известен, может измениться, если источник одновременно изменяется во время обхода. Такой Spliterator несогласован, и никакие гарантии не могут быть даны относительно любых вычислений, использующих этот Spliterator. Под-Spliterators могут сообщать SIZED, если размер подразделения известен, и добавления или удаления в исходнике не отражаются при обходе.
Основной Spliterator не должен сообщать как CONCURRENT, так и IMMUTABLE, так как они взаимоисключают друг друга. Такой Spliterator несогласован, и никакие гарантии не могут быть даны относительно любых вычислений, использующих этот Spliterator. Под-Spliterators могут сообщать IMMUTABLE, если добавления или удаления в исходнике не отражаются при обходе.
- Примечание API:
- Большинство конкурирующих коллекций поддерживают политику согласованности, гарантирующую точность по отношению к элементам, присутствующим в момент создания Spliterator, но возможно не отражающих последующие добавления или удаления.
- См. также:
SUBSIZED
static final int SUBSIZED
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, действие выполняется над следующим элементом в порядке обхода. Исключение, сгенерированное действием, передаётся вызывающему объекту. Поведение Spliterator после выброса исключения не определено.
- Параметры:
-
action- Действие - Возвращает:
-
falseесли при входе в метод не существовало оставшихся элементов, иначеtrue. - Исключения:
-
NullPointerException- если указанное действие равно null
forEachRemaining
default void forEachRemaining(Consumer<? super T> action)
ORDERED, действия выполняются в порядке обхода. Исключение, сгенерированное действием, передаётся вызывающему объекту. Поведение Spliterator после выброса исключения не определено.
- Требования к реализации:
- Стандартная реализация многократно вызывает
tryAdvance(java.util.function.Consumer<? super T>)до тех пор, пока она не вернётfalse. Она должна быть переопределена, когда это возможно. - Параметры:
-
action- Действие - Исключения:
-
NullPointerException- если указанное действие равно null
trySplit
Spliterator<T> trySplit()
Если этот разделитель является ORDERED, возвращаемый разделитель должен охватывать строгий префикс элементов.
Если этот разделитель не охватывает бесконечное число элементов, повторные вызовы trySplit() должны в конечном итоге вернуть null. При ненулевом возврате:
- значение, отчётливое для
estimateSize()до разделения, должно после разделения быть больше или равноestimateSize()для этого и возвращаемого разделителя; и - если этот разделитель является
SUBSIZED, тоestimateSize()для этого разделителя до разделения должен быть равен суммеestimateSize()для этого и возвращаемого разделителя после разделения.
Этот метод может вернуть null по любой причине, включая пустоту, невозможность разделения после начала обхода, ограничения структуры данных и соображения эффективности.
- Примечание API:
- Идеальный метод
trySplitэффективно (без обхода) делит свои элементы точно пополам, позволяя сбалансированное параллельное вычисление. Многие отклонения от этой идеализации остаются высокоэффективными; например, только приблизительное разделение приблизительно сбалансированного дерева или для дерева, в котором листья могут содержать либо один, либо два элемента, без дальнейшего разделения этих узлов. Однако значительные отклонения в балансе и/или чрезмерно неэффективные механизмыtrySplitобычно приводят к плохой параллельной производительности. - Возвращает:
- разделитель
Spliterator, охватывающий часть элементов, илиnull, если этот разделитель не может быть разделен
estimateSize
long estimateSize()
forEachRemaining(java.util.function.Consumer<? super T>), или возвращает Long.MAX_VALUE, если бесконечно, неизвестно или слишком дорого вычислить. Если этот разделитель является SIZED и ещё не был частично пройден или разделён, или этот разделитель является SUBSIZED и ещё не был частично пройден, эта оценка должна быть точным подсчётом элементов, которые встретятся при полном обходе. В противном случае эта оценка может быть произвольно неточной, но должна уменьшаться, как указано, при вызовах trySplit().
- Примечание API:
- Даже неточная оценка часто полезна и недорога в вычислении. Например, под-разделитель приблизительно сбалансированного бинарного дерева может возвращать значение, которое оценивает количество элементов как половину от родительского; если корневой разделитель не поддерживает точный подсчёт, он может оценить размер, как степень двойки, соответствующую максимальной глубине.
- Возвращает:
- оценку размера, или
Long.MAX_VALUEесли бесконечно, неизвестно или слишком дорого вычислить.
getExactSizeIfKnown
default long getExactSizeIfKnown()
- Требования к реализации:
- Стандартная реализация возвращает результат
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. Если источник отсортирован по естественному порядку, возвращает null. Иначе, если источник не отсортирован, бросает IllegalStateException.- Требования к реализации:
- Стандартная реализация всегда бросает
IllegalStateException. - Возвращает:
- сравнение, или
nullесли элементы отсортированы по естественному порядку. - Исключения:
-
IllegalStateException- если разделитель не сообщает о характеристикеSORTED.
© 1993, 2021, 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.
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Spliterator.html