Коллекция, коллекции, сбор, коллекционер, Collectos
Коллекция является интерфейсом предков Java Collections.
Коллекции - это класс инструментов в рамках пакета java.util, который означает различные статические методы для обработки коллекций.
java.util.stream.stream#collect (java.util.stream.collector <? Super T, A, R>) - это функция потока, отвечающего за сбор потоков.
java.util.stream.collector - это интерфейс для сбора функций, который объявляет функции коллекционера.
java.util.comparators - это класс инструментов коллекционера с серией коллекционных реализаций.
Функция коллекционера
Вы можете думать о потоках Java8 как о причудливых и ленивых итераторах наборов данных. Они поддерживают два типа операций: промежуточные операции (например, фильтр, карта) и операции терминала (такие как счет, FindFirst, Foreach, уменьшение). Промежуточные операции могут быть подключены для преобразования одного потока в другой. Эти операции не потребляют потоки, и цель состоит в том, чтобы создать трубопровод. Напротив, операции терминалов потребляют классы, что приведет к окончательному результату. COLLECT - это операция по сокращению, точно так же, как сокращение, она может принимать различные методы в качестве параметров и накапливать элементы в потоке в сводном результате. Конкретный подход определяется путем определения нового интерфейса коллекционера.
Предопределенные коллекционеры
Ниже приведена краткая демонстрация основного встроенного коллекционера. Смоделированный источник данных выглядит следующим образом:
Final Arraylist <dhine> bids = lists.newarraylist (новое блюдо («свинина», false, 800, type.meat), новое блюдо («говядина», ложно, 700, тип. Meat), новое блюдо («курица», ложно, 400, тип. true, 120, type.ether), новое блюдо («Пицца», True, 550, type.other), новое блюдо («креветки», ложно, 300, тип. Рыбка), новое блюдо («лосось», ложь, 450, тип. Рыбка));
Максимальное значение, минимальное значение, среднее значение
// Зачем возвращать обязательно? Что делать, если поток нулевой? Оптин имеет большой смысл в это время опционально band.stream (). Collect (Collectors.AvagingInt (Bid :: GetCalories)); IntsummaryStatistics SummaryStatistics = BIDE.STREAM (). COLLECT (Collectors.SummarizingInt (Dish :: GetCalories)); Double Magne = SummaryStatistics.GetAguerge (); long count = summarystatistics.getCount (); int max = summarystatistics.getmax (); int min = summarystatistics.getmin (); long sum = summarystatistics.getsum ();
Эти простые статистические индикаторы имеют встроенные коллекционеры коллекционеров, особенно для функций распаковки числовых типов, которые будут намного дешевле, чем непосредственно эксплуатировать тип упаковки.
Подключите коллекционер
Хотите собрать элементы потока?
// непосредственно подключить строку join1 = band.stream (). Map (dish :: getName) .collect (collectors.joining ()); // comma string join2 = dishes.stream (). Map (dish :: getName) .collect (collectors.joining (","));толист
List <string> names = band.stream (). Map (bind :: getName) .collect (tolist ());
Составьте оригинальный поток в одном потоке элементов и соберите его в качестве списка.
Тосе
SET <Sype> types = bids.stream (). Map (bid :: getType) .collect (collectors.toset ());
Соберите тип в качестве набора, и вы можете повторить его.
томап
Map <type, band> bytype = band.stream (). Collect (tomap (band :: getType, d -> d));
Иногда может быть необходимо преобразовать массив в карту для кеша, которая облегчает многочисленные расчеты и приобретения. Томап предоставляет функции генерации методов k и v. (Обратите внимание, что вышеупомянутая демонстрация - это яма, вы не можете использовать ее так !!! Пожалуйста, используйте Tomap (функция, функция, бинарная оператор))))
Вышеуказанное - почти наиболее часто используемые коллекционеры, и их в основном достаточно. Но как новичок, понимание требует времени. Чтобы по -настоящему понять, почему это можно использовать для сбора, вы должны проверить внутреннюю реализацию. Вы можете видеть, что эти коллекционеры основаны на java.util.stream.collectors.collectorimpl, который является классом коллекционера, упомянутого в начале. Пользовательский коллекционер узнает конкретное использование позже.
Пользовательское сокращение
Предыдущие немногие являются особыми случаями процесса сокращения, определяемого методом Factory Crement. Фактически, коллекционеры. Редакция может использоваться для создания коллекционера. Например, ищите сумму
Integer totalCalories = band.stream (). Соберите (уменьшение (0, Band :: getCalories, (i, j) -> i + j)); // Использование встроенной функции вместо функции стрелки Integer TotalCalories2 = BIDES.Stream (). СОБЩЕНИЕ (уменьшается (0, Dish :: getCalories, integer :: sum));
Конечно, вы также можете использовать уменьшение напрямую
Необязательно <Integer> totalCalories3 = BIDE.Stream (). MAP (BID :: GetCalories). Reduce (integer :: sum);
Хотя это нормально, если вы рассматриваете эффективность, вам все равно следует выбрать следующее
int sum = bids.stream (). Maptoint (Bind :: getCalories) .sum ();
Выберите лучшее решение в соответствии с ситуацией
Как упомянуто выше, функциональное программирование обычно обеспечивает несколько способов выполнения одной и той же операции. Использование Collector Collect является более сложным, чем использование потоковых API. Преимущество состоит в том, что Collect может обеспечить более высокий уровень абстракции и обобщения, и его легче использовать и настроить.
Наш совет состоит в том, чтобы как можно больше изучить различные решения решающей проблемы, всегда выбирайте наиболее профессиональный, что, как правило, является лучшим решением с точки зрения читаемости и производительности.
В дополнение к получению начального значения, снижение может также использовать первый элемент в качестве начального значения
Необязательно <DING> mothcaloriedish = band.stream () .collect (Reducing ((D1, D2) -> d1.getCalories ()> d2.getCalories ()? D1: D2));
Уменьшение
Использование сокращения довольно сложно, и цель состоит в том, чтобы объединить два значения в одно значение.
Public Static <T, U> Collector <T ,?, U> REDUCING (U Identity, Function <? Super T ,? Extends U> Mapper, Binaryoperator <u> OP)
Во -первых, я видел 3 дженерики.
U - тип возвращаемого значения. Например, тепло, рассчитанное в приведенной выше демонстрации, u является целым числом.
Что касается t, t - тип элемента в потоке. Из функции функции мы можем знать, что функция Mapper состоит в том, чтобы получить параметр T, а затем вернуть результат U. Соответствующий блюдо в демонстрации.
«В середине общего списка с коллекционером возвращаемых значений это представляет собой тип контейнера. Коллекционеру, конечно, нужен контейнер для хранения данных. Здесь? Это означает, что тип контейнера неопределен. На самом деле, контейнер здесь u [].
О параметрах:
Идентификация - это начальное значение типа возвращаемого значения, которое можно понимать как начальную точку аккумулятора.
Mapper - это функция карты, и ее значение заключается в преобразовании потоков в поток типа, который вы хотите.
OP является основной функцией, и его функция заключается в том, как справиться с двумя переменными. Среди них первая переменная - это кумулятивное значение, которое можно понять как сумма, а вторая переменная - это следующий элемент, который будет рассчитан. Таким образом, накопление достигается.
Существует также перегруженный метод для опускания первого параметра, что означает, что первый параметр в потоке используется в качестве начального значения.
Public Static <T> Collector <T ,?, необязательное <t>> снижение (Binaryoperator <T> OP)
Давайте посмотрим на разницу между возвратом значения. T представляет входное значение, а тип возвращаемого значения, то есть тип входного значения и тип выходного значения одинаковы. Другое отличие не является обязательным. Это потому, что нет начального значения, а первый параметр может быть нулевым. Когда элемент потока нулевой, очень значимо вернуть необязательно.
Глядя на список параметров, остался только Binaryoperator. Binaryoperator - это интерфейс тройной функции, цель состоит в том, чтобы вычислить два параметра одного типа и возвращаемых значений того же типа. Это можно понять как 1> 2? 1: 2, то есть найти максимальное значение двух чисел. Поиск максимального значения является относительно простым для понимания заявления. Вы можете настроить выражение Lambda, чтобы выбрать возвращаемое значение. Затем, здесь, он должен получить тип элемента из двух потоков и вернуть возвращаемое значение типа T. Также можно использовать сумму для понимания.
В приведенной выше демонстрации обнаружено, что функции уменьшения и сбора практически одинаковы, оба возвращают конечный результат. Например, мы можем использовать снижение эффекта толиста:
// Вручную реализовать толистколлектор --- злоупотребление уменьшением, неизменные правила --- невозможно параллельно списки <Integer> калории = band.stream (). Map (dish :: getCalories) .reduce (new ArrayList <Integer> (), (список <Integer> l, integer e)-> {l.Add (e); return L;}, (integer> l; L2) -> {L1.Addall (L2);Позвольте мне объяснить вышеупомянутые практики.
<u> u уменьшить (u идентификация, бифункция <u, super t, u> accumulator, binaryoperator <u> combiner);
U - тип возврата значения, вот список
Бифункция <u,? Super T, U> Accumulator является аккумулятором, и его целью является накопление значений и правил расчета для отдельных элементов. Вот операция списка и элементов, и, наконец, список возврата. То есть добавьте элемент в список.
Binaryoperator <u> Combiner - это комбинезон, и цель состоит в том, чтобы объединить две переменные типов возвратных значений в одну. Вот слияние двух списков.
Есть две проблемы с этим решением: одна - семантическая проблема, а другая - практическая проблема. Семантическая проблема заключается в том, что метод уменьшения направлен на объединение двух значений для генерации нового значения, которое является неизменным сокращением. Вместо этого дизайн метода сбора состоит в том, чтобы изменить контейнер и накопление результатов, которые будут выходить. Это означает, что приведенный выше фрагмент кода злоупотребляет методом уменьшения, поскольку он меняет список как аккумулятор на месте. Неправильная семантика для использования метода уменьшения также создает практическую проблему: это сокращение не может работать параллельно, потому что одновременная модификация одной и той же структуры данных с помощью нескольких потоков может разрушить сам список. В этом случае, если вы хотите безопасность потока, вам нужно выделять новый список за раз, и распределение объектов, в свою очередь, повлияет на производительность. Вот почему сбор подходит для выражения сокращения изменчивых контейнеров, и, что более важно, он подходит для параллельных операций.
Резюме: уменьшение подходит для неизменного сокращения контейнеров, сбор подходит для изменяемого сокращения контейнеров. Сбор подходит для параллелизма.
Группировка
База данных часто сталкивается с необходимостью в групповом суммировании и предоставляет группу примитивным. В Java, если вы будете следовать учебному стилю (вручную писать петли), это будет очень громоздко и подвержен ошибкам. Java 8 предоставляет функциональные решения.
Например, групповое блюдо по типу. Подобно предыдущему томапу, но группировка - это не блюдо, а список.
Map <type, list <dood >> bishesbytype = bides.stream (). Collect (Groupbyby (bind :: getType));
здесь
public static <t, k> collector <t ,?
Классификатор параметров - это функция, предназначенная для получения одного параметра и преобразования его в другой тип. Демонстрация выше - преобразовать блюдо элемента потока в тип типа, а затем группировать поток в соответствии с типом. Его внутренняя группировка реализована через HashMap. Groupingby (классификатор, Hashmap :: new, вниз по течению);
В дополнение к группировке в соответствии с функцией свойства самого элемента потока, вы также можете настроить основу группировки, такую как группировка в соответствии с диапазоном тепла.
Поскольку вы уже знаете, что параметр группировки - это функция, а тип параметров - это блюдо, вы можете настроить классификатор как:
Private Caloriclevel GetCaloricAlvel (Band D) {if (d.getCalories () <= 400) {return Caloriclevel.diet; } else if (d.getCalories () <= 700) {return Caloriclevel.normal; } else {return galoriclevel.fat; }}Просто передайте параметры
Карта <caloriclevel, list <Band >> bidsbylevel = bides.stream () .collect (Groupsby (это :: getCaloriclevel));
Многоуровневая группировка
Groupingby также перегружает несколько других методов, таких как
Public Static <T, K, A, D> Collector <T ,?, MAP <K, D >> GroupingBy (function <? Super T ,? Extends K> Classififier, Collector <? Super T, A, D> вниз по течению)
Есть много общих и ужасов. Давайте получим краткое понимание. Классификатор также является классификатором, который получает тип элемента потока и возвращает основу, для которой вы хотите группировать, то есть кардинальность основы группировки. Таким образом, T представляет текущий тип элемента потока, а K представляет тип элемента группировки. Второй параметр находится вниз по течению, а вниз по течению является коллекционером коллекционера. Этот тип элемента коллекционера является подклассом T, контейнер типа контейнера является a, а тип возврата восстановления - D. то есть, k группы предоставляется через классификатор, а значение группы уменьшается через коллектор второго параметра. Точно так и получилось, что исходный код предыдущей демонстрации:
public static <t, k> collector <t ,?, map <k, list <t >>> Groupingby (function <? Super T ,? Extends K> Classifier) {return Groupingby (classifier, tolist ()); }Используйте толист в качестве коллекционера Crement, и конечным результатом является список <Dish>, поэтому тип значения заканчивается группой, это список <DING>. Затем тип значения может быть аналогично определяется с помощью коллекционера Reduct, и существуют десятки миллионов уменьшенных коллекционеров. Например, я хочу снова сгруппировать значение, и группировка также является своего рода уменьшением.
// Карта группировки многоуровневой группировки <тип, карта <caloriclevel, list <dion >>> bytypeandcalory = band.stream (). Collect (Groupby (Bid :: getType, Groupingby (это :: getCaloriClevel)); bytypeandcalory.foreach (тип,: System.out.println("------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- System.out.println ("/t" + level);Результаты проверки:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------. [Блюдо (имя = говядина, вегетарианская = ложно, калории = 700, type=MEAT)]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Тип = другой)]]
Резюме: Основными параметрами группировки являются k генераторы и генераторы V. Генератор V может быть любым типом коллекционера.
Например, генератор V может рассчитать число, реализовав таким образом счет SELECT (*) в операторе SQL из таблицы A Group по типу
Map <type, long> typeScount = band.stream (). Collect (GroupingBy (Bind :: getType, counting ())); System.out.println (typeScount); ---------- {Fish = 2, Meat = 3, другие = 4} SQL Search Group Наибольшая оценка select MAX(id) from table A group by Type
Map <type, необязательный <Bish >> mostcaloricbytype = bides.stream () .collect (Groupsby (bind :: getType, maxby (comparator.comparingint (band :: getCalories))));
Необязательно здесь не имеет смысла, потому что это, конечно, не ноль. Тогда мне пришлось вынуть это. Используя коллекционирование и
Map <тип, блюдо> mostcaloricbytype = band.stream () .collect (Groupbyby (Bind :: getType, CollectionAndThen (Maxby (Comparation.comParingInt (Bind :: getCalories)), необязательно :: get)));
Кажется, что результат приходит сюда, но идея не согласна. Он собирает желтую тревогу и меняет ее на:
Map <тип, блюдо> mostcaloricbytype = bids.stream () .collect (tomap (band :: getType, function.identity (), binaryoperator.maxby (comparingint (dish :: getCalories))));
Да, Groupingby становится томапом, ключ все еще тип, значение все еще является блюдом, но есть еще один параметр! ! Здесь мы отвечаем на яму в начале. Демонстрация томапа в начале предназначена для легкого понимания. Если это действительно используется, это будет убито. Мы знаем, что реорганизация списка в карту неизбежно столкнется с той же проблемой. Когда k одинаково, V переопределяет или игнорирует его? Предыдущий демонстрационный метод должен снова вставить K и сдать исключение непосредственно, когда присутствует K:
java.lang.illegalStateException: дубликат ключевой блюдо (имя = свинина, овощ = false, калории = 800, тип = мясо) на java.util.stream.collectors.lambda $ throwingmerger $ 0 (collectors.java:133)
Правильным способом является обеспечение функций для обработки конфликтов. В этой демонстрации принцип обращения конфликтов состоит в том, чтобы найти самый большой, который просто соответствует нашим требованиям для группировки и поиска крупнейшей. (Я действительно не хочу больше заниматься функциональным обучением Java 8, я чувствую, что повсюду есть ловушки проблем с производительностью)
Продолжить картирование базы данных SQL, select sum(score) from table a group by Type
Map <type, integer> totalCalorioriesBytype = BINE.Stream () .collect (GroupingBy (Bind :: GetType, SummingInt (Bid :: getCalories)));
Тем не менее, другой коллекционер, который часто используется в сочетании с группировкой, генерируется методом отображения. Этот метод получает два параметра: одна функция преобразует элементы в потоке, а другая собирает преобразованные объекты результата. Цель состоит в том, чтобы применить функцию отображения к каждому входному элементу перед накоплением, чтобы коллекционер, который получает элементы определенного типа, мог адаптироваться к различным типам объектов. Позвольте мне посмотреть на практическое пример использования этого коллекционера. Например, вы хотите получить то, что есть в меню для каждого типа блюда. Мы можем объединить групповые и картирование коллекционеров следующим образом:
Map <type, set <caloricalevel >> Caloriclevelsbytype = bides.stream () .collect (Groupbyby (Bind :: getType, mapping (это :: getCaloriclevel, toset ()));
Toset здесь использует хэшсет по умолчанию, и вы также можете вручную указать конкретную токографию реализации (hashset :: new)
Профила
Разделение - это особый случай группировки: предикат (функция, которая возвращает логическое значение) используется в качестве функции классификации, которая называется функцией раздела. Функция разделения возвращает логическое значение, что означает, что тип ключа сгруппированной карты является логическим, поэтому его можно разделить на две группы: true или false. Например, если вы вегетарианец, вы можете отделить меню вегетарианским и невегетарианским:
Map <boolean, list <dood >> deptitionedmenu = band.stream (). Collect (partitionbyby (band :: isevigetarian));
Конечно, использование фильтра может достичь того же эффекта:
Список <DING> Вегетарианцы = BIDE.STREAM (). Фильтр (BIND :: ISvegetarian) .collect (collectors.tolist ());
Преимущество разделения состоит в том, чтобы сохранить две копии, что полезно, когда вы хотите классифицировать список. В то же время, как и Groupingby, Partityingby имеет перегруженные методы, которые могут указать тип значения группировки.
Карта <boolean, map <type, list <dood >>> vagearitaridishesbytype = bind.stream () .collect (partitionbyby (bind :: isevegetarian, Groupbyby (Bid :: getType))); карта <boolean, Integer> Вегетариатаридестоталькалуры = блюдо. SummingInt (Bind :: GetCalories))); Map <Boolean, Dish> MothcaloricPartitionedByVegetarian = Bines.stream () .collect (PartitionBy (Bid :: isevegetarian, collection и (maxby (comparingInt (bid :: getCalories)), опция :: get));
В качестве последнего примера использования коллекционера PartitionBy, мы откладываем модель данных меню в сторону, чтобы увидеть более сложный и интересный пример: разделение массивов на основные и не преподавательские номера.
Во -первых, определите основную функцию разделения:
Частный логический iSprime (int кандидат) {int candidateroot = (int) math.sqrt ((двойной) кандидат); return intstream.rangeclosed (2, candidateroot) .nonematch (i -> Candidate % i == 0);}Затем найдите первичные и не преподавательные номера от 1 до 100
Map <boolean, list <Integer >> partitionprimes = intstream.rangeclosed (2, 100) .boded () .collect (partitionby (это :: isprime));