如果对象上的属性相同,则压缩对象列表

时间:2018-06-13 18:41:52

标签: java

我有一个对象列表,如下所示:

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相同,那些对象实际上并不相同,它们只需要在这种特殊情况下组合。而且它不是很简洁。 还有更好的方法吗?

5 个答案:

答案 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函数假设mergepublic 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

相关问题