Перейти к содержимому

Java spliterator что это

  • автор:

Сплитератор Java

Java Spliterator — это один из четырех итераторов — Enumeration, ListIterator и Spliterator.

Сплитератор Java

Подобно Iterator и ListIterator, Spliterator — это итератор Java, который используется для последовательной итерации элементов из реализованного объекта List. Некоторые важные моменты о Java Spliterator:

  1. Java Spliterator – это интерфейс в Java Collection API.
  2. Spliterator представлен в выпуске Java 8 в пакете java.util .
  3. Он поддерживает функции параллельного программирования.
  4. Мы можем использовать его как для классов Collection API, так и для классов Stream API.
  5. Он предоставляет характеристики коллекций или объектов API.
  6. Нельзя использовать этот итератор для классов, реализованных на карте.
  7. Он использует метод tryAdvance() для индивидуальной итерации элементов в нескольких потоках для поддержки параллельной обработки.
  8. Он использует метод forEachRemaining() для последовательной итерации элементов в одном потоке.
  9. Он использует метод trySplit() , чтобы разделить себя на вспомогательные разделители для поддержки параллельной обработки.
  10. Spliterator поддерживает как последовательную, так и параллельную обработку данных.

Spliterator сам по себе не поддерживает параллельное программирование. Тем не менее, он предоставляет некоторые методы для его поддержки. Разработчики должны использовать методы интерфейса Spliterator и реализовывать параллельное программирование с помощью Fork/Join Framework (один из хороших подходов).

Основные функции Spliterator
  • Разделение исходных данных.
  • Обработка исходных данных.

Диаграмма классов Java Spliterator

Методы разделения Java

В этом разделе мы перечислим все методы Java Spliterator один за другим с некоторым полезным описанием.

  1. int характеристики(): возвращает набор характеристик этого разветвителя и его элементов.
  2. длинная оценкаSize(): возвращает оценку количества элементов, которые будут обнаружены при обходе forEachRemaining(), или возвращает значение Long.MAX_VALUE, если число элементов бесконечно, неизвестно или слишком дорого для вычисления.
  3. по умолчанию void forEachRemaining(действие потребителя): выполняет указанное действие для каждого оставшегося элемента последовательно в текущем потоке, пока все элементы не будут обработаны или пока действие не вызовет исключение.
  4. Компаратор по умолчанию getComparator(): если источник этого разделителя СОРТИРОВАН компаратором, возвращает этот компаратор.
  5. длинный по умолчанию getExactSizeIfKnown(): удобный метод, который возвращает AssessmentSize(), если этот разделитель SIZED, иначе -1.
  6. логическое значение по умолчанию hasCharacteristics(int характеристики): возвращает значение true, если характеристики этого разветвителя() содержат все заданные характеристики.
  7. boolean tryAdvance(Consumer action): если оставшийся элемент существует, выполняет над ним заданное действие, возвращая true; иначе возвращает ложь.
  8. Сплитератор trySplit(): если этот сплитератор может быть разделен, возвращает сплитератор, покрывающий элементы, которые после возврата из этого метода не будут покрыты этим сплитератором.

Пример сплиттера Java

В этом разделе мы обсудим, как создать объект Java Spliterator с помощью spliterator(), и разработаем простой пример.

import java.util.Spliterator; import java.util.ArrayList; import java.util.List; public class SpliteratorSequentialIteration < public static void main(String[] args) < Listnames = new ArrayList<>(); names.add("Rams"); names.add("Posa"); names.add("Chinni"); // Getting Spliterator Spliterator namesSpliterator = names.spliterator(); // Traversing elements namesSpliterator.forEachRemaining(System.out::println); > > 
Rams Posa Chinni 

Если мы посмотрим на приведенную выше программу и вывод, мы легко поймем, что этот метод Spliterator.forEachRemaining() работает так же, как ArrayList.foreach(). Да, оба работают одинаково.

Преимущества сплитератора

  1. В отличие от Iterator и ListIterator, он поддерживает функции параллельного программирования.
  2. В отличие от Iterator и ListIterator, он поддерживает как последовательную, так и параллельную обработку данных.
  3. По сравнению с другими итераторами, он обеспечивает более высокую производительность.

Итератор против Сплитератора

Iterator Spliterator
Introduced in Java 1.2. Introduced in Java 1.8.
It is an Iterator for whole Collection API. It is an Iterator for both Collection and Stream API, except Map implemented classes.
It is an Universal Iterator. It is NOT an Universal Iterator.
It does NOT support Parallel Programming. It supports Parallel Programming.

Это все о Spliterator в Java. Ссылка: документ API

Использование Spliterator в Java

Итераторы в Java используются для прохождения элементов данного источника. Spliterator в Java является одним из четырех доступных итераторов Java – Iterator, Enumeration, ListIterator и Spliterator . Это интерфейс, доступный в пакете java.util .

Spliterator был впервые представлен в Java 8 для поддержки параллельного программирования. Однако мы можем использовать его как для последовательной, так и для параллельной обработки элементов данных. Чтобы получить экземпляр Java Spliterator , мы будем использовать метод spliterator () :

List list = Arrays.asList( 1 , 2 , 3 , 4 , 5 );
Spliterator splitr = list.spliterator();

Мы можем думать о Java Spliterator как:

Spliterator = Splitting + Iteration

Характеристики сплитератора :

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

  • SIZED – способен возвращать точное количество элементов в источнике, когда мы вызываем метод valuSize ()
  • SUBSIZED – когда мы разделяем экземпляр с помощью trySplit () и получаем также SIZED SplitIterators
  • ORDERED – перебор упорядоченной последовательности
  • SORTED – перебирать отсортированную последовательность
  • NONNULL – источник гарантирует, что значения не равныNULL
  • DISTINCT – в нашей исходной последовательности нет дубликатов
  • IMMUTABLE – если мы не можем структурно изменить источник элемента
  • CONCURRENT – источник элемента может быть безопасно одновременно изменен

Мы можем использовать метод int Характеристики () для запроса характеристик нашего экземпляра Spliterator . Он возвращает значение OR для всех значений квалифицирующих признаков для нашего Spliterator . Для нашего определенного разделителя у нас будет:

int charactersticsORed = splitr.characteristics(); //16464

hasCharacteristics () :

Мы можем использовать логический метод hasCharacteristics (int признак), чтобы проверить, имеет ли наш экземпляр данную характеристику или нет:

boolean isSized = splitr.hasCharacteristics(Spliterator.SIZED); //true
boolean isSorted = splitr.hasCharacteristics(Spliterator.SORTED); //false
boolean isNonNull = splitr.hasCharacteristics(Spliterator.NONNULL); //false

estimateSize ():

МетодtimateSize () возвращает приблизительное количество элементов, оставшихся для итерации. Он возвращает Long.MAX_VALUE, если значение бесконечно, неизвестно или слишком дорого для вычисления. Для SIZED Spliterator он возвращает значение, которое точно соответствует количеству элементов, которые будут встречаться при успешном обходе:

long estimatedSize = splitr.estimateSize(); // 5

getExactSizeIfKnown ():

Это просто вспомогательный метод, который возвращает оценкуSize (), если это SIZED Spliterator или возвращает -1 :

long size = splitr.getExactSizeIfKnown(); // 5

tryAdvance ():

Подпись метода tryAdvance () выглядит следующим образом:

default boolean tryAdvance(Consumer action)

Метод tryAdvance () в Spliterator объединяет операторы hasNext () и next (), присутствующие в базовом итераторе . Поэтому, если остающийся элемент существует, он выполняет с ним заданное действие, возвращая true; иначе возвращает ложь . Другими словами, он выполняет действие над следующим элементом в последовательности, а затем продвигает итератор.

while (splitr.tryAdvance((item) -> System.out.println(item)));

Если у нас есть ORDERED Spliterator , действие выполняется над следующим элементом в порядке столкновения.

forEachRemaining ():

Метод forEachRemaining (Consumer action) выполняет данное действие для каждого оставшегося элемента, последовательно в текущем потоке, пока все элементы не будут обработаны или действие не вызовет исключение:

splitr.forEachRemaining(item -> System.out.println(item));

Текущая реализация по умолчанию неоднократно вызывает tryAdvance (), пока не вернет false .

trySplit ():

Если разбиение возможно, метод trySplit () разделяет вызывающий Spliterator и возвращает ссылку на элементы покрытия Spliterator, которые не будут покрываться этим Spliterator по возвращении из этого метода. В противном случае возвращается ноль .

Таким образом, после успешного разделения оригинальный Spliterator будет выполнять итерацию по одной части последовательности, а возвращенный Spliterator – по другой ее части.

Кроме того, возвращаемый Spliterator охватывает строгий префикс элементов для начального ORDERED Spliterator (например, над списком ) :

Как работает Java Spliterator

Здесь можно и без spliterator ‘а. Способы делятся на три типа.

    изменяется исходный список
    .replaceAll() (спасибо @zRrr!)

List list = Arrays.asList(1, 2, 3); list.replaceAll(x -> x * 2); 
for (int i = 0; i
List listTransformed = list .stream() .map(x -> x * 2) .collect(Collectors.toList()); 
List listTransformed = new ArrayList<>(); for (int x : list)
Lists.transform(list, x -> x * 2); 

Варианты, когда в результате получается массив вместо списка:

int[] array = list .stream() .mapToInt(x -> x * 2) .toArray(); 

Параллельные операции над массивами в Java 8 — перевод

Java-университет

Новый релиз Java упрощает параллельное взаимодействие с массивами — что приводит к значительно улучшенной производительности с минимумом написания кода. Сейчас, Oracle выпускает Java SE 8 — что является огромным шагом вперёд в плане языка. Одна из важных деталей этого релиза — улучшенная concurrency(параллельность), часть которой появляется в базовом классе java.util.Arrays. В этот класс добавлены новые методы, которые я и буду описывать в этой статье. Некоторые из этих них используются в другой новой фиче JDK8 — в lambda(лямбдах). Но перейдем к делу.

Arrays.paralellSort()

Множество особенностей parallelSort базируется на параллельном алгоритме сортировки слиянием, который рекурсивно разделяет массив на части, сортирует их, а потом рекомбинирует их одновременно в итоговый массив. Используя его вместо существующего, последовательного метода Arrays.sort выражается в улучшенной производительности и эффективности при сортировке больших массивов. Например, код ниже использует последовательную сортировку sort() и параллельную parallelSort() для того чтобы отсортировать один и тот же массив данных: public class ParallelSort < public static void main(String[] args) < ParallelSort mySort = new ParallelSort(); int[] src = null; System.out.println("\nSerial sort:"); src = mySort.getData(); mySort.sortIt(src, false); System.out.println("\nParallel sort:"); src = mySort.getData(); mySort.sortIt(src, true); >public void sortIt(int[] src, boolean parallel) < try < System.out.println("--Array size: " + src.length); long start = System.currentTimeMillis(); if ( parallel == true ) < Arrays.parallelSort(src); >else < Arrays.sort(src); >long end = System.currentTimeMillis(); System.out.println( «—Elapsed sort time: » + (end-start)); > catch ( Exception e ) < e.printStackTrace(); >> private int[] getData() < try < File file = new File("src/parallelsort/myimage.png"); BufferedImage image = ImageIO.read(file); int w = image.getWidth(); int h = image.getHeight(); int[] src = image.getRGB(0, 0, w, h, null, 0, w); int[] data = new int[src.length * 20]; for ( int i = 0; i Для проверки, я загрузил чистые данные из изображения в массив, что заняло 46,083,360 байт(а у вас будет зависеть от изображений которые будете использовать). Метод с последовательной сортировкой занял почти 3,000 миллисекунд для того чтобы отсортировать массив на моём 4х ядерном ноутбуке, в то время как метод параллельной сортировки занял максимум порядка 700 миллисекунд. Согласитесь, не часто случается чтобы новое обновление языка улучшало бы производительность класса в 4 раза.

Arrays.parallelPrefix()

Метод parallelPrefix применяет заданную математическую функцию к элементам массива в совокупности, обрабатывая результаты внутри массива параллельно. Это намного более эффективно на современном многоядерном железе, по сравнению с последовательными операциями на больших массивах. Есть много реализаций этого метода для разных базовых типов операций над данными(например IntBinaryOperator, DoubleBinaryOperator, LongBinaryOperator и так далее), также как и для различных типов математических операторов. Приведу пример суммирования с накоплением на параллельном массиве, использующего тот же самый большой массив, как и в предыдущем примере, который завершается примерно за 100 миллисекунд на моём 4х ядерном ноутбуке. public class MyIntOperator implements IntBinaryOperator < @Override public int applyAsInt(int left, int right) < return left+right; >> public void accumulate() < int[] src = null; // accumulate test System.out.println("\nParallel prefix:"); src = getData(); IntBinaryOperator op = new ParallelSort.MyIntOperator(); long start = System.currentTimeMillis(); Arrays.parallelPrefix(src, new MyIntOperator()); long end = System.currentTimeMillis(); System.out.println("--Elapsed sort time: " + (end-start)); >. >

Arrays.parallelSetAll()

Новый метод parallelSetAll() создает массив и устанавливает каждому элементу массива значение в соответствии с генерирующей эти значения функцией, используя параллельность для повышения эфективности. Этот метод основан на лямбдах(называемых «замыканиями»(closures) в других языках)(и, да, тут ошибка автора, ибо лямбды и замыкания это разные вещи) , и которые являются еще одной новинкой JDK8, которую мы обсудим в будущих статьях. Будет достаточно заметить, лямбды, чей синтаксис легко опознать по оператору ->, производящему операцию над правой частью после стрелки для всех переданных ему элементов. В примере кода, представленном ниже — действие производится для каждого элемента в массиве, проиндексированного по i. Array.parallelSetAll() генерирует элементы массива. Например, следующий код заполняет большой массив случайными integer-значениями: public void createLargeArray() < Integer[] array = new Integer[1024*1024*4]; // 4M Arrays.parallelSetAll( array, i ->new Integer( new Random().nextInt())); > Для создания более сложного генератора элементов массива(например, такого что генерировал бы значения на основе считывания с датчиков из реального мира), можно использовать код близкий к следующему: public void createLargeArray() < Integer[] array = new Integer[1024*1024*4]; // 4M Arrays.parallelSetAll( array, i ->new Integer( customGenerator(getNextSensorValue()))); > public int customGenerator(int arg) < return arg + 1; // some fancy formula here. >public int getNextSensorValue() < // Just random for illustration return new Random().nextInt(); >Мы начнем с getNextSensorValue, который в реальности будет запрашивать датчик(например термометр) вернуть ему текущее значение. Здесь же для примера генерируется случайное значение. Следующий customGenerator() метод генерирует массив элементов с использованием выбранной логики на основе выбранного вами случая. Вот небольшое дополнение, но для реальных случаев, тут было бы что-нибудь посложнее.

Что такое Spliterator?

Другое дополнение к классу Arrays, которое использует параллельность и лямбды — это Spliterator, который используется для итерации и разделения массива. Его действие не ограничено только массивами — он также хорошо работает и для классов Collection и IO каналов. Spliterator’ы работают на основе автоматического разбиения массива на различные части, а новый Spliterator устанавливается для того чтобы производить операции над этими связанными подмассивами. Его название составленно из Iterator(итератора), который «разделяет»(splits) его работу по перемещению-итерации на части. Используя наши, всё те же, данные, мы можем произвести раздельноитерированное(splititerated) действие над нашим массивом следующим образом: public void spliterate() < System.out.println("\nSpliterate:"); int[] src = getData(); Spliterator spliterator = Arrays.spliterator(src); spliterator.forEachRemaining( n ->action(n) ); > public void action(int value) < System.out.println("value:"+value); // Perform some real work on this data here. >Выполнение действий над данными таким образом использует плюсы параллельности. Вы можете также задать параметры сплититератора, такие как минимальный размер каждого подмассива.

Stream — обработка

Наконец, из массива(Array), вы можете создавать объект Stream, который позволяет производить параллельную обработку на выборке данных как целом, обобщенном в последовательность-поток(stream). Разница между коллекцией(Collection) данных и последовательностью-потоком(Stream) из новой JDK8 такая что коллекции позволяют работать с элементами по-отдельности, когда как последовательность-поток не позволяет. Например, с использованием коллекций, вы можете добавлять элементы, удалять, и вставлять в середину. Последовательность-поток Stream не позволяет манипулировать отдельными элементами из набора данных, но вместо этого позволяет выполнять функции над данными как одним целом. Вы можете выполнять такие полезные операции как вытащить только конкретные значения(игнорируя повторы) из набора, операции преобразования данных, нахождение минимумов и максимумов массива, функций map-reduce(используются при распределённых вычислениях), и других математических операциях. Следующий простой пример использует concurrency для параллельной обработки массива данных и суммирования элементов. public void streamProcessing() < int[] src = getData(); IntStream stream = Arrays.stream(src); int sum = stream.sum(); System.out.println("\nSum: " + sum); >

Заключение

Java 8 определённо будет одним из самых полезных обновлений языка. Параллельные фичи, упомянутые здесь, лямбды, и множество других расширений, будут предметом рассмотрения на нашем сайте в других обзорах на Java 8.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Luxury Car Rental in Miami | Reserve a Premium Vehicle with RealCar