В Java 8 есть Stream.collect
возможность объединения по коллекциям. В Kotlin, это не существует таким же образом, за исключением, может быть, как набор функций расширения в stdlib. Но не ясно, каковы эквивалентности для разных вариантов использования.
Например, в верхней части JavaDocCollectors
находятся примеры, написанные для Java 8, и при переносе их на Kolin вы не можете использовать классы Java 8 в другой версии JDK, поэтому, скорее всего, они должны быть написаны по-другому.
С точки зрения ресурсов онлайн, показывающих примеры коллекций Kotlin, они, как правило, тривиальны и на самом деле не сравниваются с теми же вариантами использования. Каковы хорошие примеры, которые действительно соответствуют случаям, таким как документированные для Java 8 Stream.collect
? Список там есть:
- Накапливать имена в список
- Накапливать имена в TreeSet
- Преобразуйте элементы в строки и объедините их через запятую
- Рассчитать сумму заработной платы работника
- Сотрудники группы по отделам
- Рассчитать сумму зарплат по отделам
- Разделить студентов на прохождение и провал
С подробностями в JavaDoc связаны выше.
Примечание: этот вопрос намеренно написан и получен ответ от автора ( Вопросы с самоотвечением ), так что идиоматические ответы на часто задаваемые темы Kotlin присутствуют в SO. Также, чтобы прояснить некоторые действительно старые ответы, написанные для альф Kotlin, которые не являются точными для современного Kotlin.
источник
collect(Collectors.toList())
или подобное, вы можете решить эту проблему: stackoverflow.com/a/35722167/3679676 (проблема, с обходными путями)Ответы:
В Kotlin stdlib есть функции для усреднения, подсчета, различения, фильтрации, поиска, группировки, объединения, отображения, минимального, максимального, разбиения, нарезки, сортировки, суммирования, списков в / из массивов, списков в / из карт, карт в / из карт , объединение, ко-итерация, все функциональные парадигмы и многое другое. Таким образом, вы можете использовать их для создания маленьких однострочников, и нет необходимости использовать более сложный синтаксис Java 8.
Я думаю, что единственное, чего не хватает во встроенномCollectors
классе Java 8, это суммирование (но в другом ответе на этот вопрос простое решение) .Одна вещь, отсутствующая в обоих, - это пакетирование по количеству, которое видно в другом ответе переполнения стека и также имеет простой ответ. Еще один интересный случай - это также из Stack Overflow: идиоматический способ разделения последовательности на три списка с использованием Kotlin . А если вы хотите создать что-то подобноеStream.collect
для другой цели, см. Custom Stream.collect в KotlinРЕДАКТИРОВАТЬ 11.08.2017: Операции сбора по частям / окнам были добавлены в kotlin 1.2 M2, см. Https://blog.jetbrains.com/kotlin/2017/08/kotlin-1-2-m2-is-out/
Всегда полезно изучить Справочник по API для kotlin.collections в целом, прежде чем создавать новые функции, которые могут там уже существовать.
Вот некоторые преобразования из
Stream.collect
примеров Java 8 в эквивалент в Kotlin:Накапливать имена в список
Преобразуйте элементы в строки и объедините их через запятую
Рассчитать сумму заработной платы работника
Сотрудники группы по отделам
Рассчитать сумму зарплат по отделам
Разделить студентов на прохождение и провал
Имена участников мужского пола
Групповые имена членов в списке по полу
Фильтровать список в другой список
Нахождение кратчайшей строки в списке
Подсчет элементов в списке после применения фильтра
и так далее ... Во всех случаях для имитации не требовалось никаких специальных функций сгиба, уменьшения или других функций
Stream.collect
. Если у вас есть другие варианты использования, добавьте их в комментарии, и мы увидим!Про лень
Если вы хотите лениво обработать цепочку, вы можете преобразовать ее в
Sequence
использованиеasSequence()
перед цепочкой. В конце цепочки функций, вы обычно также заканчиваете сSequence
. Затем вы можете использоватьtoList()
,toSet()
,toMap()
или какой -либо другой функции , чтобы материализоватьSequence
в конце.Почему нет типов?!?
Вы заметите, что примеры Kotlin не указывают типы. Это потому, что Kotlin имеет полный вывод типа и полностью безопасен во время компиляции. Более того, чем Java, потому что он также имеет обнуляемые типы и может помочь предотвратить страшный NPE. Итак, это в Котлине:
такой же как:
Поскольку котлинский знает , что
people
есть, и чтоpeople.age
это ,Int
следовательно , выражение фильтра допускает только сравнение кInt
, и чтоpeople.name
это ,String
следовательно,map
шаг производитList<String>
(только для чтенияList
изString
).Теперь, если бы
people
было возможноnull
, как-тоList<People>?
тогда:Возвращает a
List<String>?
, который должен быть проверен на нуль ( или использовать один из других операторов Kotlin для значений Nullable, см. Этот идиоматический способ Kotlin для работы со значениями Nullable, а также Idiomatic способ обработки пустых или пустых списков в Kotlin )Смотрите также:
источник
Для дополнительных примеров, вот все примеры из Java 8 Stream Tutorial, преобразованные в Kotlin. Название каждого примера получено из исходной статьи:
Как работают потоки
Различные виды потоков # 1
или создайте в String функцию расширения с именем ifPresent:
Смотрите также:
apply()
функцияСмотрите также: Функции расширения
См. Также:
?.
Оператор безопасного вызова и вообще обнуляемость: в Kotlin, каков идиоматический способ иметь дело со значениями, допускающими обнуляемость, ссылаться на них или преобразовывать ихРазличные виды потоков # 2
Различные виды потоков № 3
Различные виды потоков # 4
Различные виды потоков № 5
Различные виды потоков № 6
Различные виды потоков # 7
Почему порядок имеет значение
Этот раздел учебника по Java 8 Stream одинаков для Kotlin и Java.
Повторное использование потоков
В Kotlin, это зависит от типа коллекции, может ли она быть использована более одного раза. A
Sequence
генерирует новый итератор каждый раз, и если он не утверждает «использовать только один раз», он может сбрасываться к началу при каждом действии. Поэтому пока следующее не работает в потоке Java 8, но работает в Kotlin:И в Java, чтобы получить то же поведение:
Поэтому в Котлине провайдер данных решает, сможет ли он сбросить данные и предоставить новый итератор или нет. Но если вы хотите преднамеренно ограничить
Sequence
одноразовую итерацию, вы можете использоватьconstrainOnce()
функциюSequence
следующим образом:Расширенные операции
Соберите пример № 5 (да, я пропустил те, что уже были в другом ответе)
И, как примечание, в Kotlin мы можем создавать простые классы данных и создавать тестовые данные следующим образом:
Соберите пример № 6
Хорошо, более интересный случай здесь для Котлина. Сначала неправильные ответы, чтобы исследовать варианты создания
Map
из коллекции / последовательности:А теперь верный ответ:
Нам просто нужно было объединить совпадающие значения, чтобы свернуть списки и предоставить преобразователь
jointToString
для перехода отPerson
экземпляра кPerson.name
.Соберите пример № 7
Ладно, это легко сделать без кастомов
Collector
, так что давайте решим это способом Kotlin, а затем создадим новый пример, который показывает, как выполнить аналогичный процесс, дляCollector.summarizingInt
которого изначально не существует в Kotlin.Я не виноват, что они выбрали тривиальный пример !!! Хорошо, вот новый
summarizingInt
метод для Kotlin и соответствующий образец:SummarizingInt Пример
Но лучше создать функцию расширения 2, чтобы на самом деле соответствовать стилям в Kotlin stdlib:
Теперь у вас есть два способа использовать новые
summarizingInt
функции:И все они дают одинаковые результаты. Мы также можем создать это расширение для работы
Sequence
и для соответствующих примитивных типов.Для забавы сравните код Java JDK с пользовательским кодом Kotlin, необходимым для реализации этого обобщения.
источник
.map { it.substring(1).toInt() }
: как вы знаете, выводимый тип - это сила котлина.В некоторых случаях трудно избежать звонка
collect(Collectors.toList())
или чего-то подобного. В этих случаях вы можете быстрее перейти на эквивалент Kotlin, используя такие функции расширения, как:Тогда вы можете просто
stream.toList()
илиstream.asSequence()
вернуться обратно в Kotlin API. Например,Files.list(path)
выStream
можете столкнуться с ситуацией, когда вам это может не понадобиться, и эти расширения помогут вам вернуться к стандартным коллекциям и API Kotlin.источник
Больше на лени
Давайте возьмем пример решения для «Вычислить сумму зарплат по отделам», данное Джейсоном:
Чтобы сделать это ленивым (т. Е. Избежать создания промежуточной карты на
groupBy
шаге), использование невозможноasSequence()
. Вместо этого мы должны использоватьgroupingBy
иfold
операцию:Для некоторых людей это может быть даже более читабельным, поскольку вы не имеете дела с записями на карте:
it.value
часть решения сначала меня тоже смутила.Поскольку это частый случай, и мы бы предпочли не записывать
fold
каждый раз, может быть, лучше просто предоставить обобщеннуюsumBy
функцию дляGrouping
:так что мы можем просто написать:
источник