![](/img/trans.png)
[英]What is a efficient way to condense a List of objects to based on an object value?
[英]Condense a list of objects if a property on the object is the same
我有一个对象列表,如下所示:
public class MyObject {
String id;
List<String> list;
MyObject(String id, List<String> list) {
this.id = id;
this.list = list;
}
String getId() {
return id;
}
List<String> getList() {
return list;
}
}
我想压缩列表,以便如果两个MyObject具有相同的id,它们将合并为一个。
我能想到的最好的解决方案是覆盖MyObject中的equals方法,这样如果它们的id相同,则2个对象是相同的,并执行如下操作:
List<MyObject> condense(List<MyObject> fullList) {
List<MyObject> condensedList = new ArrayList<>();
for (MyObject obj : fullList) {
if (condensedList.contains(obj)) {
MyObject obj2 = condensed.get(condensed.indexOf(obj));
obj = merge(obj, obj2);
}
condensedList.add(obj);
}
return condensedList;
}
MyObject merge(MyObject obj1, MyObject obj2) {
List<String> merged = new ArrayList<>();
merged.addAll(obj1.getList());
merged.addAll(obj2.getList());
return new MyObject(obj1.getId(), merged);
}
这不是一个很好的解决方案,因为如果它们的ID是相同的那些对象并不是真的相等,它们只需要在这种特殊情况下组合。 而且它不是很简洁。 有没有更好的办法?
你确实可以使这更简洁。
List<MyObject> condense(List<MyObject> fullList) {
Map<String, MyObject> tempMap = new HashMap<>();
fullList.forEach(a -> tempMap.merge(a.getId(), a, MyObject::merge));
return new ArrayList<>(tempMap.values());
}
这使用Map :: merge来执行工作。 Map :: merge基本上表示如果指定的键a.getId()
尚未与值关联或与null关联,则将其与给定的非null值相关联。
否则,将相关值替换为给定重映射函数MyObject::merge
的结果,或者如果结果为null则删除。
完成后,我们收集映射值并将其返回到ArrayList
实例中。
注意上面的MyObject::merge
函数假定merge
在MyObject
类中定义为public static MyObject merge (MyObject obj1, MyObject obj2) {...}
。
对集合对象使用Map
:
public static List<MyObject> condense(List<MyObject> objs) {
objs = Optional.ofNullable(objs).orElse(Collections.emptyList());
Map<String, MyObject> map = new LinkedHashMap<>();
objs.stream().map(MyObject::getId).distinct().forEach(id -> map.put(id, new MyObject(id)));
objs.forEach(obj -> map.get(obj.getId()).addList(obj.getList()));
return new ArrayList<>(map.values());
}
PS注意MyObject
封装:
public final class MyObject {
private final String id;
private final List<String> list = new ArrayList<>();
public MyObject(String id) {
this.id = id;
}
public String getId() {
return id;
}
public List<String> getList() {
return list.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(list);
}
public void addList(List<String> list) {
this.list.addAll(list);
}
}
您可以这样做,而不依赖于equals
方法。
List<MyObject> condense(List<MyObject> fullList) {
// LinkedHashMap preserves the order of the original list
// To make the result sorted by ID, use TreeMap instead
Map<String, List<String>> lists = new LinkedHashMap<>();
for (MyObject o : fullList) {
List<String> list = lists.get(o.getId());
if (list == null) {
list = new ArrayList<>(o.getList());
lists.put(o.getId(), list);
} else {
list.addAll(o.getList());
}
}
List<MyObject> objects = new ArrayList<>(lists.size());
for (Map.Entry<String, List<String>> e : lists.entrySet()) {
objects.add(new MyObject(e.getKey(), e.getValue()));
}
return objects;
}
我们可以使用Map的merge()
方法:
List<MyObject> condense(List<MyObject> fullList) {
Map<String, MyObject> map = new LinkedHashMap<>();
fullList.forEach(myObject -> {
map.merge(myObject.getId(), myObject, (myObject1, myObject2) -> {
myObject1.getList().addAll(myObject2.getList());
return myObject1;
});
});
return new ArrayList(map.values());
}
注意:省略了null检查。
你的merge
方法很好,虽然我会用一个大小来初始化它,以防止不必要的ArrayList调整大小。
static MyObject merge (MyObject obj1, MyObject obj2) {
List<String> list1 = obj1.getList();
List<String> list2 = obj2.getList();
List<String> merged = new ArrayList<>(list1.size() + list2.size());
merged.addAll(list1);
merged.addAll(list2);
return new MyObject(obj1.getId(), merged);
}
接下来,将List
流式传输到Map
这样您就可以使用合并函数来决定遇到相等键(您的id
)时要执行的操作。 这比搜索列表中的匹配键更有效。
public List<MyObject> condense(List<MyObject> fullList) {
Map<String, MyObject> temp = fullList.stream()
.collect(Collectors.toMap(MyObject::getId, Function.identity(), Main::merge));
return new ArrayList<>(temp.values());
}
在这里,您将使用Collectors.toMap
的重载,该重载采用键映射器,值映射器和合并函数 。 我假设merge
的类是Main
。 然后,您可以将值转换回List
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.