试图理解Java8中流的distinct()

试图理解Java8中流的distinct(),java,java-8,stream-operators,Java,Java 8,Stream Operators,我正在阅读一本关于Java8的书,书中解释了distinct是用于流的。需要指出的是,生成不同元素的相等性是由hashCode()和equals()方法的实现决定的。 因此,我编写了以下代码,以便通过示例理解: static class Order{ public Order(int id,Double value){ this.id = id; this.value = value; } int i

我正在阅读一本关于Java8的书,书中解释了distinct是用于流的。需要指出的是,生成不同元素的相等性是由
hashCode()
equals()
方法的实现决定的。 因此,我编写了以下代码,以便通过示例理解:

static class Order{
        public Order(int id,Double value){
            this.id = id;
            this.value = value;
        }
        int id;
        Double value;
        @Override
        public int hashCode() {
            System.out.println("In Hashcode() - " + this.id +","+this.value);
            return this.id;
        }
        @Override
        public boolean equals(Object o){
            System.out.println("In Equals()");
            return this.id == ((Order)o).id;
        }
    }

    public static void main(String[] args) {
        Stream<Order> orderList = Stream.of(new Order(1,10.0),new Order(2,140.5),new Order(2,100.8));
        Stream<Order> biggerOrders = orderList.filter(o->o.value > 75.0);
        biggerOrders.distinct().forEach(o->System.out.println("OrderId:"+ o.id));
    }
我不明白为什么hashCode方法在同一个Order对象上(2140.5) 在将其与另一个Order对象(2100.8)进行比较之前调用两次


提前感谢。

第一次调用hashCode检查HashMap中是否已经存在项(顺序)(distinct使用内部HashMap)。第二次调用时,如果不存在,则将该项(顺序)放入hashmap中


提示:尝试调试hashCode方法。

正如@Adi所回答的,
distinct()
正在内部使用一个HashMap,该HashMap调用
顺序的
hashCode()

下面是进行这两个调用的相关代码

java.util.stream.DistinctOps.makeRef()中

返回新Sink.ChainedReference(Sink){
集看到;
@凌驾
公共空白开始(长尺寸){
seen=newhashset();
下游。开始(-1);
}
@凌驾
公共无效结束(){
seen=null;
下游。结束();
}
@凌驾
公共无效接受(T){
如果(!seen.contains(t)){//在此处进行第一次调用
seen.add(t);//在这里进行第二次调用
下游。接受(t);
}
}
};
下面是这两个调用的堆栈跟踪


您看过源代码了吗?如果我想弄明白这一点,我会先看看那里…一个非常常见的反模式
if(!seen.contains(t)){seen.add(t);…}
可以很容易地替换为
if(seen.add(t)){…}
避免双重散列,因为这正是
add
的约定,仅当元素尚未包含在
集中时才添加该元素(并返回是否已添加该元素)。这在Oracle JDK中
In Hashcode() - 2,140.5
In Hashcode() - 2,140.5
OrderId:2
In Hashcode() - 2,100.8
In Equals()
return new Sink.ChainedReference<T, T>(sink) {
    Set<T> seen;

    @Override
    public void begin(long size) {
        seen = new HashSet<>();
        downstream.begin(-1);
    }

    @Override
    public void end() {
        seen = null;
        downstream.end();
    }

    @Override
    public void accept(T t) {
        if (!seen.contains(t)) {//first call is made here
            seen.add(t);//second call is made here
            downstream.accept(t);
        }
    }
};