#java #java-8 #java-stream #groupingby
Вопрос:
У меня есть простой урок Person
:
class Person { String firstName; String lastName; //getter, setter, constructor, toString }
И список входных Persons
данных, таких как:
Listlt;Persongt; myList = List.of( new Person("Helena", "Graves"), new Person("Jasmine", "Knight"), new Person("Phoebe", "Reyes"), new Person("Aysha", "Graham"), new Person("Madeleine", "Jenkins"), new Person("Christina", "Johnson"), new Person("Melissa", "Carpenter"), new Person("Marie", "Daniel"), new Person("Robin", "French"), new Person("Tamara", "Wyatt"), new Person("Freya", "Montgomery"), new Person("Lacey", "Todd"), new Person("Heather", "Parker"), new Person("Lauren", "Wright"), new Person("Annie", "Bradley") );
Теперь мне нужно сгруппировать приведенный выше список по первому символу фамилий человека и снова сгруппировать группы таким образом, чтобы все фамилии, начинающиеся между A-H
ними, попадали в одну группу, в следующую группу для тех, которые начинаются с I-N
и, наконец, O-Z
с.
Я уже могу сгруппировать список по первому символу фамилии:
myList.stream() .collect(Collectors.groupingBy(p -gt; String.valueOf(p.getLastName().charAt(0)))) .entrySet() .forEach(System.out::println);
Что дает мне :
P=[Person{Heather, Parker}] B=[Person{Annie, Bradley}] R=[Person{Phoebe, Reyes}] C=[Person{Melissa, Carpenter}] T=[Person{Lacey, Todd}] D=[Person{Marie, Daniel}] F=[Person{Robin, French}] W=[Person{Tamara, Wyatt}, Person{Lauren, Wright}] G=[Person{Helena, Graves}, Person{Aysha, Graham}] J=[Person{Madeleine, Jenkins}, Person{Christina, Johnson}] K=[Person{Jasmine, Knight}] M=[Person{Freya, Montgomery}]
Возникли трудности с тем, как действовать дальше, так как мне нужно еще больше обобщить вышесказанное, чтобы получить карту с тремя записями/ключами. Желаемый результат:
Maplt;String, Listlt;Persongt;gt; result = ... A-H = [Person{Helena, Graves}, Person{Aysha, Graham}, Person{Melissa, Carpenter}, Person{Marie, Daniel}, Person{Robin, French}, Person{Annie, Bradley}] I-N = [Person{Jasmine, Knight}, Person{Madeleine, Jenkins}, Person{Christina, Johnson}, Person{Freya, Montgomery}] O-Z = [Person{Phoebe, Reyes}, Person{Tamara, Wyatt}, Person{Lacey, Todd}, Person{Heather, Parker}, Person{Lauren, Wright}]
Ответ №1:
Вам следует просто немного изменить функцию классификатора, чтобы объединить ряд символов.
Кроме того, может потребоваться отсортировать набор записей() (или использовать SortedMap
/ TreeMap
при сборе на карту):
myList.stream() .collect(Collectors.groupingBy( p -gt; p.getLastName().charAt(0) lt; 'I' ? "A-H" : p.getLastName().charAt(0) lt; 'O' ? "I-N" : "O-Z" )) .entrySet() .stream() .sorted(Map.Entry.comparingByKey()) .forEach(System.out::println);
Выход:
A-H=[Person {Helena Graves}, Person {Aysha Graham}, Person {Melissa Carpenter}, Person {Marie Daniel}, Person {Robin French}, Person {Annie Bradley}] I-N=[Person {Jasmine Knight}, Person {Madeleine Jenkins}, Person {Christina Johnson}, Person {Freya Montgomery}] O-Z=[Person {Phoebe Reyes}, Person {Tamara Wyatt}, Person {Lacey Todd}, Person {Heather Parker}, Person {Lauren Wright}]
Комментарии:
1. Большое спасибо. Не знал, что могу добавить условие
groupingBy
. Усвоил новый урок. Спасибо.
Ответ №2:
В принципе, все, что вам нужно, — это группировка с использованием Collectors.groupBy(Function)
и функция, которая назначает каждую Person
из них в правильную группу:
/** * This method is null-friendly */ String group(Person person) { return Optional.ofNullable(person) .map(Person::getFirstName) .filter(name -gt; name.length() gt; 0) .map(name -gt; name.charAt(0)) .map(ch -gt; { if (ch gt;= 'A' amp;amp; ch lt;= 'H') { return "A-H"; } else if (ch gt; 'H' amp;amp; ch lt;= 'N') { return "I-N"; } else if (ch gt; 'N' amp;amp; ch lt;= 'Z') { return "O-Z"; } return "*"; // In case the name starts with a character out of A-Z range }) .orElse("none"); // In case there is empty/null firstName }
Maplt;String, Listlt;Persongt;gt; map = myList .stream() .collect(Collectors.groupingBy(this::group));
Комментарии:
1. Большое спасибо. Это работает, за исключением одной небольшой проблемы, но, скорее всего, это моя вина. Я получаю ошибку компиляции, на
this::group
которую нельзя ссылаться из статического контекста. Я заставляю это работать с помощью лямбдыp -gt; group(p)
2. Если вы запускаете поток в
static
методе, то методgroup
static
также должен быть:static String group (Person person) { ... }
. Затем в потоке вы также можете использовать ссылку на метод (приFoo
условии, что класс, в котором находятся методы):Collectors.groupingBy(Foo::group)