java.util.stream.stream.distinct()方法如何工作?我可以覆盖流对象的equals()方法吗?

发布于 2025-01-25 19:36:06 字数 3840 浏览 2 评论 0原文

我的用例是,我试图使用 stream的方法从类code> Studale> Student> studentCoursagping 。

POJO详细信息如下:

public class StudentCourseMapping implements Serializable{
    private String name;
    private String dept;
    private Integer roll;
    private String course;

以下是等等方法:

    @Override
    public boolean equals(Object obj) {
        StudentCourseMapping other = (StudentCourseMapping) obj;
        if (roll == null) {
            if (other.roll != null)
                return false;
        } else if (!roll.equals(other.roll))
            return false;
        return true;
    }

以下是实现:

public class RemoveDuplicateUsingStream {
    public static void main(String[] args) {
        List<StudentCourseMapping> studentCourceList = JacksonJSONReaderObjectMapper.jsonReader();
        
        studentCourceList.stream().distinct().forEach(System.out::println);
        StudentCourseMapping s0 = studentCourceList.get(0);
        StudentCourseMapping s1 = studentCourceList.get(1);
        System.out.println(s0.equals(s1));

        Set<Integer> st = new HashSet();
        List<StudentCourseMapping>studentCourceList2 = studentCourceList.stream().filter(s -> st.add(s.getRoll()))
                .collect(Collectors.toCollection(ArrayList::new));
        System.out.println(studentCourceList2.size());
    }
}

输出IS

StudentCourseMapping [name=Alu, dept=Physics, roll=12, course=Quantum Theory]
StudentCourseMapping [name=Alu, dept=Physics, roll=12, course=English]
StudentCourseMapping [name=Sam, dept=Commerce, roll=16, course=English]
StudentCourseMapping [name=Sam, dept=Commerce, roll=16, course=Accounts]
StudentCourseMapping [name=Joe, dept=Arts, roll=19, course=English]
StudentCourseMapping [name=Joe, dept=Arts, roll=19, course=Hindi]
true
3

JacksonjsonreaderObjectmapper.jsonreader()是一种自定义方法,读取以下JSON。我能够通过使用过滤器并添加到hashset来实现相同的目标,但是我真的很想知道我独特的实现是什么问题。

{
    "studentCourseMapping": [
        {
            "name": "Alu",
            "dept": "Physics",
            "roll": 12,
            "course": "Quantum Theory"
        },
        {
            "name": "Alu",
            "dept": "Physics",
            "roll": 12,
            "course": "English"
        },
        {
            "name": "Sam",
            "dept": "Commerce",
            "roll": 16,
            "course": "English"
        },
        {
            "name": "Sam",
            "dept": "Commerce",
            "roll": 16,
            "course": "Accounts"
        },
        {
            "name": "Joe",
            "dept": "Arts",
            "roll": 19,
            "course": "English"
        },
        {
            "name": "Joe",
            "dept": "Arts",
            "roll": 19,
            "course": "Hindi"
        }
    ]
}

当我尝试直接测试Equals方法时,它正常工作并返回true,因为s0s1都滚动为12。

        StudentCourseMapping s0 = studentCourceList.get(0);
        StudentCourseMapping s1 = studentCourceList.get(1);
        System.out.println(s0.equals(s1));

但是,当我使用时,所有对象都被打印了,并且在试图在Eclipse中调试时,我写的独特方法没有被调用,但是文档应该称呼它。我正在使用JDK 11:

流&lt; t&gt;独特()

返回由不同元素组成的流(根据

My use case is that I am trying to use the distinct method of Stream to remove students with the same roll number from a list of objects of class StudentCourseMapping.

POJO details are below:

public class StudentCourseMapping implements Serializable{
    private String name;
    private String dept;
    private Integer roll;
    private String course;

Below is the equals method:

    @Override
    public boolean equals(Object obj) {
        StudentCourseMapping other = (StudentCourseMapping) obj;
        if (roll == null) {
            if (other.roll != null)
                return false;
        } else if (!roll.equals(other.roll))
            return false;
        return true;
    }

Below is the implementation:

public class RemoveDuplicateUsingStream {
    public static void main(String[] args) {
        List<StudentCourseMapping> studentCourceList = JacksonJSONReaderObjectMapper.jsonReader();
        
        studentCourceList.stream().distinct().forEach(System.out::println);
        StudentCourseMapping s0 = studentCourceList.get(0);
        StudentCourseMapping s1 = studentCourceList.get(1);
        System.out.println(s0.equals(s1));

        Set<Integer> st = new HashSet();
        List<StudentCourseMapping>studentCourceList2 = studentCourceList.stream().filter(s -> st.add(s.getRoll()))
                .collect(Collectors.toCollection(ArrayList::new));
        System.out.println(studentCourceList2.size());
    }
}

And the output is

StudentCourseMapping [name=Alu, dept=Physics, roll=12, course=Quantum Theory]
StudentCourseMapping [name=Alu, dept=Physics, roll=12, course=English]
StudentCourseMapping [name=Sam, dept=Commerce, roll=16, course=English]
StudentCourseMapping [name=Sam, dept=Commerce, roll=16, course=Accounts]
StudentCourseMapping [name=Joe, dept=Arts, roll=19, course=English]
StudentCourseMapping [name=Joe, dept=Arts, roll=19, course=Hindi]
true
3

JacksonJSONReaderObjectMapper.jsonReader() is a custom method which reads the below JSON. I am able to achieve the same by using filter and adding to HashSet but I really want to know what is wrong with my distinct implementation.

{
    "studentCourseMapping": [
        {
            "name": "Alu",
            "dept": "Physics",
            "roll": 12,
            "course": "Quantum Theory"
        },
        {
            "name": "Alu",
            "dept": "Physics",
            "roll": 12,
            "course": "English"
        },
        {
            "name": "Sam",
            "dept": "Commerce",
            "roll": 16,
            "course": "English"
        },
        {
            "name": "Sam",
            "dept": "Commerce",
            "roll": 16,
            "course": "Accounts"
        },
        {
            "name": "Joe",
            "dept": "Arts",
            "roll": 19,
            "course": "English"
        },
        {
            "name": "Joe",
            "dept": "Arts",
            "roll": 19,
            "course": "Hindi"
        }
    ]
}

When I try to test the equals method directly it was working properly and returning true since both the s0 and s1 has roll as 12.

        StudentCourseMapping s0 = studentCourceList.get(0);
        StudentCourseMapping s1 = studentCourceList.get(1);
        System.out.println(s0.equals(s1));

But when I am using distinct all the objects are getting printed and also while trying to debug in Eclipse the distinct method I wrote is not getting called, but the documentation says it should be called. I am using JDK 11:

Stream<T> distinct()

Returns a stream consisting of the distinct elements (according to Object.equals(Object)) of this stream.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

金橙橙 2025-02-01 19:36:06

正如@RealSkeATDIC和@Jesper在评论中已经说过的那样,在您的studentscoursapping类中,您还需要覆盖hashcode()根据您的equals()实现,以正确比较流中的元素并仅保留不同的元素。

实际上,尽管没有hashcode() distract() 文档:

返回由此流的不同元素(根据对象。Equals(object))组成的流。

提到了 equals() ,其文档介绍了hashCode方法的一般合同,该合同在 hashcode() 文档。引用equals()文档:

请注意,每当该方法被覆盖时,通常都必须覆盖哈希码方法,以维护哈希码方法的一般合同,该方法指出,相等的对象必须具有相等的哈希代码。

实际上,一旦hashCode()方法被正确覆盖,您的摘要就会产生所需的输出。您也可以在

public class StudentCourseMapping implements Serializable {
    private String name;
    private String dept;
    private Integer roll;
    private String course;

    //... your class implementation ...

    @Override
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (obj == this) return true;
        if (obj.getClass() != getClass()) return false;
        StudentCourseMapping student = (StudentCourseMapping) obj;
        return Objects.equals(roll, student.roll);
    }

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

    @Override
    public String toString() {
        return String.format("[name = %s, dept = %s, roll = %d, course = %s]", name, dept, roll, course);
    }
}

As @RealSkeptic and @Jesper have already said in the comments, in your StudentCourseMapping class you also need to override the hashCode() method according to your equals() implementation, in order to properly compare the elements in the stream and keep only the distinct ones.

In fact, although no hashCode() redefinition is mentioned in the distinct() documentation:

Returns a stream consisting of the distinct elements (according to Object.equals(Object)) of this stream.

It is mentioned though the use of equals(), whose documentation introduces the general contract for the hashcode method, which is amply covered in the hashCode() documentation. Quoting the equals() documentation:

Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.

In fact, once the hashCode() method is properly overridden, your snippet produces the desired output. You can also test it at OneCompiler.

public class StudentCourseMapping implements Serializable {
    private String name;
    private String dept;
    private Integer roll;
    private String course;

    //... your class implementation ...

    @Override
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (obj == this) return true;
        if (obj.getClass() != getClass()) return false;
        StudentCourseMapping student = (StudentCourseMapping) obj;
        return Objects.equals(roll, student.roll);
    }

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

    @Override
    public String toString() {
        return String.format("[name = %s, dept = %s, roll = %d, course = %s]", name, dept, roll, course);
    }
}
下壹個目標 2025-02-01 19:36:06

从理论上讲,您现有的实施将起作用,因为不同的是非平等价值。但是stream()实现distion()使用linkedHashset(它可以直接使用对象的hashcode 方法)捕获重复项。由于每个类引用都是唯一的,并且hashCode的默认实现基于该引用的值,因此不会从equals perspective中获得重复。

这是使用默认HashCode的简单演示。创建了10个对象的列表。然后,该列表反复添加到自身并改组中,从而列出了10,240个对象的列表,共有10个不同的哈希码。因此,当DISTICT()方法检查这些Hashcodes时,它将找到重复项并忽略它们。

注意:此类使用简单且不完整的等于方法,该方法足以容纳该受控演示。

class Etest {
        int v;
        
        public Etest(int v) {
            this.v = v;
        }
        
        @Override
        public boolean equals(Object ob) {
            return ((Etest)ob).v == this.v;
        }
        
        @Override
        public String toString() {
            return v+"";
        }
}

List<Etest> list = new ArrayList<>(IntStream.range(1, 11)
        .mapToObj(i -> new Etest(i)).toList());

for(int i = 0; i < 10; i++) {
    Collections.shuffle(list);
    list.addAll(list);
}
System.out.println("Total entries = " + list.size());
list.stream().distinct().forEach(e->System.out.printf("%s ", e));

打印类似的东西

Total entries = 10240
3 4 8 9 2 10 6 5 1 7 

In theory your existing implementation would work since distinct implies non equal values. But the Stream() implementation of distinct() makes use of a LinkedHashSet (which makes direct use of the object's hashCode method) to catch the duplicates. Since each class reference is unique and the default implementation of hashCode is based on the value of that reference, duplicates from an equals perspective will not be caught.

Here is a simple demo using the default hashCode. A list of 10 objects is created. Then that list is repeatedly added to itself and shuffled resulting in a list of 10,240 objects, with a total of 10 distinct hashCodes. So when the distinct() method checks those hashcodes, it will find duplicates and ignore them.

Note: This class uses a simple and incomplete equals method that is sufficient for this controlled demo.

class Etest {
        int v;
        
        public Etest(int v) {
            this.v = v;
        }
        
        @Override
        public boolean equals(Object ob) {
            return ((Etest)ob).v == this.v;
        }
        
        @Override
        public String toString() {
            return v+"";
        }
}

List<Etest> list = new ArrayList<>(IntStream.range(1, 11)
        .mapToObj(i -> new Etest(i)).toList());

for(int i = 0; i < 10; i++) {
    Collections.shuffle(list);
    list.addAll(list);
}
System.out.println("Total entries = " + list.size());
list.stream().distinct().forEach(e->System.out.printf("%s ", e));

prints something like

Total entries = 10240
3 4 8 9 2 10 6 5 1 7 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文