您好,登錄后才能下訂單哦!
本篇內容介紹了“怎么解決使用Hashcode中distinct()方法沒有起效問題”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
大佬在項目中寫了類似這樣的一段代碼:
List<ProjectId> list = new ArrayList<>(); // 省略add數據操作 List<DeviceModel> models = list.stream().map(ProjectId::getDeviceModel).distinct().collect(Collectors.toList()); System.out.println(models);
結果呢,這段代碼中的distinct()方法并沒有起效,并沒有達到去重的預期。
通過API文檔來看并沒有問題,進而大佬開啟了debug模式,發現奇怪的是實體類的equals方法都沒進。
根據大佬發的部分代碼和實現思路,把整個模擬的測試程序補充完整,創建了兩個實體類ProjectId和DeviceModel,并重寫了equals方法(跟大佬溝通,他重寫了equals方法,并且單獨使用是生效的)。
DeviceModel實體類,簡單重寫了equals方法,只比較字段no是否相等。
@Data public class DeviceModel { private String no; @Override public String toString(){ return no; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } return this.toString().equals(other.toString()); } }
ProjectId實體類,重寫了equals方法,
@Data public class ProjectId { private int id; private DeviceModel deviceModel; }
然后,構建了測試類:
public class Test { public static void main(String[] args) { List<ProjectId> list = new ArrayList<>(); DeviceModel device1 = new DeviceModel(); device1.setNo("1"); ProjectId projectId1 = new ProjectId(); projectId1.setDeviceModel(device1); projectId1.setId(1); list.add(projectId1); DeviceModel device2 = new DeviceModel(); device2.setNo("1"); ProjectId projectId2 = new ProjectId(); projectId2.setDeviceModel(device2); projectId2.setId(1); list.add(projectId2); DeviceModel device3 = new DeviceModel(); device3.setNo("2"); ProjectId projectId3 = new ProjectId(); projectId3.setDeviceModel(device3); projectId3.setId(2); list.add(projectId3); List<DeviceModel> models = list.stream().map(ProjectId::getDeviceModel).distinct().collect(Collectors.toList()); System.out.println(models); } }
先構建了一組數據,然后讓device1與device2的no屬性一樣,重寫了equals方法,理論上它們應該是相等的,device3對象用來做對照。
執行上面的程序,控制臺打印如下:
[1, 1, 2]
的確還原了大佬的bug,也奇怪為什么會這樣。但既然bug已重現,解決就是比較簡單的事了。
此時,大佬又發來另外一個線索,說通過for循環形式沒事:
List<DeviceModel> results = new ArrayList<>(); for (DeviceModel deviceModel : list.stream().map(ProjectId::getDeviceModel).collect(Collectors.toList())) { if (!results.contains(deviceModel)) { results.add(deviceModel); } } System.out.println(results);
這種實現形式恰好又可以用來做對照。
進行問題排查時首先也想到了debug,但是同樣出現并未走equals方法的情況。
仔細看了一下代碼,發現在Stream處理的過程中用到了map操作。而在之前的文章中也提到,Map中判斷一個對象是否已經存在是先通過key的hash值定位到對應的數組下標,如果該位置上的Entry沒有值,則直接保存;如果已經有存在的值,再通過equals方法比較值是否一樣。
那么,是不是因為重寫了equals方法,而沒有重寫hashcode方法導致的呢?于是,在DeviceModel類中新增了hashcode方法:
@Override public int hashCode() { // JDK7新增的Objects工具類 return Objects.hash(no); }
再次執行,測試方法,發現可以成功去重了。很顯然,大佬的失誤是在重寫equals方法時違背了一條原則:如果一個類的equals方法相等,那么它們的hashcode方法必須相等。由于沒有重寫hashcode方法導致違背這一原則。因此,在隱式使用Map時就出現了莫名其妙的問題。
經過這一番周折,問題終于解決。想必大家更也更加明白了為什么重寫equals方法一定要重寫hashcode方法了。后面大佬又考問我一個問題:為什么list.contains方法不會出現這個問題呢?
因為List的底層結構是數組,不像Map那樣為了提升效率先對Key進行hash處理比較。簡單看一下ArrayList中contains方法的核心實現:
public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1; }
可以看出如果對象不為null時,還是循環調用的equals方法來處理的。
“怎么解決使用Hashcode中distinct()方法沒有起效問題”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。