同样的输入,为什么Objects.hash()方法返回的hash值每次不一样?

解决AopMethod在Set中重复添加问题
本文探讨了在使用Set保存AopMethod对象时遇到的重复添加问题,详细分析了hashCode和equals方法的不当实现导致的问题,并提供了解决方案,即正确覆盖hashCode方法以确保对象的唯一性。

背景

开发过程中发现一个问题,项目中用Set保存AopMethod对象用于去重,但是发现即使往set中添加相同内容的对象,每次也能够添加成功。

AopMethod类的部分代码如下:

public class AopMethod {
    private String methodName;
    private Class<?>[] parameterTypes = new Class<?>[]{};
    //是否需要忽略掉参数匹配
    private boolean ignoreParameterTypes;
​
    public AopMethod() {
    }
​
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AopMethod aopMethod = (AopMethod) o;
        return ignoreParameterTypes == aopMethod.ignoreParameterTypes &&
                Objects.equals(methodName, aopMethod.methodName) &&
                Arrays.equals(parameterTypes, aopMethod.parameterTypes);
    }
​
    @Override
    public int hashCode() {
        return Objects.hash(methodName, parameterTypes, ignoreParameterTypes);
    }
}

通过debug发现,对象即使内容完全相同,hashCode每次返回的hash值都是不一样的。

AopMethod{methodName='m81', parameterTypes=[int], ignoreParameterTypes=false} hash:-1850752941
AopMethod{methodName='m81', parameterTypes=[int], ignoreParameterTypes=false} hash:-526785805

equals、hashCode方法是Idea IDE自动生成的,看来,自动生成的不靠谱啊。。。

 

why

Class<?>[] parameterTypes = new Class<?>[]{};

Objects.hash内部会调用hahCode方法,但是parameterTypes为数组,而数组是没有hashCode()方法的。

可参考:https://stackoverflow.com/questions/29955291/why-objects-hash-returns-different-values-for-the-same-input

 

最佳实践

不要依赖Idea IDE自动生成的hashCode方法。

如果有数组的话,用数组用Arrays.hashCode()包起来,然后再作为Objects.hash()方法的参数。

hashCode改成下面的实现就OK了!

    @Override
    public int hashCode() {
        return Objects.hash(methodName, Arrays.hashCode(parameterTypes), ignoreParameterTypes);
    }

 

参考资料

https://stackoverflow.com/questions/29955291/why-objects-hash-returns-different-values-for-the-same-input

转载于:https://www.cnblogs.com/yeyang/p/10478447.html

需要在全家创建集合保存去重的数据用员工工号为键获取明细数据, // 内部类用于分组键 static class EventGroupKey { private final String orderNumber; // 单号 private final String boardNumber; // 板号 private final String machineId; // 机床号 private final String workerId; // 员工工号 public EventGroupKey(EventDataDm dm) { // 从dataKey解析单号和板号(格式:单号,板号) String[] keys = dm.getDataKey().split(","); this.orderNumber = keys[0]; this.boardNumber = keys.length > 1 ? keys[1] : ""; this.machineId = dm.getTag5(); this.workerId = dm.getTag4(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EventGroupKey that = (EventGroupKey) o; return Objects.equals(orderNumber, that.orderNumber) && Objects.equals(boardNumber, that.boardNumber) && Objects.equals(machineId, that.machineId) && Objects.equals(workerId, that.workerId); } @Override public int hashCode() { return Objects.hash(orderNumber, boardNumber, machineId, workerId); } } public List<ZhuanXuReportQuery> processEventData( List<EventDataDm> eventDataList,String date) { // 处理转序数据 Map<String, Long> transCountByWorker = countTransEvents(eventDataList); ArrayList zhuanXuReportQueryArrayList = new ArrayList<ZhuanXuReportQuery>(); for (Map.Entry<String, Long> entry : transCountByWorker.entrySet()) { String key = entry.getKey(); ZhuanXuReportQuery zhuanXuReportQuery = new ZhuanXuReportQuery(); zhuanXuReportQuery.setZhuanXuNum(transCountByWorker.get(key).toString()); zhuanXuReportQuery.setName(hrEmpRemote.getEmpNameByCode(key)+"("+key+")"); zhuanXuReportQuery.setSearchDate(date); zhuanXuReportQueryArrayList.add(zhuanXuReportQuery); } return zhuanXuReportQueryArrayList; } private Map<String, Long> countTransEvents(List<EventDataDm> eventDataList) { // 1. 过滤出转序事件并按员工分组 Map<String, List<EventDataDm>> eventsByWorker = eventDataList.stream() .filter(dm -> "转序".equals(dm.getDataType())) .collect(Collectors.groupingBy(EventDataDm::getTag4)); // 2. 处理每个员工的事件 return eventsByWorker.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, entry -> processWorkerEvents(entry.getValue()) )); } private long processWorkerEvents(List<EventDataDm> events) { // 1. 按复合键分组(单号+板号+机床号) Map<EventGroupKey, List<EventDataDm>> groupedEvents = events.stream() .collect(Collectors.groupingBy(EventGroupKey::new)); // 2. 处理每个分组 return groupedEvents.values().stream() .mapToLong(this::processEventGroup) .sum(); } private long processEventGroup(List<EventDataDm> group) { // 1. 按时间排序 group.sort(Comparator.comparing(EventDataDm::getUpdateDate)); // 2. 合并5分钟内的事件 long count = 0; Date lastEventTime = null; for (EventDataDm event : group) { if (lastEventTime == null) { // 第一条记录直接计数 count++; lastEventTime = event.getUpdateDate(); } else { // 计算时间差(毫秒) long timeDiff = event.getUpdateDate().getTime() - lastEventTime.getTime(); // 超过5分钟才计数 if (timeDiff > TimeUnit.MINUTES.toMillis(5)) { count++; lastEventTime = event.getUpdateDate(); } } } return count; } }
最新发布
08-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值