对于从 Student 到 StudentInfo 的一对一转换:
class StudentInfo {
public static StudentInfo of(Student student) {
StudentInfo si = new StudentInfo();
si.setAddressNo(student.getAddressNo());
si.setCode(student.getCode());
si.setTax(student.getTax());
return si;
}
}
从一个Map 转换为另一个:
Map<Long,List<Student>> studentMap = ...
Map<Long,List<StudentInfo>> studentInfoMap = studentMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, //same key
entry -> entry.getValue().stream()
.map(StudentInfo::of) //conversion of Student to StudentInfo
.collect(Collectors.toList()) //or simply `.toList()` as of Java 16
));
现在你的分组....
来自java.util.stream.Stream<T> public abstract <R, A> R collect(java.util.stream.Collector<? super T, A, R> collector) 的 JavaDoc:
下面会按城市对 Person 对象进行分类:
Map<String, List<Person>> peopleByCity
= personStream.collect(Collectors.groupingBy(Person::getCity));
以下将按州和城市对 Person 对象进行分类,将两个 Collector 级联在一起:
Map<String, Map<String, List<Person>>> peopleByStateAndCity
= personStream.collect(Collectors.groupingBy(Person::getState,
Collectors.groupingBy(Person::getCity)));
请注意最后一个示例如何生成一个 Map 和另一个 Map 作为其值。
现在,summingDouble 通过 StudentInfo::getTax 生成 BigDecimal,而不是 Map。替换为groupingBy 将有助于对getTax 具有相同数量的学生进行分类:
Map<String, Map<Long, Map<BigDecimal, List<StudentInfo>>>> summary =
studentInfoMap.values().stream()
.flatMap(List::stream) //YOU ALSO NEED THIS
.collect(
Collectors.groupingBy(StudentInfo::getCode,
Collectors.groupingBy(StudentInfo::getAddressNo,
Collectors.groupingBy(StudentInfo::getTax)))
);
编辑:保留 1,2,3,4 原始键
要保留原始键,您可以迭代或流式传输原始 entrySet,其中包含键和值:
Map<Long,Map<String, Map<Long, Map<BigDecimal, List<StudentInfo>>>>> summaryWithKeys =
studentInfoMap.entrySet().stream() //NOTE streaming the entrySet not just values
.collect(
Collectors.toMap(Map.Entry::getKey, //Original Key with toMap
entry -> entry.getValue().stream() //group the value-List members
.collect(Collectors.groupingBy(StudentInfo::getCode,
Collectors.groupingBy(StudentInfo::getAddressNo,
Collectors.groupingBy(StudentInfo::getTax))))
));
作为一个练习,如果你想要一个平面地图 (Map<MyKey,List>),你需要一个复合键 MyKey
根据我的评论,如果您希望拥有一个单一的平面地图,您可以设计一个复合键,这需要同时实现 equals() 和 hashCode() 来收缩。例如,这是 Lombok 将为 StudentInfo 生成的内容(是的,它更容易依赖 lombok 并使用 @EqualsAndHashCode):
public boolean equals(final Object o) {
if(o == this) return true;
if(!(o instanceof StudentInfo)) return false;
final StudentInfo other = (StudentInfo) o;
if(!other.canEqual((Object) this)) return false;
if(this.getAddressNo() != other.getAddressNo()) return false;
final Object this$code = this.getCode();
final Object other$code = other.getCode();
if(this$code == null ? other$code != null : !this$code.equals(other$code)) return false;
final Object this$tax = this.getTax();
final Object other$tax = other.getTax();
if(this$tax == null ? other$tax != null : !this$tax.equals(other$tax)) return false;
return true;
}
protected boolean canEqual(final Object other) {return other instanceof StudentInfo;}
public int hashCode() {
final int PRIME = 59;
int result = 1;
final long $addressNo = this.getAddressNo();
result = result * PRIME + (int) ($addressNo >>> 32 ^ $addressNo);
final Object $code = this.getCode();
result = result * PRIME + ($code == null ? 43 : $code.hashCode());
final Object $tax = this.getTax();
result = result * PRIME + ($tax == null ? 43 : $tax.hashCode());
return result;
}
然后您可以使用 StudentInfo 作为复合键,如下所示:
Map<Long, List<Student>> studentMap = ...
Map<StudentInfo,List<Student>>> summaryMap = studentMap.values().stream()
.collect(Collectors.groupingBy(StudentInfo::of))
));
这意味着您现在有一个由复合键引用的嵌套映射。 Students 具有完全相同的地址编号、代码和税金将成为每个此类键引用的列表的一部分。
编辑:保留原始密钥
同样,如果您想保留原始密钥,您可以将它们添加到复合密钥中,或与上述类似:
Map<Long, List<Student>> studentMap = ...
Map<Long, Map<StudentInfo,List<Student>>>> summaryMap = studentMap.entrySet().stream()
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.groupingBy(StudentInfo::of)))
));