Java找出兩個大數(shù)據(jù)量List集合中的不同元素的方法總結
本文將帶你了解如何快速的找出兩個相似度非常高的List集合里的不同元素。主要通過Java API、List集合雙層遍歷比較不同、借助Map集合查找三種方式,以及他們之間的執(zhí)行效率情況,話不多說,開搞! 集合初始化方法:
/**
* 制造任意個元素的的List集合
* @param size List集合的size
* @return List<String>
*/
private static List<String> dataList(int size) {
List<String> dataList = new ArrayList<>();
for (int i = 0; i < size; i++) {
dataList.add("" + i);
}
return dataList;
}測試數(shù)據(jù)為集合A: 1千, 1萬, 10萬,1百萬, 1千萬的數(shù)據(jù)量.
- 集合B比集合A多初始化六條數(shù)據(jù),集合A添加一條特有的數(shù)據(jù)。
- 測試數(shù)據(jù)使用空字符串 + 自然數(shù)的方式。
JavaAPI過濾(不推薦)
1千數(shù)據(jù)量
List<String> listA = dataList(1000);
//集合A添加一個集合B沒有的元素
listA.add("onlyA10086");
List<String> listB = dataList(1006);
Long startTime = System.currentTimeMillis();
// 復制集合A和集合B作為備份
List<String> listABak = new ArrayList<>(listA);
List<String> listBBak = new ArrayList<>(listB);
// 集合B存在,集合A不存在的元素
listB.removeAll(listA);
// 集合A存在,集合B不存在的元素
listA.removeAll(listBBak);
Long endTime = System.currentTimeMillis();
List<String> differentList = new ArrayList<>();
differentList.addAll(listB);
differentList.addAll(listA);
System.out.println("集合A和集合B不同的元素:"+differentList);
Long costTime = endTime-startTime;
System.out.println("比對耗時:"+costTime+"毫秒。");
耗時:22毫秒

1萬數(shù)據(jù)量
List<String> listA = dataList(10000);
//集合A添加一個集合B沒有的元素
listA.add("onlyA10086");
List<String> listB = dataList(10006);
Long startTime = System.currentTimeMillis();
// 復制集合A和集合B作為備份
List<String> listABak = new ArrayList<>(listA);
List<String> listBBak = new ArrayList<>(listB);
// 集合B存在,集合A不存在的元素
listB.removeAll(listA);
// 集合A存在,集合B不存在的元素
listA.removeAll(listBBak);
Long endTime = System.currentTimeMillis();
List<String> differentList = new ArrayList<>();
differentList.addAll(listB);
differentList.addAll(listA);
System.out.println("集合A和集合B不同的元素:"+differentList);
Long costTime = endTime-startTime;
System.out.println("比對耗時:"+costTime+"毫秒。");
耗時:613毫秒

10萬數(shù)據(jù)量
List<String> listA = dataList(100000);
//集合A添加一個集合B沒有的元素
listA.add("onlyA10086");
List<String> listB = dataList(100006);
Long startTime = System.currentTimeMillis();
// 復制集合A和集合B作為備份
List<String> listABak = new ArrayList<>(listA);
List<String> listBBak = new ArrayList<>(listB);
// 集合B存在,集合A不存在的元素
listB.removeAll(listA);
// 集合A存在,集合B不存在的元素
listA.removeAll(listBBak);
Long endTime = System.currentTimeMillis();
List<String> differentList = new ArrayList<>();
differentList.addAll(listB);
differentList.addAll(listA);
System.out.println("集合A和集合B不同的元素:"+differentList);
Long costTime = endTime-startTime;
System.out.println("比對耗時:"+costTime+"毫秒。");

可以看出來十萬數(shù)據(jù)量級別已經(jīng)比較慢了,需要77秒
100萬數(shù)據(jù)量
emmm估計挺慢,不繼續(xù)驗證了。
為什么在數(shù)據(jù)量增大的時候,這種方法性能下降的這么明顯? 我們不妨來看一下removeAll的源碼:
public boolean removeAll(Collection<?> c) {
// 先判空,然后執(zhí)行批量remove
Objects.requireNonNull(c);
return batchRemove(c, false);
}
通過源碼我們可以看到,該方法是使用for循環(huán)對集合進行遍歷 第一層循環(huán)需要執(zhí)行 listA.size()次,里面調用了contains方法來確定集合B是否含有該元素, 再看contains方法的源碼:

可以看到,indexOf方法里又進行了一層遍歷. 平均每次遍歷要進行l(wèi)ist.size() / 2次計算, 假設集合A的元素個數(shù)為m,集合B的元素個數(shù)為n 我們可以得到結論,運算次數(shù)為 m *(n/2) 對于100W數(shù)據(jù)量來說,假設你的計算機每秒能夠執(zhí)行1千萬次運算,也需要13.8個小時才能對比出來。所以大數(shù)據(jù)量不建議通過此方法。

List集合雙層遍歷比較不同(不推薦)
該方法實際上就是將removeAll的實現(xiàn)邏輯用自己的方式寫出來,所以執(zhí)行效率,運行結果和上一種方法沒什么區(qū)別,這里只貼代碼出來,不再贅述。
private static void doubleFor() {
List<String> listA = dataList(1000);
//集合A添加一個集合B沒有的元素
listA.add("onlyA10086");
List<String> listB = dataList(1006);
System.out.println("數(shù)量級為 " + listA.size() + "集合的不同元素為");
List<String> differentList = new ArrayList<>();
long startTime = System.currentTimeMillis();
for (String str : listB) {
if (!listA.contains(str)) {
differentList.add(str);
}
}
for (String str : listA) {
if (!listB.contains(str)) {
differentList.add(str);
}
}
long endTime = System.currentTimeMillis();
System.out.println("集合A和集合B不同的元素:"+differentList);
System.out.println("使用雙層遍歷方法 比對耗時: " + (endTime - startTime)+"毫秒。");
}以上兩個方法中我都做了m*n次循環(huán),其實完全沒有必要循環(huán)這么多次,我們的需求是找出兩個List中的不同元素,那么我可以這樣考慮:用一個map存放lsit的所有元素,其中的key為lsit1的各個元素,value為該元素出現(xiàn)的次數(shù),接著把list2的所有元素也放到map里,如果已經(jīng)存在則value加1,最后我們只要取出map里value為1的元素即可,這樣我們只需循環(huán)m+n次,大大減少了循環(huán)的次數(shù)。
借助Map集合查找(推薦)
以List集合里的元素作為Map的key,元素出現(xiàn)的次數(shù)作為Map的Value,那么兩個List集合的不同元素在Map集合中value值為1,所對應的鍵。把所有value值為1的鍵找出來,我們就得到了兩個List集合不同的元素。 代碼如下:
/**
* 借助Map來獲取listA、listB的不同元素集合
*
* @param listA 集合A
* @param listB 集合B
* @return list<String> 不同元素集合
*/
public static List<String> getDifferListByMap(List<String> listA, List<String> listB) {
List<String> differList = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
long beginTime = System.currentTimeMillis();
for (String strA : listA) {
map.put(strA, 1);
}
for (String strB : listB) {
Integer value = map.get(strB);
if (value != null) {
map.put(strB, ++value);
continue;
}
map.put(strB, 1);
}
for (Map.Entry<String, Integer> entry : map.entrySet()) {
//獲取不同元素集合
if (entry.getValue() == 1) {
differList.add(entry.getKey());
}
}
long endTime = System.currentTimeMillis();
System.out.println("集合A和集合B不同的元素:"+differList);
System.out.println("使用map方式遍歷, 對比耗時: " + (endTime - beginTime)+"毫秒。");
return differList;
}1千數(shù)據(jù)量
List<String> listA = dataList(1000);
//集合A添加一個集合B沒有的元素
listA.add("onlyA10086");
List<String> listB = dataList(1006);
getDifferListByMap(listA,listB);
耗時:7毫秒

1萬數(shù)據(jù)量
List<String> listA = dataList(10000);
//集合A添加一個集合B沒有的元素
listA.add("onlyA10086");
List<String> listB = dataList(10006);
getDifferListByMap(listA,listB);
耗時:42毫秒

10萬數(shù)據(jù)量
List<String> listA = dataList(100000);
//集合A添加一個集合B沒有的元素
listA.add("onlyA10086");
List<String> listB = dataList(100006);
getDifferListByMap(listA,listB);
耗時:130毫秒

100萬數(shù)據(jù)量
List<String> listA = dataList(1000000);
//集合A添加一個集合B沒有的元素
listA.add("onlyA10086");
List<String> listB = dataList(1000006);
getDifferListByMap(listA,listB);
耗時:283毫秒

1000萬數(shù)據(jù)量
List<String> listA = dataList(10000000);
//集合A添加一個集合B沒有的元素
listA.add("onlyA10086");
List<String> listB = dataList(10000006);
getDifferListByMap(listA,listB);
耗時:6.6秒

可以看出來這種方法相當高效,千萬級數(shù)據(jù)比對也才用了6.6秒。 使用map集合的方式尋找不同元素,時間增長基本上是線性的,它的時間復雜度為O(m)。 而上面的remove方式和雙層循環(huán)遍歷的時間復雜度為O(m * n)。 所以,選用這種方式帶來的性能收益隨著集合元素的增長而增長。
優(yōu)化
上述通過map集合的方式效率很高,有沒有可以優(yōu)化的點呢?
- 兩個集合如果數(shù)量差距較大時,可以把小的在后面添加,這樣會減少循環(huán)里的判斷,性能又有了一定的提升。
- 創(chuàng)建map集合的時候直接指定大小,防止再散列。
優(yōu)化后代碼如下:
/**
* 找出兩個集合中不同的元素
*
* @param collmax
* @param collmin
* @return
*/
public static Collection getDifferListByMapPlus(Collection collmax, Collection collmin) {
//使用LinkedList防止差異過大時,元素拷貝
Collection csReturn = new LinkedList();
Collection max = collmax;
Collection min = collmin;
long beginTime = System.currentTimeMillis();
//先比較大小,這樣會減少后續(xù)map的if判斷次數(shù)
if (collmax.size() < collmin.size()) {
max = collmin;
min = collmax;
}
//直接指定大小,防止再散列
Map<Object, Integer> map = new HashMap<Object, Integer>(max.size());
for (Object object : max) {
map.put(object, 1);
}
for (Object object : min) {
if (map.get(object) == null) {
csReturn.add(object);
} else {
map.put(object, 2);
}
}
for (Map.Entry<Object, Integer> entry : map.entrySet()) {
if (entry.getValue() == 1) {
csReturn.add(entry.getKey());
}
}
long endTime = System.currentTimeMillis();
System.out.println("集合A和集合B不同的元素:"+csReturn);
System.out.println("使用map方式遍歷, 對比耗時: " + (endTime - beginTime)+"毫秒。");
return csReturn;
}找出相同元素
同樣找出相同元素可以使用如下代碼:
/**
* 找出兩個集合中相同的元素
*
* @param collmax
* @param collmin
* @return
*/
public static Collection getSameListByMap(Collection collmax, Collection collmin) {
//使用LinkedList防止差異過大時,元素拷貝
Collection csReturn = new LinkedList();
Collection max = collmax;
Collection min = collmin;
//先比較大小,這樣會減少后續(xù)map的if判斷次數(shù)
if (collmax.size() < collmin.size()) {
max = collmin;
min = collmax;
}
//直接指定大小,防止再散列
Map<Object, Integer> map = new HashMap<Object, Integer>(max.size());
for (Object object : max) {
map.put(object, 1);
}
for (Object object : min) {
if (map.get(object) != null) {
csReturn.add(object);
}
}
return csReturn;
}以上就是Java找出兩個大數(shù)據(jù)量List集合中的不同元素的方法總結的詳細內(nèi)容,更多關于Java找出List集合中的不同元素的資料請關注腳本之家其它相關文章!
相關文章
Docker?DockerFile部署java?jar項目包及Mysql和Redis的詳細過程
Dockerfile是一種用于構建Docker鏡像的文件格式,可以通過Dockerfile部署Java項目,這篇文章主要給大家介紹了關于Docker?DockerFile部署java?jar項目包及Mysql和Redis的詳細過程,需要的朋友可以參考下2023-12-12
Java實現(xiàn)中文字符串與unicode互轉工具類
這篇文章主要為大家詳細介紹了Java實現(xiàn)中文字符串與unicode互轉的工具類,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-04-04
SpringBoot基于SpringSecurity表單登錄和權限驗證的示例
這篇文章主要介紹了SpringBoot基于SpringSecurity表單登錄和權限驗證的示例。文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-09-09
SpringBoot概述及在idea中創(chuàng)建方式
SpringBoot提供了一種快速使用Spring的方式,基于約定大于配置的思想,可以讓開發(fā)人員不必在配置與邏輯業(yè)務之間進行思維的切換,這篇文章主要介紹了SpringBoot概述及在idea中創(chuàng)建方式,需要的朋友可以參考下2022-09-09
java基礎之初始化ArrayList時直接賦值的4種方式總結
ArrayList是Java中的一個類,它是Java集合框架中的一部分,用于實現(xiàn)動態(tài)數(shù)組,下面這篇文章主要給大家介紹了關于java基礎之初始化ArrayList時直接賦值的4種方式,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-07-07

