我有一个对象列表,如下所示:
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相同,那些对象实际上并不相同,它们只需要在这种特殊情况下组合。而且它不是很简洁。 还有更好的方法吗?
答案 0 :(得分:2)
你的确可以更简洁。
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关联,则将其与给定的非空值相关联。
否则,将相关值替换为给定重映射函数MyObject::merge
的结果,或者如果结果为null则删除。
完成后,我们收集地图值并将其返回到ArrayList
实例。
注意上面的MyObject::merge
函数假设merge
在public static MyObject merge (MyObject obj1, MyObject obj2) {...}
类中定义为MyObject
。
答案 1 :(得分:1)
对集合对象使用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());
}
P.S。关注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);
}
}
答案 2 :(得分:1)
您可以这样做,而不依赖于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;
}
答案 3 :(得分:1)
我们可以使用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());
}
注意:省略了空检查。
答案 4 :(得分:0)
你的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());
}
在这里,您将使用the overload of Collectors.toMap
that takes a key mapper, a value mapper, and a merge function。我假设merge
所在的班级是Main
。然后,您可以将值转换回List
。