在日常的業務開發中,偶爾會遇到需要將 List 集合中的重復數據去除掉的場景。這個時候可能有同學會問:為什么不直接使用 Set 或者 LinkedHashSet 呢?這樣不就沒有重復數據的問題了嘛?
不得不說,能提這個問題的同學很機智,一眼就看到了問題的本質。
但是,在實際的業務開發中遇到的情況會更復雜。比如,List 集合可能是歷史遺留問題,也有可能是調用接口返回的類型限制,只能使用 List 接收,又或者是代碼寫了一半,在做多個集合合並的時候才發現了這個問題,總之造成問題的原因有很多種,這里就不一一列舉了。
當發現這個問題之后,如果可以通過改造原有代碼,把原來的 List 類型替換成 Set 類型,那就可以直接修改集合的類型即可。但如果壓根就修改不了,或者是修改的成本太大,那接下來這 6 種去重的方法,將幫你將解決問題。
前置知識
正式開始之前,先來搞懂兩組概念:無序集合和有序集合 & 無序和有序。因為接下來的方法實現中,會反復提及這兩組概念,所以有必要在正式開始之前,先把它們搞清楚。
無序集合
無序集合是指,數據讀取的順序和數據插入的順序是不一致的。例如,插入集合的順序是:1、5、3、7,而集合的讀取順序竟然是:1、3、5、7。
有序集合
有序集合的概念和無序集合的概念正好相反,它是指集合的讀取順序和插入順序是一致的。例如,插入數據的順序是:1、5、3、7,那么讀取的順序也是:1、5、3、7。
有序和無序
通過上面的無序集合和有序集合,我們可以得出有序和無序的概念。
有序指的是數據的排列順序和讀取順序符合我們的預期就叫做有序。而無序指的是數據的排列順序和讀取順序不符合我們的預期就叫做無序。
PS:如果對於有序和無序的概念不是很清楚也沒關系,通過下面的事例,我們可以進一步的理解它們的含義。
方法1:contains判斷去重(有序)
要進行數據去重,我們首先想到的是新建一個集合,然后循環原來的集合,每次循環判斷原集合中的循環項,如果當前循環的數據,沒有在新集合中存在就插入,已經存在了就舍棄,這樣當循環執行完,我們就得到了一個沒有重復元素的集合了,實現代碼如下:
public class ListDistinctExample {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>() {{
add(1);
add(3);
add(5);
add(2);
add(1);
add(3);
add(7);
add(2);
}};
System.out.println("原集合:" + list);
method(list);
}
/**
* 自定義去重
* @param list
*/
public static void method(List<Integer> list) {
// 新集合
List<Integer> newList = new ArrayList<>(list.size());
list.forEach(i -> {
if (!newList.contains(i)) { // 如果新集合中不存在則插入
newList.add(i);
}
});
System.out.println("去重集合:" + newList);
}
}
以上程序執行的結果,如下所示:
此方法的優點的:理解起來比較簡單,並且最終得到的集合也是有序的,這里的有序指的是新集合的排列順序和原集合的順序是一致的;但缺點是實現代碼有點多,不夠簡潔優雅。
方法2:迭代器去重(無序)
自定義 List 去重,除了上面的新建集合之外,我們也可以使用迭代器循環判斷每一項數據,如果當前循環的數據,在集合中存在兩份或兩份以上,就將當前的元素刪除掉,這樣循環完之后,也可以得到一個沒有重復數據的集合,實現代碼如下:
public class ListDistinctExample {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>() {{
add(1);
add(3);
add(5);
add(2);
add(1);
add(3);
add(7);
add(2);
}};
System.out.println("原集合:" + list);
method_1(list);
}
/**
* 使用迭代器去重
* @param list
*/
public static void method_1(List<Integer> list) {
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
// 獲取循環的值
Integer item = iterator.next();
// 如果存在兩個相同的值
if (list.indexOf(item) != list.lastIndexOf(item)) {
// 移除最后那個相同的值
iterator.remove();
}
}
System.out.println("去重集合:" + list);
}
}
以上程序執行的結果,如下所示:
此方法的實現比上一種方法的實現代碼要少一些,並且不需要新建集合,但此方法得到的新集合是無序的,也就是新集合的排列順序和原集合不一致,因此也不是最優的解決方案。
方法3:HashSet去重(無序)
我們知道 HashSet 天生具備“去重”的特性,那我們只需要將 List 集合轉換成 HashSet 集合就可以了,實現代碼如下:
public class ListDistinctExample {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>() {{
add(1);
add(3);
add(5);
add(2);
add(1);
add(3);
add(7);
add(2);
}};
System.out.println("原集合:" + list);
method_2(list);
}
/**
* 使用 HashSet 去重
* @param list
*/
public static void method_2(List<Integer> list) {
HashSet<Integer> set = new HashSet<>(list);
System.out.println("去重集合:" + set);
}
}
以上程序執行的結果,如下所示:
此方法的實現代碼較為簡潔,但缺點是 HashSet 會自動排序,這樣新集合的數據排序就和原集合不一致了,如果對集合的順序有要求,那么此方法也不能滿足當前需求。
方法4:LinkedHashSet去重(有序)
既然 HashSet 會自動排序不能滿足需求,那就使用 LinkedHashSet,它既能去重又能保證集合的順序,實現代碼如下:
public class ListDistinctExample {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>() {{
add(1);
add(3);
add(5);
add(2);
add(1);
add(3);
add(7);
add(2);
}};
System.out.println("原集合:" + list);
method_3(list);
}
/**
* 使用 LinkedHashSet 去重
* @param list
*/
public static void method_3(List<Integer> list) {
LinkedHashSet<Integer> set = new LinkedHashSet<>(list);
System.out.println("去重集合:" + set);
}
}
以上程序執行的結果,如下所示:
從上述代碼和執行結果可以看出,LinkedHashSet 是到目前為止,實現比較簡單,且最終生成的新集合與原集合順序保持一致的實現方法,是我們可以考慮使用的一種去重方法。
方法5:TreeSet去重(無序)
除了以上的 Set 集合之外,我們還可以使用 TreeSet 集合來實現去重功能,實現代碼如下:
public class ListDistinctExample {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>() {{
add(1);
add(3);
add(5);
add(2);
add(1);
add(3);
add(7);
add(2);
}};
System.out.println("原集合:" + list);
method_4(list);
}
/**
* 使用 TreeSet 去重(無序)
* @param list
*/
public static void method_4(List<Integer> list) {
TreeSet<Integer> set = new TreeSet<>(list);
System.out.println("去重集合:" + set);
}
}
以上程序執行的結果,如下所示:
比較遺憾的是,TreeSet 雖然實現起來也比較簡單,但它有着和 HashSet 一樣的問題,會自動排序,因此也不能滿足我們的需求。
方法6:Stream去重(有序)
JDK 8 為我們帶來了一個非常實用的方法 Stream,使用它可以實現很多功能,比如下面的去重功能:
public class ListDistinctExample {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>() {{
add(1);
add(3);
add(5);
add(2);
add(1);
add(3);
add(7);
add(2);
}};
System.out.println("原集合:" + list);
method_5(list);
}
/**
* 使用 Stream 去重
* @param list
*/
public static void method_5(List<Integer> list) {
list = list.stream().distinct().collect(Collectors.toList());
System.out.println("去重集合:" + list);
}
}
以上程序執行的結果,如下所示:
Stream 實現去重功能和其他方法不同的是,它不用新創建集合,使用自身接收一個去重的結果就可以了,並且實現代碼也很簡潔,並且去重后的集合順序也和原集合的順序保持一致,是我們最優先考慮的去重方法。
總結
本文我們介紹了 6 種集合去重的方法,其中實現最簡潔,且去重之后的順序能和原集合保持一致的實現方法,只有兩種:LinkedHashSet 去重和 Stream 去重,而后一種去重方法無需借助新集合,是我們優先考慮的去重方法。
是非審之於己,毀譽聽之於人,得失安之於數。
博主介紹:80 后程序員,寫博客這件事“堅持”了 11 年,愛好:讀書、慢跑、羽毛球。
我的公眾號:Java面試真題解析
個人微信:GG_Stone,歡迎圍觀朋友圈,做個點贊只交。