Интерфейс 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, это значение точно соответствует количеству элементов, которые будут встречены при успешном обходе. Однако, даже если оно не известно точно, оцененное значение всё равно может быть полезно для операций, выполняемых над источником, например, для того, чтобы определить, предпочтительнее ли дальнейшее разделение или последовательный обход оставшихся элементов.
Несмотря на очевидную полезность в параллельных алгоритмах, от Spliterator не ожидается, что он будет потокобезопасным; вместо этого, реализации параллельных алгоритмов, использующих Spliterator, должны гарантировать, что Spliterator используется только одной нитью за раз. Это обычно легко достигается с помощью последовательного потокового ограничения, которое часто является естественным следствием типичных параллельных алгоритмов, работающих путём рекурсивного разложения. Нить, вызывающая trySplit(), может передать возвращаемый Spliterator другой нити, которая, в свою очередь, может обходить или дополнительно разделять этот Spliterator. Поведение разделения и обхода не определено, если две или более нити одновременно работают с одним и тем же Spliterator. Если исходная нить передаёт Spliterator другой нити для обработки, лучше, если эта передача происходит до потребления каких-либо элементов с помощью tryAdvance(), так как некоторые гарантии (например, точность estimateSize() для SIZED Spliterator) действительны только до начала обхода.
Для значений типа int, long и double предоставляются специализации примитивных подтипов Spliterator. Стандартные реализации подтипов для 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
- См. также:
Краткое описание вложенных классов
| Modifier and Type | Interface | Description |
|---|---|---|
static interface |
Spliterator.OfDouble |
A Spliterator, специализированный для значений double. |
static interface |
Spliterator.OfInt |
A Spliterator, специализированный для значений int. |
static interface |
Spliterator.OfLong |
A Spliterator, специализированный для значений long. |
static interface |
Spliterator.OfPrimitive<T, |
A Spliterator, специализированный для примитивных значений. |
Краткое описание полей
| Modifier and Type | Field | Description |
|---|---|---|
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 |
Краткое описание методов
| Modifier and Type | Method | Description |
|---|---|---|
int |
characteristics() |
Возвращает набор характеристик этого Spliterator и его элементов. |
long |
estimateSize() |
Возвращает оценку количества элементов, которые будут встречены при обходе forEachRemaining(java.util.function.Consumer<? super T>), или возвращает Long.MAX_VALUE, если количество бесконечно, неизвестно или слишком сложно для вычисления. |
default void |
forEachRemaining |
Выполняет заданное действие для каждого оставшегося элемента последовательно в текущем потоке, пока все элементы не будут обработаны или действие не выбросит исключение. |
default Comparator |
getComparator() |
|
default long |
getExactSizeIfKnown() |
|
default boolean |
hasCharacteristics |
Возвращает true , если характеристики этого Spliterator characteristics() содержат все заданные характеристики. |
boolean |
tryAdvance |
Если существует оставшийся элемент: выполняет заданное действие над ним, возвращая true; иначе возвращает false. |
Spliterator |
trySplit() |
Если этот итератор может быть разделен, возвращает 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. При возвращаемом значении не равном 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. Если источник SORTED в естественном порядке, возвращает null. В противном случае, если источник не SORTED, выбрасывает IllegalStateException.- Требования к реализации:
- Реализация по умолчанию всегда выбрасывает
IllegalStateException. - Возвращает:
- компаратор, или
nullесли элементы отсортированы в естественном порядке. - Выбрасывает:
-
IllegalStateException- если разделитель не сообщает о характеристикеSORTED.
© 1993, 2023, 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/21/docs/api/java.base/java/util/Spliterator.html