Продолжая читать « Функциональное программирование в Java » Венката Субраманиама, я дошел до той части книги, где представлена функция Stream # collect .
Мы хотим собрать коллекцию людей, сгруппировать их по возрасту и вернуть карту (возраст -> имена людей), для которой это пригодится.
Чтобы обновить, вот как выглядит класс Person:
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
 | 
static class Person {    private String name;    private int age;    Person(String name, int age) {        this.name = name;        this.age = age;    }    @Override    public String toString() {        return String.format("Person{name='%s', age=%d}", name, age);    }} | 
И мы можем написать следующий код в Java 8, чтобы получить карту имен людей, сгруппированных по возрасту:
| 
 1 
2 
3 
4 
 | 
Stream<Person> people = Stream.of(new Person("Paul", 24), new Person("Mark", 30), new Person("Will", 28));Map<Integer, List<String>> peopleByAge = people    .collect(groupingBy(p -> p.age, mapping((Person p) -> p.name, toList())));System.out.println(peopleByAge); | 
| 
 1 
 | 
{24=[Paul], 28=[Will], 30=[Mark]} | 
Мы запускаем функцию «собирать» над коллекцией, группируя по мере необходимости свойство «возраст» и группируя имена людей, а не самих людей.
Это немного отличается от того, что вы делаете в Ruby, где есть функция group_by, которую вы можете вызывать для коллекции:
| 
 1 
2 
3 
 | 
> people = [ {:name => "Paul", :age => 24}, {:name => "Mark", :age => 30}, {:name => "Will", :age => 28}]> people.group_by { |p| p[:age] }=> {24=>[{:name=>"Paul", :age=>24}], 30=>[{:name=>"Mark", :age=>30}], 28=>[{:name=>"Will", :age=>28}]} | 
Это возвращает нам списки людей, сгруппированных по возрасту, но нам нужно применить дополнительную операцию «карта», чтобы вместо этого изменить список имен:
| 
 1 
2 
 | 
> people.group_by { |p| p[:age] }.map { |k,v| [k, v.map { |person| person[:name] } ] }=> [[24, ["Paul"]], [30, ["Mark"]], [28, ["Will"]]] | 
На этом этапе у нас есть массив пар (age, names), но, к счастью, в Ruby 2.1.0 есть функция to_h, которую мы можем вызвать, чтобы снова вернуться к хешу:
| 
 1 
2 
 | 
> people.group_by { |p| p[:age] }.map { |k,v| [k, v.map { |person| person[:name] } ] }.to_h=> {24=>["Paul"], 30=>["Mark"], 28=>["Will"]} | 
Если мы хотим следовать Java-подходу к группированию по свойству при выполнении приведения к коллекции, у нас будет что-то вроде следующего:
| 
 1 
2 
 | 
> people.reduce({}) { |acc, item| acc[item[:age]] ||=[]; acc[item[:age]] << item[:name]; acc }=> {24=>["Paul"], 30=>["Mark"], 28=>["Will"]} | 
Если мы используем Clojure, мы можем получить что-то вроде этого:
| 
 1 
2 
3 
4 
5 
 | 
(def people  [{:name "Paul", :age 24} {:name "Mark", :age 30} {:name "Will", :age 28}])> (reduce (fn [acc [k v]] (assoc-in acc [k] (map :name v))) {} (group-by :age people)){28 ("Will"), 30 ("Mark"), 24 ("Paul")} | 
Я думал, что версия на Java выглядела немного странно с самого начала, но на самом деле это не так уж и плохо, если разобраться с проблемой в нескольких других языках.
Было бы хорошо узнать, есть ли лучший способ сделать это с помощью Ruby / Clojure!