0

我在对 java 对象进行分组时遇到问题。让我们看一下示例对象:

public class MyObject {

    private String field1;

    public MyObject(String field1) {
        this.field1 = field1;
    }
}

我想要实现的是以MyObject这样一种方式对 's 进行分组,即每个组只包含一个具有指定field1值的对象。例如,对于这样的元素列表:

  public static void main(String[] args) {
    
    MyObject o1 = new MyObject("1");
    MyObject o2 = new MyObject("1");
    MyObject o3 = new MyObject("1");

    MyObject o4 = new MyObject("2");
    MyObject o5 = new MyObject("2");

    MyObject o6 = new MyObject("3");

    List<MyObject> list = Arrays.asList(o1, o2, o3, o4, o5, o6);
    List<List<MyObject>> listsWithUniqueField1Values = new ArrayList<>();

我想要listsWithUniqueField1Values看起来像这样:

[
    [
        MyObject{field1='1'}, 
        MyObject{field1='2'}, 
        MyObject{field1='3'}
    ], 
    [   
        MyObject{field1='1'}, 
        MyObject{field1='2'}
    ], 
    [
        MyObject{field1='1'}
    ]
]

我试图通过使用java.util.stream.Collectors.groupingBy方法以有效的方式实现它,但我失败了。

4

4 回答 4

0

您可以使用 agroupingBy本身来执行此操作。(不需要equalsor hashCode

  1. 第一组使用field1. 这将给出一个地图:
{ 1 : [1,1,1], 2 : [2,2], 3 : [3] }
  1. 现在,对于这些键中的每一个,迭代它们各自的列表并将每个列表添加MyObjectlistsWithUniqueField1Values.

一个。第一次处理 key 1,列表变为[[1]]-> [[1], [1]]-> [[1], [1], [1]]

湾。然后 key 2,列表变为[[1,2], [1], [1]]-> [[1,2], [1,2], [1]]

C。for 键3,列表变为[[1,2,3], [1,2], [1]]

代码 :

List<List<MyObject>> uniqueList = new ArrayList<>();
list.stream()
    .collect(Collectors.groupingBy(MyObject::getField1))
    .values()
    .stream()
    .forEach(values -> addToList(uniqueList, values));
    
return uniqueList;

下面的方法addToList是填充唯一列表的地方。 在这种情况下ListIterator使用 over ,因为方法在 .IteratoraddListIterator

private static void addToList(List<List<MyObject>> uniqueList, List<MyObject> values) {
    ListIterator<List<MyObject>> iterator = uniqueList.listIterator();
    for (MyObject o : values) {
        List<MyObject> list;
        if (!iterator.hasNext()) {
            // the object needs to be added to a new list.
            list = new ArrayList<>();
            iterator.add(list);
        } else {
            list = iterator.next();
        }
        list.add(o);
    }
}
于 2021-04-25T09:37:24.890 回答
0

我认为您不能使用 groupingBy 来处理它。这是我的解决方案 - 我还添加了自动生成的 equals、hashCode 和 toString

public class SO67140234 {

    public static void main(String[] args) {

        MyObject o1 = new MyObject("1");
        MyObject o2 = new MyObject("1");
        MyObject o3 = new MyObject("1");

        MyObject o4 = new MyObject("2");
        MyObject o5 = new MyObject("2");

        MyObject o6 = new MyObject("3");

        List<MyObject> list = Arrays.asList(o1, o2, o3, o4, o5, o6);
        List<Set<MyObject>> listsWithUniqueField1Values = new ArrayList<>();

        outer:
        for (MyObject obj : list) {
            for (Set<MyObject> bucket : listsWithUniqueField1Values) {
                if (bucket.add(obj)) {
                    continue outer;
                }
            }
            listsWithUniqueField1Values.add(new HashSet<>(Collections.singleton(obj)));
        }

        System.out.println(listsWithUniqueField1Values);
    }

}

class MyObject {

    private final String field1;

    public MyObject(String field1) {
        this.field1 = field1;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyObject myObject = (MyObject) o;
        return Objects.equals(field1, myObject.field1);
    }

    @Override
    public int hashCode() {
        return Objects.hash(field1);
    }

    @Override
    public String toString() {
        return "MyObject{" +
            "field1='" + field1 + '\'' +
            '}';
    }
}
于 2021-04-17T16:31:39.833 回答
0

假设MyObject有一个吸气剂,我能想到的最简单的方法之一就是结合

  • Collectors.collectingAndThen
  • Collectors.groupingBy
  • 链表
  • 从 LinkedList 中弹出项目并将它们插入结果中的方法
List<List<MyObject>> finalResult = list.stream()
        .collect(Collectors.collectingAndThen(
                Collectors.groupingBy(MyObject::getField1, Collectors.toCollection(LinkedList::new)),
                map -> {
                    List<List<MyObject>> result = new ArrayList<>();
                    Collection<LinkedList<MyObject>> values = map.values();
                    while (!values.isEmpty()) {
                        List<MyObject> subList = values.stream()
                                .map(LinkedList::pop)
                                .toList();
                        result.add(subList);
                        values.removeIf(LinkedList::isEmpty);
                    }
                    return result;
                }));

结果是

[
  [
    MyObject{field1='1'}, 
    MyObject{field1='2'}, 
    MyObject{field1='3'}
  ], 
  [
    MyObject{field1='1'}, 
    MyObject{field1='2'}
  ], 
  [
    MyObject{field1='1'}
  ]
]
于 2021-04-25T10:01:52.380 回答
0

为了按实例分组MyObject,这个类需要实现equalshashCode方法,也field1应该是最终的,以避免hashCode在更改其值时损坏。

public class MyObject {

    private final String field1;

    public MyObject(String field1) {
        this.field1 = field1;
    }

    public String getField1() {return this.field1;}

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (null == o || !(o instanceof MyObject)) return false;
        MyObject that = (MyObject) o;
        return Objects.equals(this.field1, that.field1);
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.field1);
    }

    @Override
    public String toString() {
        return "field1=" + this.field1;
    }
}

Collectors.groupingBy不能用于获得所需的结果,但Stream::collect可以应用自定义操作来创建唯一MyObject实例集的列表(有点提醒@Rubydesic 的解决方案,但没有嵌套循环)。

List<MyObject> list = Arrays.asList(o1, o4, o5, o2, o6, o3);

List<Set<MyObject>> result = list.stream()
    .collect(
        ArrayList::new, //  `Supplier<ArrayList<Set<>>>`
        (lst, x) -> {   // accumulator
            for (Set<MyObject> set : lst) { 
                if (set.add(x)) {
                    return; // found a bucket to place MyObject instance
                }
            }
            // create new bucket 
            Set<MyObject> newSet = new HashSet<>(); 
            newSet.add(x); 
            lst.add(newSet);
        },
        (lst1, lst2) -> {} // empty combiner
    );

    System.out.println(result);

输出 :

[[field1=1, field1=2, field1=3], [field1=1, field1=2], [field1=1]]
于 2021-04-17T16:31:57.757 回答