使用Java獲取List交集數(shù)據(jù)的實現(xiàn)方案小結(jié)
需求背景
今天遇到一個小需求,當用戶上傳了一個關(guān)于用戶數(shù)據(jù)的列表,我們需要將其與數(shù)據(jù)庫中已有的用戶數(shù)據(jù)進行比較。假設(shè)數(shù)據(jù)庫中的用戶數(shù)據(jù)存儲在集合A中,而用戶上傳的數(shù)據(jù)存儲在集合B中。我們需要確定集合B中有多少數(shù)據(jù)在集合A中,以及有多少數(shù)據(jù)不在集合A中,并記錄這些信息到日志中。那么,我們應該如何處理這個需求呢?
解決方案
一、如何查找兩個集合的重復數(shù)據(jù)?
如果兩個集合中存放的都是String類型數(shù)據(jù),那這個操作就會簡單很多,這里先初始化一下兩個集合的數(shù)據(jù)作為參考,接著給大家一些參考的方法
List<String> listA = Arrays.asList("Apple", "Banana", "Cherry", "Date");
List<String> listB = Arrays.asList("Banana", "Date", "Fig", "Grape");
1、使用retainAll()
retainAll()方法會修改原始的集合A,使其只包含同時存在于集合A和集合B中的元素。
// 直接在集合A上使用retainAll()方法,它會保留只存在于集合A和集合B中的元素
listA.retainAll(listB);
System.out.println("Elements in both lists: " + listA);
2、使用stream()和filter()
// 使用stream()方法和filter()方法找到兩個集合的交集
List<String> intersection = listA.stream()
.filter(listB::contains)
.collect(Collectors.toList());
System.out.println("Elements in both lists: " + intersection);
3、使用stream()和anyMatch()
// 使用anyMatch()檢查集合A中的每個元素是否在集合B中
List<String> intersection = listA.stream()
.filter(element -> listB.anyMatch(b -> b.equals(element)))
.collect(Collectors.toList());
System.out.println("Elements in both lists: " + intersection);
上面的代碼使用 listB.anyMatch(b -> b.equals(element))。對于 listA 中的每個元素,它創(chuàng)建一個新的流來遍歷 listB 的所有元素,直到找到相等的元素或遍歷完所有元素。每次調(diào)用 anyMatch 都會遍歷 listB,這同樣是一個 O(n) 操作;但它在內(nèi)部使用了流,這會增加額外的開銷。
4、使用Collection的intersection()
如果你想要獲取兩個集合的交集,可以使用Collection接口提供的intersection()方法:
Set<String> intersectionSet = new HashSet<>(listA);
intersectionSet.retainAll(listB);
List<String> intersection = new ArrayList<>(intersectionSet);
System.out.println("Elements in both lists: " + intersection);
5、查詢集合B中不與集合A重合的數(shù)據(jù)
這時候如果要查詢包含集合B中不與集合A重合的數(shù)據(jù),我們只要簡單修改一下上面的方法即可,我們還是使用Java 8的Stream API來創(chuàng)建一個新的集合,這個集合包含集合B中獨有的元素。
// 使用Stream API找出集合B中不包含在集合A中的元素
List<String> uniqueInB = listB.stream()
.filter(element -> !listA.contains(element))
.collect(Collectors.toList());
// 打印集合B中不和集合A重合的數(shù)據(jù)
System.out.println("Elements in list B only: " + uniqueInB);
在數(shù)據(jù)量不大的情況下,使用Stream API的方法通常是足夠高效的,并且代碼簡潔易讀。如果數(shù)據(jù)量非常大,您可能需要考慮其他方法,例如將集合轉(zhuǎn)換為HashSet以提高查找效率,或者使用并行流(parallel streams)來利用多核處理器。
二、假設(shè)集合A的數(shù)據(jù)更多,該如何優(yōu)化?
如果集合A的數(shù)據(jù)比集合B中的數(shù)據(jù)更多,為了提高效率,我們可以做一些調(diào)整。這里有兩個優(yōu)化點:
- 我們使用了
listB::contains來檢查一個元素是否在集合B中。如果集合A更大,那么使用listA::contains可能會更高效,因為遍歷較小的集合將減少必要的contains檢查次數(shù)。 .contains方法的性能取決于被搜索的集合的類型。對于ArrayList,contains方法的時間復雜度是 O(n),它會遍歷整個列表來查找元素,而對于HashSet,時間復雜度是 O(1),因為它使用哈希表進行查找。我們這時候就可以將集合A轉(zhuǎn)換為一個HashSet。
完整的示例代碼:
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
// 假設(shè)集合A和集合B已經(jīng)初始化
List<String> listA = Arrays.asList("Apple", "Banana", "Cherry", "Date", "Fig", "Grape");
List<String> listB = Arrays.asList("Banana", "Date", "Fig");
// 將集合A轉(zhuǎn)換為HashSet,以提高查找效率
Set<String> setA = new HashSet<>(listA);
// 生成集合C,保存集合A和集合B的重合數(shù)據(jù)
List<String> listC = listB.stream()
.filter(setA::contains) // 使用HashSet來檢查交集,提高效率
.collect(Collectors.toList()); // 收集結(jié)果到一個新的列表
// 生成集合D,保存集合B中沒有和集合A重合的數(shù)據(jù)
List<String> listD = listB.stream()
.filter(element -> !setA.contains(element)) // 使用HashSet來檢查差異,提高效率
.collect(Collectors.toList()); // 收集結(jié)果到一個新的列表
// 打印結(jié)果
System.out.println("List C (common elements): " + listC);
System.out.println("List D (unique to list B): " + listD);
補充說明:
如果集合A和集合B都是使用
List實現(xiàn),那么兩種方法的時間復雜度在本質(zhì)上是相同的。每次調(diào)用contains方法時,都會在另一個列表上進行線性搜索,這意味著每次調(diào)用的時間復雜度都是O(n)。對集合A中的每個元素調(diào)用
listB::contains,如果集合A有n個元素,集合B有m個元素,那么總的時間復雜度就是O(n*m)。對集合B中的每個元素調(diào)用
listA::contains,同樣地,如果集合A有n個元素,集合B有m個元素,那么總的時間復雜度也是O(n*m)。如果集合A遠大于集合B,遍歷較小的集合B通常在實際應用中效率更高,即使時間復雜度在理論上是相等的。這是因為較小的集合遍歷次數(shù)更少,從而減少了實際執(zhí)行的總步驟數(shù)。不過,這種效率的差異只能在實際運行時才能體現(xiàn)。
三、如果集合中存放的是對象,該如何操作?
通常情況下,我們不會在集合中存放字符串,都是放一些對象數(shù)據(jù),這時候該如何獲取呢?在這里我們定義一個Person作為示例,假設(shè)Person對象在name和age屬性都相同時被認為是相等的。
在Java中使用contains方法來檢查一個集合是否包含某個對象時,就需要重寫對象的equals和hashCode方法。這是因為contains方法的實現(xiàn)依賴于equals方法來比較對象,而hashCode方法則用于快速查找和確定對象在散列數(shù)據(jù)結(jié)構(gòu)(如HashSet或HashMap)中的位置。
equals和hashCode方法之間有一個重要的一致性約定:
- 如果兩個對象根據(jù)
equals方法是相等的,那么它們的hashCode方法也必須返回相同的值。 - 如果兩個對象的
hashCode值不同,那么它們一定不相等(根據(jù)equals方法)。
這個約定對于HashSet、HashMap等集合的正確運作至關(guān)重要。如果你只重寫了equals方法而沒有重寫hashCode方法,可能會導致集合的行為不符合預期,例如,即使兩個對象相等,HashSet也可能認為它們是不同的對象并存儲兩個副本。
示例代碼
public class Person {
private String name;
private int age;
// 構(gòu)造函數(shù)、getter和setter省略
@Override
public boolean equals(Object o) {
if (this == o) return true; // 如果是同一個對象,直接返回true
if (o == null || getClass() != o.getClass()) return false; // 如果對象為空或者類類型不一致,返回false
Person person = (Person) o; // 向下轉(zhuǎn)型
// 比較name和age屬性
return Objects.equals(name, person.name) && age == person.age;
}
@Override
public int hashCode() {
// 使用31作為質(zhì)數(shù),可以減少哈希沖突
int result = 17;
result = 31 * result + Objects.hashCode(name); // 根據(jù)name計算哈希碼
result = 31 * result + Integer.hashCode(age); // 根據(jù)age計算哈希碼
return result;
}
}
在這個實現(xiàn)中,equals方法首先檢查是否是同一個對象,然后檢查對象是否為空或者是否是不同的類型。如果這些檢查都通過了,它會通過Objects.equals方法比較name屬性,并直接比較age屬性的值。
hashCode方法使用了一個固定的質(zhì)數(shù)(在這里是17)作為初始哈希碼。然后,它使用31作為乘數(shù)(31是一個質(zhì)數(shù),通常用于計算哈希碼,因為它有助于避免哈希沖突)。hashCode方法分別對name和age屬性調(diào)用Objects.hashCode和Integer.hashCode方法來計算它們的哈希碼,并將它們組合起來。
以上就是使用Java獲取List交集數(shù)據(jù)的實現(xiàn)方案小結(jié)的詳細內(nèi)容,更多關(guān)于Java獲取List交集數(shù)據(jù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
chatgpt java環(huán)境調(diào)用源碼實現(xiàn)demo
這篇文章主要介紹了chatgpt java環(huán)境調(diào)用源碼實現(xiàn)demo,本文結(jié)合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02
SpringAnimation 實現(xiàn)菜單從頂部彈出從底部消失動畫效果
最近做項目遇到這樣一個需求,要求實現(xiàn)一種菜單,菜單從頂部彈入,然后從底部消失,頂部彈入時,有一個上下抖動的過程,底部消失時,先向上滑動,然后再向下滑動消失。下面給大家?guī)砹藢崿F(xiàn)代碼,感興趣的朋友一起看看吧2018-05-05
mybatis中查詢結(jié)果為空時不同返回類型對應返回值問題
這篇文章主要介紹了mybatis中查詢結(jié)果為空時不同返回類型對應返回值問題,本文分幾種方法給大家介紹的非常詳細,需要的朋友可以參考下2019-10-10

