我正在尝试使用员工姓名合并集合。
我的MainDTO
有List<Employee(name, List<Address(String)>)>
Employee
有String name
和List Address
Address
是String
。
MainDTO -> List<Employee> empList;
Employee-> String name, List<String>
我有输入数据:
( ("emp1",[("KY"),"("NY")]),
("em2",[("KY"),"("NY")]),
("emp1",[("MN"),"("FL")]),
("emp1",[("TN"),"("AZ")])
)
输出将是:
( ("emp1",[("KY"),"("NY"),("MN"),"("FL"),("TN"),"("AZ")]),
("em2",[("KY"),"("NY")])
)
使用java 8或java 7对此数据进行排序的最佳方法。
答案 0 :(得分:4)
如果Java 9是一个选项,您可以使用flatMapping:
Map<String, List<String>> addressesByEmployeeName = empList
.stream()
.collect(groupingBy(
Employee::getName, flatMapping(
e -> e.getAddresses().stream(),
toList())));
但我有这种奇怪的感觉Java 9不是一种选择。
答案 1 :(得分:2)
如果您使用具有多重映射的库(例如Eclipse Collections。
),则会变得更加容易ListIterable<Employee> empList = ...;
MutableSortedSetMultimap<String, String> addressesByEmployeeName =
new TreeSortedSetMultimap<>();
empList.each(e -> e.getAddresses().each(a -> addressesByEmployeeName.put(e.getName(), a)));
如果empList
和地址列表无法从List
更改为ListIterable
,那么您可以使用ListAdapter
来实现此功能。
ListAdapter.adapt(empList).each(
e -> ListAdapter.adapt(e.getAddresses()).each(
a -> addressesByEmployeeName.put(e.getName(), a)));
由于此模式有些常见,我们可能会将RichIterable.toMultimap()
添加到库中。然后这个例子将归结为单行。
MutableListMultimap<String, String> addressesByEmployeeName =
empList.toMultimap(Employee::getName, Employee::getAddresses);
注意:我是Eclipse Collections的提交者。
答案 2 :(得分:1)
shmosel's answer很好用,因为它充分利用了新的JDK 9 flatMapping
收集器。如果JDK 9不是一个选项,它可以在JDK 8中做类似的事情,尽管它涉及的更多一些。它在流中执行平面映射操作,从而产生一对员工姓名和地址流。我没有创建特殊的配对类,而是使用AbstractMap.SimpleEntry
。
基本思想是流式传输员工列表并将其平面映射为一对(名称,地址)。完成此操作后,将它们收集到地图中,按名称分组,并将地址收集到每个组的列表中。这是执行此操作的代码:
import static java.util.AbstractMap.SimpleEntry;
import static java.util.Map.Entry;
import static java.util.stream.Collectors.*;
Map<String, List<String>> mergedMap =
empList.stream()
.flatMap(emp -> emp.getAddresses().stream().map(
addr -> new SimpleEntry<>(emp.getName(), addr)))
.collect(groupingBy(Entry::getKey, mapping(Entry::getValue, toList())));
这样会产生Map
。如果要从这些对象创建Employee
个对象,可以对条目进行流式处理以创建它们:
List<Employee> mergedEmps =
mergedMap.entrySet().stream()
.map(entry -> new Employee(entry.getKey(), entry.getValue()))
.collect(toList());
创建地图输入对象并从中提取数据有点麻烦,但并不可怕。如果你愿意,可以将这里的一些习语提取到实用方法中,使事情变得更清晰。
答案 3 :(得分:0)
这是您的问题的可能解决方案 - 如果您想避免重复值,请使用Set;否则,请使用List:
public static void main(String[]args){
final List<Map.Entry<String, List<String>>> inputData = new ArrayList<>();
inputData.add(new AbstractMap.SimpleEntry<String,
List<String>>("emp1", Arrays.asList("KY","NY")));
inputData.add(new AbstractMap.SimpleEntry<String,
List<String>>("emp2", Arrays.asList("KY","NY")));
inputData.add(new AbstractMap.SimpleEntry<String,
List<String>>("emp1", Arrays.asList("MN","FL")));
inputData.add(new AbstractMap.SimpleEntry<String,
List<String>>("emp1", Arrays.asList("MN","FL")));
//If you do not care about duplicates - use List
final Map<String,List<String>> possibleWithDuplicatesData =
inputData.stream()
.collect(Collectors.toMap(Map.Entry::getKey,
entry -> new ArrayList<String>()));
inputData.stream()
.filter(entry -> possibleWithDuplicatesData.containsKey(entry.getKey()))
.forEach(entry->
possibleWithDuplicatesData.get(entry.getKey()).addAll(entry.getValue()));
//If you want to avoid duplicates - use Set
final Map<String,Set<String>> noDuplicatesData =
inputData.stream()
.collect(Collectors.toMap(Map.Entry::getKey,
entry -> new HashSet<String>()));
inputData.stream()
.filter(entry -> noDuplicatesData.containsKey(entry.getKey()))
.forEach(entry->
noDuplicatesData.get(entry.getKey()).addAll(entry.getValue()));
}
答案 4 :(得分:0)
试试这个:
private static class Address {
private String address;
public Address(String address) {
this.address = address;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
private static class Employee {
private String name;
private List<Address> addresses;
public Employee(String name, List<Address> addresses) {
this.name = name;
this.addresses = addresses;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Address> getAddresses() {
return addresses;
}
public void setAddresses(List<Address> addresses) {
this.addresses = addresses;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", addresses=" + addresses +
'}';
}
}
private static class MainDTO {
private List<Employee> employees;
public MainDTO(List<Employee> employees) {
this.employees = employees;
}
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
@Override
public String toString() {
return "MainDTO{" +
"employees=" + employees +
'}';
}
}
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("emp1", Arrays.asList(new Address("KY"), new Address("NY"))));
employees.add(new Employee("emp2", Arrays.asList(new Address("KY"), new Address("NY"))));
employees.add(new Employee("emp1", Arrays.asList(new Address("MN"), new Address("FL"))));
employees.add(new Employee("emp1", Arrays.asList(new Address("TN"), new Address("AZ"))));
MainDTO dto = new MainDTO(employees);
List<Employee> merged = dto.getEmployees().stream()
.collect(Collectors.toMap(Employee::getName,
e -> e,
(l, r) ->
new Employee(l.getName(),
Stream.concat(l.getAddresses().stream(), r.getAddresses().stream())
.collect(Collectors.toList())),
LinkedHashMap::new))
.values()
.stream()
.collect(Collectors.toList());
System.out.println(merged);
}
情侣笔记:
LinkedHashMap用于保留列表的原始顺序。
Stream.concat用于支持Collection.addAll(Collection),因为它们可以说是不可变的。
输出:
[Employee{name='emp1', addresses=[Address{address='KY'}, Address{address='NY'}, Address{address='MN'}, Address{address='FL'}, Address{address='TN'}, Address{address='AZ'}]}, Employee{name='emp2', addresses=[Address{address='KY'}, Address{address='NY'}]}]