首頁 > 軟體

Java中兩個List之間的比較方法(差集、交集和並集)

2022-06-16 10:01:06

實現比較兩個List之間的差異,包括獲取兩List的差集,交集,並集(不去重&去重)的API解法和優化解法的解決方案。

求差集

/**
 * 差集(基於API解法) 適用於小資料量
 * 求List1中有的但是List2中沒有的元素
 * 時間複雜度 O(list1.size() * list2.size())
 */
public static List<String> subList(List<String> list1, List<String> list2) {
    list1.removeAll(list2);
    return list1;
}
 
/**
 * 差集(基於常規解法)優化解法1 適用於中等資料量
 * 求List1中有的但是List2中沒有的元素
 * 空間換時間降低時間複雜度
 * 時間複雜度O(Max(list1.size(),list2.size()))
 */
public static List<String> subList1(List<String> list1, List<String> list2) {
    //空間換時間 降低時間複雜度
    Map<String, String> tempMap = new HashMap<>();
    for(String str:list2){
        tempMap.put(str,str);
    }
    //LinkedList 頻繁新增刪除 也可以ArrayList容量初始化為List1.size(),防止資料量過大時頻繁擴容以及陣列複製
    List<String> resList = new LinkedList<>();
    for(String str:list1){
        if(!tempMap.containsKey(str)){
            resList.add(str);
        }
    }
    return resList;
}
 
/**
 * 差集(基於java8新特性)優化解法2 適用於巨量資料量
 * 求List1中有的但是List2中沒有的元素
 */
public static List<String> subList2(List<String> list1, List<String> list2) {
    Map<String, String> tempMap = list2.parallelStream().collect(Collectors.toMap(Function.identity(), Function.identity(), (oldData, newData) -> newData));
    return list1.parallelStream().filter(str->{
        return !tempMap.containsKey(str);
    }).collect(Collectors.toList());
}

求交集

/**
 * 交集(基於API解法) 適用於小資料量
 * 求List1和List2中都有的元素
 * 時間複雜度 O(list1.size() * list2.size())
 */
public static List<String> intersectList(List<String> list1, List<String> list2){
    list1.retainAll(list2);
    return list1;
}
/**
 * 交集(基於常規解法) 優化解法1  適用於中等資料量
 * 求List1和List2中都有的元素
 * 時間複雜度O(Max(list1.size(),list2.size()))
 */
public static List<String> intersectList1(List<String> list1, List<String> list2){
    //空間換時間 降低時間複雜度
    Map<String, String> tempMap = new HashMap<>();
    for(String str:list2){
        tempMap.put(str,str);
    }
    //LinkedList 頻繁新增刪除 也可以ArrayList容量初始化為List1.size(),防止資料量過大時頻繁擴容以及陣列複製
    List<String> resList = new LinkedList<>();
    for(String str:list1){
        if(tempMap.containsKey(str)){
            resList.add(str);
        }
    }
    return resList;
}
/**
 * 交集(基於java8新特性)優化解法2 適用於巨量資料量
 * 求List1和List2中都有的元素
 */
public static List<String> intersectList2(List<String> list1, List<String> list2){
    Map<String, String> tempMap = list2.parallelStream().collect(Collectors.toMap(Function.identity(), Function.identity(), (oldData, newData) -> newData));
    return list1.parallelStream().filter(str->{
        return tempMap.containsKey(str);
    }).collect(Collectors.toList());
}

求並集(不去重)

/**
 * 並集(不去重)
 * 合併list1和list2 不考慮去除重複元素
 * 陣列擴容 陣列copy
 * @param list1
 * @param list2
 * @return
 */
public static List<String> mergeList(List<String> list1, List<String> list2){
    list1.addAll(list2);
    return list1;
}

求並集(去重)

/**
 * 並集(去重) 基於API解法
 * 合併list1和list2 去除重複元素
 * 時間複雜度主要取決於removeAll 取差集 O(list1.size() * list2.size())
 */
public static List<String> distinctMergeList(List<String> list1, List<String> list2){
    //第一步 先求出list1與list2的差集
    list1.removeAll(list2);
    //第二部 再合併list1和list2
    list1.addAll(list2);
    return list1;
}
/**
 * 並集(去重) 基於Java8新特性 適用於巨量資料量
 * 合併list1和list2 去除重複元素
 */
public static List<String> distinctMergeList1(List<String> list1, List<String> list2){
    //第一步 先求出list1與list2的差集
    list1 = subList2(list1,list2);
    //第二部 再合併list1和list2
    list1.addAll(list2);
    return list1;
}

實際業務場景

根據客戶需求,業務提交稽核需要很直觀的看到此次提交的資料關聯產品的狀態變更。

第一種情況:新增的渠道授權關聯的產品,所有的授權產品均為新增;

第二種情況:已稽核通過的渠道授權重新提交授權稽核的,要直觀的標記出此次提交稽核渠道關聯授權產品新增了那些,刪除了那些,更改了那些等資訊;

第三種情況:作廢渠道提交的稽核要標註出所有的關聯授權產品為刪除狀態。

授權關聯產品為申請表單中一對多關聯表,前端展示根據資料的不同狀態展示不同的樣式:

  • 新增授權產品顯示為紅色
  • 刪除授權產品顯示為刪除線樣式(中劃線 )
  • 更新授權產品顯示標註紅色*號

建立關聯產品Vo

首先模擬建立一個產品的實體,此處只簡單列入幾個屬性,在比較所關聯產品資訊是否是變更狀態的時候根據實際業務需要需重寫 hashCode 和 equals 方法。

class ProductVo{
    private String id;
    private String name;
    //其他屬性不在列入
    //資料狀態(新增:insert; 更新:update; 刪除:delete)
    private String status;
    //get set 省略
    //如有必要重寫hashCode equals
}

業務程式碼實現

業務實現主要通過 空間換時間 方式降低時間複雜度,先把List轉為Map,利用map的 get 和 containsKey 方法理想情況下O(1)的時間複雜度降低巢狀的兩次List遍歷。

/**
 * 渠道授權新提交關聯授權產品 與 歷史已審批授權資訊對比處理標註授權產品的狀態資訊<br/>
 * 前端可以根據不同的資料狀態顯示不同的樣式<br/>
 * 用於稽核人員直接看到此次提交稽核新增了那些授權,取消了那些授權,更改了那些授權
 * @param oldList  原始關聯授權產品列表
 * @param newList  提交關聯授權產品列表
 * @return
 */
public List<ProductVo> productStatusHandle(List<ProductVo> oldList,List<ProductVo> newList){
    //原始關聯授權產品為空 並且 新關聯授權產品為空(基本不存在此場景)
    if((oldList == null || oldList.isEmpty()) && (newList == null || newList.isEmpty())){
        return Collections.emptyList();
    }
    //原始關聯授權產品為空 則提交關聯授權產品全部為新增
    if(oldList == null || oldList.isEmpty()){
        return newList.stream().map(vo->{
            vo.setStatus("insert");
            return vo;
        }).collect(Collectors.toList());
    }
    //提交關聯授權產品為空 則刪除之前所有的產品授權
    if(newList == null || newList.isEmpty()){
        return oldList.stream().map(vo->{
            vo.setStatus("delete");
            return vo;
        }).collect(Collectors.toList());
    }
    //原始關聯授權產品與此次提交關聯授權產品均不為空
    List<ProductVo> resList = new LinkedList<>();
    //空間換時間 降低時間複雜度
    //說明:list中不會存在重複(ID相同)的授權產品 否則此toMap收集會丟擲異常
    Map<String, ProductVo> oldMap = oldList.stream().collect(Collectors.toMap(ProductVo::getId, Function.identity()));
    Map<String, ProductVo> newMap = newList.stream().collect(Collectors.toMap(ProductVo::getId, Function.identity()));
    for(ProductVo vo:newList){
        ProductVo productVo = oldMap.get(vo.getId());
        //提交關聯授權產品在原始關聯授權產品
        if(productVo != null){
            if(!vo.equals(productVo)){//重寫hashCode與equals自定義規則 用於判定是否資料更新
                vo.setStatus("update");
            }
        }else{//提交稽核資料不在舊資料之列
            vo.setStatus("insert");
        }
        resList.add(vo);
    }
    //原始關聯授權產品是否存在已取消的情況
    for(ProductVo vo:oldList){
        if(!newMap.containsKey(vo.getId())){
            vo.setStatus("delete");
            resList.add(vo);
        }
    }
    return resList;
}

總結

到此這篇關於Java中兩個List之間的比較方法的文章就介紹到這了,更多相關Java中List比較內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com