Может ли Java Collectors.groupingBy вернуть поток в виде списка сгруппированных элементов?

В C # Linq GroupBy возвращает IEnumerable из IGrouping элементов, которые в свою очередь являются IEnumerable элементов выбранного типа значения. Вот пример:

 var namesAndScores = new Dictionary<string, int>> {
    ["David"] = 90,
    ["Jane"] = 91,
    ["Bill"] = 90,
    ["Tina"] = 89)
};
var IEnumerable<IGrouping<int, string>> namesGroupedByScore =
    namesAndScores
        .GroupBy(
            kvp => kvp.Value,
            kvp => kvp.Key
        );

// Result:
// 90 : { David, Bill }
// 91 : { Jane }
// 89 : { Tina }

В частности, обратите внимание, что каждый IGrouping<int, string> является IEnumerable<string> и, скажем, не List<string> . (Он также имеет свойство .Key.)

Очевидно, что GroupBy должно полностью перечислить элементы ввода, прежде чем он сможет создать одну группу, поскольку он ---- +: = 10 =: + ---- вместо IEnumerable<string> может быть выигрыш в производительности, если вы не перечислите всю группу, например, если вы только что сделали List<string>.

Помимо: технически, я полагаю, .First() может подождать, пока вы перечислите его, чтобы использовать один элемент из ввода, затем выдает один GroupBy и перечисляет только оставшуюся часть ввода как IGrouping перечисляется, собирая другие группы в свою внутреннюю структуру данных при поиске следующего элемента в текущей группе, но я считаю, что это маловероятная и проблематичная реализация, и ожидаю, что IGrouping будет полностью перечисляться во время вызова.

Вот как будет выглядеть код с GroupBy:

 First()

Теперь в Java Streams для группировки нужно собирать, и вы не можете просто дать сборщику var oneStudentForEachNumericScore = namesGroupedByScore .ToDictionary( grouping => grouping.Key, grouping => grouping.First() // does not fully enumerate the values ); // Result: // 90 : David -- Bill is missing and we don't care // 91 : Jane // 89 : Tina вторую лямбду для извлечения значения. Если вы хотите, чтобы значение отличалось от всего ввода, вам нужно снова отобразить (хотя обратите внимание, что коллектор groupingBy позволяет вам создавать многоуровневые группы групп ... групп за один шаг). Вот код, эквивалентный приведенному выше коду C #:

 groupingBy

Это кажется менее чем оптимальным. Итак, мои вопросы:

  1. Есть ли способ выразить это более просто, без необходимости использовать Map<Integer, List<String>> namesGroupedByScore = namesAndScores .entrySet().stream() .collect(Collectors.groupingBy( Map.Entry::getValue, Collectors.mapping( Map.Entry::getKey, Collectors.toList(), ) )); , чтобы получить элементы группы в качестве значения?
  2. Почему мы должны собирать в полностью перечислимый тип? Есть ли способ имитировать тип значения Collectors.mapping для C # IEnumerable и вернуть GroupBy из Map<Integer, Stream<String>>, или это будет бесполезно, потому что элементы значений должны быть перечислены полностью? Или мы могли бы написать наш собственный Collectors.mapping(), который принимает лямбду для второго аргумента и выполняет за нас работу, делая синтаксис ближе к Linq ---- +: = 28 =: + ---- и, по крайней мере, с более чистым синтаксисом и, возможно, немного улучшенной производительностью?
  3. В качестве теоретического упражнения, даже если оно практически бесполезно, можно написать наш собственный сборщик Java Stream Collectors.groupingBy, который возвращает ---- +: = 30 =: + ---- и НЕ выполняет итерацию своего ввода до и до тех пор, пока он не будет перечислен (итерация по одному элементу за раз, отложенная)?
4 голоса | спросил ErikE 29 Mayam18 2018, 00:10:50

2 ответа


0
Хотя эти операции выглядят похожими в некоторых аспектах, они принципиально отличаются.В отличие от операции Linq ---- +: = 0 =: + ---- , в Java ---- +: = 1 =: + ---- является ---- +: = 2 =: + -- , предназначенный для работы с терминальной операцией ---- +: = 3 =: + ---- потокового API, которая сама по себе не является промежуточной операцией и, следовательно, не может использоваться для реализацииленивая потоковая операция в общем.Коллектор ---- +: = 4 =: + ---- использует другой нисходящий поток ---- +: = 5 =: + ---- для групп, поэтому вместо потоковой передачи по элементам группы для выполненияв другой операции вы бы указали коллектор, выполняющий эту операцию на месте, в лучшем случае.Хотя эти коллекторы не поддерживают короткое замыкание, они устраняют необходимость собирать группы в ---- +: = 6 =: + ---- s, просто для их потоковой передачи.Просто подумайте, например, ---- +: = 7 =: + ---- .Случай сбора групп в ---- +: = 8 =: + ---- считается достаточно распространенным, чтобы можно было предположить, что ---- +: = 9 =: + ----, когда вы нене указывать коллектор, но это не учитывалось в случае отображения элементов перед сбором в список.Если вы столкнетесь с этим случаем достаточно часто, вам будет легко определить собственный коллектори использовать его каки, поскольку вы не обязаны использовать ссылки на методы и хотите быть ближе к оригиналу Linq:но, как уже отмечалось, если вы собираетесь потом просматривать поток по этой карте и беспокоиться о ленивости этой операции, вы, вероятно, захотите использовать другой коллектор, чем ---- +: = 13 =: + ----так или иначе.Хотя этот подход обеспечивает некоторую гибкость в отношении результирующих значений, ---- +: = 14 =: + ---- и его ключи являются неотъемлемой частью этой операции, так как не только ---- +: =15 =: + ---- обеспечивают логику хранения, ее операция поиска также отвечает за формирование групп, что также определяет семантику.Например, когда вы используете вариант с поставщиком карт с ---- +: = 16 =: + ----, вы можете получить совершенно другие группы, как и по умолчанию ---- +: = 17 =: + ---- (подумайте, например, ---- +: = 18 =: + ---- ).С другой стороны, когда вы предоставляете ---- +: = 19 =: + ---- , вы можете не получить другую семантику, но совершенно другие характеристики производительности.Напротив, операция ---- +: = 20 =: + ---- от Linq, которую вы описали, похожа на промежуточную операцию, которая вообще не имеет никакой возможности в Stream API.Как вы сами себе предположили, высоки шансы, что он все же совершит полный обход после опроса первого элемента, полностью заполняя структуру данных за кулисами.Даже если реализация пробует некоторую лень, результаты ограничены.Вы можете дешево получить первый элемент первой группы, но если вас интересует только этот элемент, вам вообще не понадобится группировка.Второй элемент первой группы может уже быть последним в исходном потоке, требуя полного обхода и хранения.Таким образом, предложение такой операции подразумевает некоторую сложность с небольшим преимуществом по сравнению с тем, чтобы собирать с нетерпением.Также трудно представить реализацию с параллельной поддержкой (предлагая преимущества по сравнению с операцией ---- +: = 21 =: + ---- ).Фактическое неудобство связано не с этим дизайнерским решением, а с тем фактом, что результирующее ---- +: = 22 =: + ---- не является ---- +: = 23 =: + ----(обратите внимание, что реализация ---- +: = 24 =: + ---- одна не подразумевает наличие метода ---- +: = 25 =: + ---- ) и решение разделить операции сбора и потокоперации .Эти два аспекта приводят к требованию использовать ---- +: = 26 =: + ---- для потоковой передачи по карте, но это выходит за рамки этого вопроса.И, как сказано выше, если вам это нужно, сначала проверьте, не может ли другой нижестоящий коллектор для ---- +: = 27 =: + ---- не получить желаемый результат.Для полноты, вот решение, которое пытается реализовать отложенную группировку:Вы можете проверить это на следующем примере:который напечатает:показывая, что лень работает как можно дальше.НоКаждая операция, которая требует знать все группы /ключи, требует полного обхода источника, так как самый последний элемент может вводить новую группуКаждая операция, требующая обработки всех элементов хотя бы одной группы, требует полного прохождения, так как самый последний элемент источника может принадлежать этой группе.Предыдущий пункт относится даже к коротким замыканиям, если они не могут остановиться рано.Например, в приведенном выше примере поиск совпадения во второй группе подразумевает неудачный полный обход первой группы, следовательно, полный обход источникаПриведенный выше пример можно переписать вкоторый предлагает еще лучшую лень (например, если матч не в первой группе).Конечно, пример был надуманным, но у меня есть сильное чувство, что практически любая операция, которая имеет потенциал отложенной обработки, т.е. не требует всех групп и не нуждается во всех элементах хотя бы одной группы, может быть переписана в операциюэто не нуждается в группировке вообще.
ответил Holger 29 Maypm18 2018, 14:39:45
0
Вот решения для части ваших вопросов от StreamEx и моей библиотеки AbacusUtilЕсли вы хотите сохранить только имя после группы по:Если вы хотите сохранить максимум 2 значения.Что касается остальных вопросов, я верю тому, что сказал Хольгер: «... но у меня есть сильное чувство, что почти любая операция, которая несет в себе потенциал отложенной обработки, т.е. не требует всех групп и не требует всех элементов, по крайней мереодна группа, может быть переписана в операцию, которая вообще не нуждается в группировке ".В любом случае, если ---- +: = 3 =: + ---- требуется, я не думаю, что существует такая реализация без итерации всех элементов, независимо от того, какой язык вы используете.Если итерация всех элементов не нужна, скорее всего ---- +: = 4 =: + ---- не нужна или используется не по назначению.
ответил 123-xyz 31 Mayam18 2018, 10:34:56

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132