Подтвердить что ты не робот

Преобразование потока List в единый контейнер

Рассмотрим ниже WorkExperience class

public class WorkExperience {
    private int year;
    private List<Skills> skill;

    public WorkExperience(int year, List<Skills> skill) {
        this.year = year;
        this.skill = skill;
    }   
    //getter setter         
}

public class Skills {
    private String skills;

    public Skills(String skills) {
        this.skills = skills;
    }

    @Override
    public String toString() {
        return "Skills [skills=" + skills + "]";
    }
}     

скажем, я хочу группировать свои навыки по годам: Так мы можем делать groupBy по годам.

public static void main(String[] args) {

        List<Skills> skillSet1 = new  ArrayList<>();
        skillSet1.add(new Skills("Skill-1"));
        skillSet1.add(new Skills("Skill-2"));
        skillSet1.add(new Skills("Skill-3"));

        List<Skills> skillSet2 = new  ArrayList<>();
        skillSet2.add(new Skills("Skill-1"));
        skillSet2.add(new Skills("Skill-4"));
        skillSet2.add(new Skills("Skill-2"));


        List<Skills> skillSet3 = new  ArrayList<>();
        skillSet3.add(new Skills("Skill-1"));
        skillSet3.add(new Skills("Skill-9"));
        skillSet3.add(new Skills("Skill-2"));

        List<WorkExperience> workExperienceList = new ArrayList<>();
        workExperienceList.add(new WorkExperience(2017,skillSet1));
        workExperienceList.add(new WorkExperience(2017,skillSet2));
        workExperienceList.add(new WorkExperience(2018,skillSet3));

        Map<Integer, Set<List<Skills>>> collect = workExperienceList.stream()
                           .collect(Collectors.groupingBy(WorkExperience::getYear,   
                       Collectors.mapping(WorkExperience::getSkill,Collectors.toSet())));
    }

Но groupBy возвращает Map<Integer, Set<List<Skills>>>
    , но на самом деле мне нужно Map<Integer, Set<Skills>>

Как преобразовать поток List в отдельный контейнер?

4b9b3361

Ответ 1

Мы можем использовать Collectors.flatMapping сборник, добавленный в Java-9.. Используя flatMapping, мы можем сгладить промежуточные списки в один контейнер. flatMapping может использоваться в случаях, когда элементы исходного потока конвертируются в поток.

workExperienceList.stream().collect(Collectors.groupingBy(
                              WorkExperience::getYear, 
                              Collectors.flatMapping(workexp -> workexp.getSkill().stream(), 
                                             Collectors.toSet())));

Примечание API:

Коллекторы flatMapping() наиболее полезны при использовании в многоуровневом восстановлении, например, ниже по течению от groupingBy или partitioningBy.

Ответ 2

Альтернативой flatMapping, использующей только функции Java 8, будет

Map<Integer, Set<Skills>> map = workExperienceList.stream()
    .collect(Collectors.toMap(
        WorkExperience::getYear,
        we -> new HashSet<>(we.getSkill()),
        (s1, s2)-> { s1.addAll(s2); return s1; }));

вы можете немного оптимизировать это

Map<Integer, Set<Skills>> map = workExperienceList.stream()
    .collect(Collectors.toMap(
        WorkExperience::getYear,
        we -> new HashSet<>(we.getSkill()),
        (s1, s2) -> {
            if(s1.size() > s2.size()) { s1.addAll(s2); return s1; }
            else { s2.addAll(s1); return s2; }
        }));

Ответ 3

Еще один способ выполнить то, что вы хотите, - реализовать свой собственный коллекционер с помощью статического метода factory Collector.of():

Map<Integer, Set<Skills>> collect = workExperienceList.stream()
    .collect(Collector.of(
        HashMap::new,
        ( map, e ) -> map.computeIfAbsent(e.getYear(), k -> new HashSet<>()).addAll(e.getSkill()),
        ( left, right ) -> {
            right.forEach(( year, set ) -> left.computeIfAbsent(year, k -> new HashSet<>()).addAll(set));
            return left;
        })
    );

Это довольно грязный и раздутый по сравнению с другими ответами.

Ответ 4

Старайтесь быть более объектно-ориентированными. Поэтому я предполагаю создать новый маленький объект

`public static class AsGroup {
        private final Integer year;
        private final Collection<Skill> skillSet;

        public AsGroup(Integer year, Collection<Skill> skillSet) {

            this.year = year;
            this.skillSet = skillSet;
        }

        public AsGroup addSkills(AsGroup asGroupSkills) {
            this.skillSet.addAll(asGroupSkills.skillSet);
            return this;
        }
    }`

И тогда вы можете решить свою проблему: `

 Map<Integer, Optional<com.company.Main.AsGroup>> groupedByYear = workExperienceList.stream()
                .map(workExperience ->
                        new AsGroup(workExperience.getYear(), new HashSet<>(workExperience.getSkill()))
                ).collect(groupingBy((asGroup) -> asGroup.year,
                          reducing((group1, group2) -> (group1.addSkills(group2))))
                );

`

Вы можете использовать его, как показано ниже:

groupedByYear.forEach(((year, groupedSkills) -> System.out.println(year + " " + groupedSkills.get().skillSet)));

Он печатает следующее:
`

2017 [Skill [skill=Skill-1], Skill [skill=Skill-1], Skill [skill=Skill-4], Skill [skill=Skill-2], Skill [skill=Skill-2], Skill [skill=Skill-3]]
2018 [Skill [skill=Skill-1], Skill [skill=Skill-2], Skill [skill=Skill-9]]

`