分组映射值但键相同
Group map values but keys are same
我有一张这样的地图。 Map<long,List<Student>> studentMap
键是数字 1,2,3,4...
学生对象是:
public class Student {
private long addressNo;
private String code;
private BigDecimal tax;
private String name;
private String city;
// getter and setters`
}
我想做的是转换它 Map<long,List<StudentInfo>> studentInfoMap
对象和组 ID、addressNo 和代码 fields.I 两个地图的密钥相同。
我可以使用这些代码对地图进行分组,但是 summingDouble 不适用于 BigDecimal.Also 我无法将我的 studentMap 转换为 studentInfoMap。:(
studentInfoMap.values().stream()
.collect(
Collectors.groupingBy(StudentInfo::getCode,
Collectors.groupingBy(StudentInfo::getAddressNo,
Collectors.summingDouble(StudentInfo::getTax))));
我的 studentInfo 对象是:
public class StudentInfo {
private long addressNo;
private String code;
private BigDecimal tax;
// getter and setters`
}
Map<Integer, Object> map = new HashMap<>();
map.put(1, studentInfoMap.values().stream().map(
student -> student.getAddressNo()
).collect(Collectors.toList()));
map.put(2, studentInfoMap.values().stream().map(
student -> student.getCode()
).collect(Collectors.toList()));
// and so ...
要将映射从 Student 转换为 StudentInfo,同时保留相同的键,您可以这样做:
Set<Long> keys = studentMap.keySet();
List<Long> keylist = new ArrayList<>(keys);
Map<Long, List<StudentInfo>> studentInfoMap = new HashMap<>();
for(int i = 0; i < keys.size(); i++){
long key = keylist.get(i);
List<Student> list = studentMap.get(key);
List<StudentInfo> result = new ArrayList<>();
// Create the new StudentInfo object with your values
list.forEach(s -> result.add(new StudentInfo(s.name())));
// Put them into the new Map with the same keys
studentInfoMap.put(key, result);
}
对于从 Student 到 StudentInfo 的 one-to-one 转换:
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:
The following will classify Person objects by city:
Map<String, List<Person>> peopleByCity
= personStream.collect(Collectors.groupingBy(Person::getCity));
The following will classify Person objects by state and city, cascading two Collectors together:
Map<String, Map<String, List<Person>>> peopleByStateAndCity
= personStream.collect(Collectors.groupingBy(Person::getState,
Collectors.groupingBy(Person::getCity)));
请注意最后一个示例如何生成一个 Map
并将另一个 Map
作为其值。
现在,summingDouble
超过 StudentInfo::getTax 生成 BigDecimal
,而不是地图。替换为 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))
));
这意味着您现在有一个由复合键引用的嵌套地图。 Student
具有完全相同的 addressNo、code 和 tax 的 s 将成为每个此类键引用的列表的一部分。
编辑:保留原始密钥
同样的,如果你想保留原来的键,你可以将它们添加到复合键中,或者类似于上面:
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)))
));
我有一张这样的地图。 Map<long,List<Student>> studentMap
键是数字 1,2,3,4... 学生对象是:
public class Student {
private long addressNo;
private String code;
private BigDecimal tax;
private String name;
private String city;
// getter and setters`
}
我想做的是转换它 Map<long,List<StudentInfo>> studentInfoMap
对象和组 ID、addressNo 和代码 fields.I 两个地图的密钥相同。
我可以使用这些代码对地图进行分组,但是 summingDouble 不适用于 BigDecimal.Also 我无法将我的 studentMap 转换为 studentInfoMap。:(
studentInfoMap.values().stream()
.collect(
Collectors.groupingBy(StudentInfo::getCode,
Collectors.groupingBy(StudentInfo::getAddressNo,
Collectors.summingDouble(StudentInfo::getTax))));
我的 studentInfo 对象是:
public class StudentInfo {
private long addressNo;
private String code;
private BigDecimal tax;
// getter and setters`
}
Map<Integer, Object> map = new HashMap<>();
map.put(1, studentInfoMap.values().stream().map(
student -> student.getAddressNo()
).collect(Collectors.toList()));
map.put(2, studentInfoMap.values().stream().map(
student -> student.getCode()
).collect(Collectors.toList()));
// and so ...
要将映射从 Student 转换为 StudentInfo,同时保留相同的键,您可以这样做:
Set<Long> keys = studentMap.keySet();
List<Long> keylist = new ArrayList<>(keys);
Map<Long, List<StudentInfo>> studentInfoMap = new HashMap<>();
for(int i = 0; i < keys.size(); i++){
long key = keylist.get(i);
List<Student> list = studentMap.get(key);
List<StudentInfo> result = new ArrayList<>();
// Create the new StudentInfo object with your values
list.forEach(s -> result.add(new StudentInfo(s.name())));
// Put them into the new Map with the same keys
studentInfoMap.put(key, result);
}
对于从 Student 到 StudentInfo 的 one-to-one 转换:
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:
The following will classify Person objects by city:
Map<String, List<Person>> peopleByCity = personStream.collect(Collectors.groupingBy(Person::getCity));
The following will classify Person objects by state and city, cascading two Collectors together:
Map<String, Map<String, List<Person>>> peopleByStateAndCity = personStream.collect(Collectors.groupingBy(Person::getState, Collectors.groupingBy(Person::getCity)));
请注意最后一个示例如何生成一个 Map
并将另一个 Map
作为其值。
现在,summingDouble
超过 StudentInfo::getTax 生成 BigDecimal
,而不是地图。替换为 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))
));
这意味着您现在有一个由复合键引用的嵌套地图。 Student
具有完全相同的 addressNo、code 和 tax 的 s 将成为每个此类键引用的列表的一部分。
编辑:保留原始密钥
同样的,如果你想保留原来的键,你可以将它们添加到复合键中,或者类似于上面:
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)))
));