将 List 流转换为单个 Container
Convert List stream into single Container
考虑以下 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流转为单个Container?
我们可以使用在Java-9中添加的Collectors.flatMapping
收集器。通过使用flatMapping
,我们可以展平将中间列表放入单个容器中。 flatMapping
可用于原始流的元素可转换为流的情况。
workExperienceList.stream().collect(Collectors.groupingBy(
WorkExperience::getYear,
Collectors.flatMapping(workexp -> workexp.getSkill().stream(),
Collectors.toSet())));
API 注:
The flatMapping() collectors are most useful when used in a multi-level reduction, such as downstream of a groupingBy or partitioningBy.
仅使用 Java 8 个特征的 flatMapping
替代方法是
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; }
}));
另一种实现您想要的方法是使用静态工厂方法实现您自己的收集器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;
})
);
与其他答案相比,这是相当混乱和臃肿的。
尝试更加面向对象。所以我想创建一个新的小对象
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]]
考虑以下 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流转为单个Container?
我们可以使用在Java-9中添加的Collectors.flatMapping
收集器。通过使用flatMapping
,我们可以展平将中间列表放入单个容器中。 flatMapping
可用于原始流的元素可转换为流的情况。
workExperienceList.stream().collect(Collectors.groupingBy(
WorkExperience::getYear,
Collectors.flatMapping(workexp -> workexp.getSkill().stream(),
Collectors.toSet())));
API 注:
The flatMapping() collectors are most useful when used in a multi-level reduction, such as downstream of a groupingBy or partitioningBy.
仅使用 Java 8 个特征的 flatMapping
替代方法是
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; }
}));
另一种实现您想要的方法是使用静态工厂方法实现您自己的收集器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;
})
);
与其他答案相比,这是相当混乱和臃肿的。
尝试更加面向对象。所以我想创建一个新的小对象
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]]