Java 分组元素,每个组仅包含一个具有指定字段的对象

Java 分组元素,每个组仅包含一个具有指定字段的对象,java,java-stream,groupingby,Java,Java Stream,Groupingby,我对java对象进行分组有问题。让我们看看示例对象: public class MyObject { private String field1; public MyObject(String field1) { this.field1 = field1; } } 我想要实现的是将MyObject分组,这样每个组只包含一个具有指定field1值的对象。例如,对于此类元素列表: public static void main(String[] ar

我对java对象进行分组有问题。让我们看看示例对象:

public class MyObject {

    private String field1;

    public MyObject(String field1) {
        this.field1 = field1;
    }
}
我想要实现的是将
MyObject
分组,这样每个组只包含一个具有指定
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<>();

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

我认为使用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 + '\'' +
            '}';
    }
}
公共类SO67140234{
公共静态void main(字符串[]args){
MyObject o1=新的MyObject(“1”);
MyObject o2=新的MyObject(“1”);
MyObject o3=新的MyObject(“1”);
MyObject o4=新的MyObject(“2”);
MyObject o5=新的MyObject(“2”);
MyObject o6=新的MyObject(“3”);
列表=数组.asList(o1,o2,o3,o4,o5,o6);
List listsWithUniqueField1Values=new ArrayList();
外部:
对于(MyObject对象:列表){
for(设置存储桶:ListSwithUniqueField1值){
如果(桶添加(obj)){
继续对外开放;
}
}
添加(新的HashSet(Collections.singleton(obj));
}
System.out.println(ListSwithUniqueField1值);
}
}
类MyObject{
私有最终字符串字段1;
公共MyObject(字符串字段1){
此字段1=字段1;
}
@凌驾
公共布尔等于(对象o){
如果(this==o)返回true;
如果(o==null | | getClass()!=o.getClass())返回false;
MyObject MyObject=(MyObject)o;
返回Objects.equals(field1,myObject.field1);
}
@凌驾
公共int hashCode(){
返回Objects.hash(field1);
}
@凌驾
公共字符串toString(){
返回“MyObject{”+
“field1=”+field1+“\”+
'}';
}
}

为了按
MyObject
的实例进行分组,此类需要实现
equals
hashCode
方法,并且
field1
应该是最终的,以避免
hashCode
在更改其值时损坏

公共类MyObject{
私有最终字符串字段1;
公共MyObject(字符串字段1){
此字段1=字段1;
}
公共字符串getField1(){返回this.field1;}
@凌驾
公共布尔等于(对象o){
如果(this==o)返回true;
if(null==o | |!(o MyObject的instanceof))返回false;
MyObject that=(MyObject)o;
返回Objects.equals(this.field1,that.field1);
}
@凌驾
公共int hashCode(){
返回Objects.hash(this.field1);
}
@凌驾
公共字符串toString(){
return“field1=”+this.field1;
}
}
收集器。groupingBy
不能用于获得所需的结果,但可以应用自定义操作来创建一组唯一的
MyObject
实例(在某种程度上提醒@Rubydesic的解决方案,但没有嵌套循环)

List List=Arrays.asList(o1、o4、o5、o2、o6、o3);
列表结果=List.stream()
.收集(
ArrayList::新建,//`供应商`
(lst,x)->{//累加器
对于(集合:lst){
如果(设置添加(x)){
return;//找到一个bucket来放置MyObject实例
}
}
//创建新桶
Set newSet=newhashset();
新闻集添加(x);
第一次添加(新闻集);
},
(lst1,lst2)->{}//空组合器
);
系统输出打印项次(结果);
输出:

[[field1=1, field1=2, field1=3], [field1=1, field1=2], [field1=1]]

您可以使用
groupingBy
本身来实现这一点。(不需要
等于
哈希代码

  • 第一组使用
    field1
    。这将给出一张地图,如下所示:
  • 现在,对于每个键,迭代它们各自的列表,并将每个
    MyObject
    添加到
    listsWithUniqueField1Values
    中的不同列表中
  • a。首先处理键
    1
    ,列表变成
    [[1]]
    ->
    [[1],[1]]
    ->
    [[1],[1],[1]]

    b。然后按
    2
    ,列表变成
    [[1,2],[1],[1]]
    ->
    [[1,2],[1,2],[1]]

    c。对于键
    3
    ,列表变为
    [[1,2,3],[1,2],[1]]

    代码:

    List uniqueList=newarraylist();
    list.stream()
    .collect(收集器.groupingBy(MyObject::getField1))
    .values()
    .stream()
    .forEach(值->添加列表(唯一列表,值));
    返回唯一列表;
    
    下面的方法
    addToList
    用于填充唯一列表。 在这种情况下,
    ListIterator
    用于
    Iterator
    ,因为
    add
    方法在
    ListIterator
    中可用

    private static void addToList(列表唯一列表,列表值){
    ListIterator迭代器=uniqueList.ListIterator();
    对于(MyObject o:值){
    名单;
    如果(!iterator.hasNext()){
    //需要将对象添加到新列表中。
    列表=新的ArrayList();
    迭代器。添加(列表);
    }否则{
    list=iterator.next();
    }
    列表。添加(o);
    }
    }
    
    假设
    MyObject
    有一个getter,我能想到的最简单的方法之一就是组合

    • 收藏,收藏,然后
    • 收集者分组
    • 链接列表
    • 从LinkedList弹出项目并将其插入结果中的方法
    List finalResult=List.stream()
    收集,收集收集,然后收集(
    Collectors.groupingBy(MyObject::getField1,Collectors.toCollection(LinkedList::new)),
    地图->{
    
    [[field1=1, field1=2, field1=3], [field1=1, field1=2], [field1=1]]
    
    { 1 : [1,1,1], 2 : [2,2], 3 : [3] }