簡體   English   中英

檢查許多列表中常見元素的最佳方法?

[英]Optimal way to check for common element among many lists?

我知道ArrayList<>的搜索速度最快( O(1) vs O(n) ), LinkedList<>的插入和刪除速度最快( O(1) vs O(n) )。

我的問題是,如果使用這兩者的組合,檢查多個列表(> 2)的常用元素的最佳方法是什么?

當前方法使用三個列表和一個迭代方法:

out:
for(int a = 0; a < list1.size(); a++) {
    for(int b = 0; b < list2.size(); b++) {
        for(int c = 0; c < list3.size(); c++) {
            if(list1.get(a) == list2.get(b) && list1.get(a) == list3.get(c) ) {
                System.out.println(list1.get(a)); // list2.get(b) or list3.get(c) could have been subbed
                break out;
            }
        }
    } 
}

如何針對效率進行優化?


編輯

感謝許多有用的回復:)我發現最好的工作是使用List .retainAll()函數。

再次,為了找到三個列表中的共同元素,我已經改進了下面的方法。

list1.retainAll(list2);
list1.retainAll(list3);
for(int i : list1) {
    System.out.println(i);
}

假設元素實現hashCode ,您可以獲得所有列表中元素數量的預期時間線性:

public static <T> Set<T> commonElements(List<? extends T> list1, List<? extends T>... lists) {
    // use LinkedList for efficient delete operation
    // make sure elements are distinct to not check the same element multiple times
    List<T> commonElements = new LinkedList<>(new HashSet<>(list1));
    for (List<? extends T> l : lists) {
        // use HashSet for fast contains check
        // keep only elements in the list
        commonElements.retainAll(new HashSet<>(l));
    }
    return new HashSet<>(commonElements);
}

這比您的方法更快,因為HashSet允許在O(1)預期時間內進行查找。

請注意,對於小輸入列表,使用此方法可以使性能更差。

如果您正在尋找性能,那么編寫一個使用哈希查找的API會更好。 list.retainAll()雖然是一個干凈的api調用,但在內部它會進行大量的處理,特別是如果傳遞的參數也是一個列表。 看看這里數組列表的retainAll()的實現 -

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayList.java#ArrayList.retainAll%28java.util.Collection%29

您可以查看正在使用的列表的實現,並查看是否可以滿足您的性能要求。 如果沒有,你可以試試這樣的東西......寫一個api來返回常見的元素。

private static Set getCommonElements (List dataList, Set dataSet) {
    Set commonDataSet = new LinkedHashSet();

    if (dataSet == null || dataSet.isEmpty()) 
        return commonDataSet;

    for (Object elem: dataList) {
        if (dataSet.contains(elem)) {//Hash based look up. Will be faster.
            commonDataSet.add(elem);
        }
    }

    return commonDataSet;
}

然后重復調用,如下所示

Set resultSet= new LinkedHashSet(list1);
resultSet= getCommonElements(list2, resultSet);
resultSet= getCommonElements(list3, resultSet);

如果您不關心訂單,可以使用hashset而不是linkedhashset。

這樣做的一個問題是,這是迭代列表中的元素,這些元素將高於公共元素。 如果我們可以遍歷公共元素並在列表中查找,那會好得多。 但為此,您可能必須將列表中的數據保存在散列烘焙列表/集中或維護排序列表。 否則查找將是昂貴的。

您可以使用Java中的HashMap對其進行優化。 假設您有n個列表,每個列表包含m個元素

算法:

  make hashmap h;
  loop i=0 to m
     loop j=0 to n
       increment j[i] key in hashmap h
     loop end
  loop end

loop i=0 to m for any list
  check hashmap value for the element, if equals to n
  print element

復雜度o(nm),如果n <<< m則復雜度(n)

使用retainAll(List<>)函數而不是迭代每個元素可以顯着減少運行時間並提高可讀性。

list1.retainAll(list2);
list1.retainAll(list3);

out:
for(int a = 0; a < list1.size(); a++) {
    for(int b = 0; b < list2.size(); b++) {
        for(int c = 0; c < list3.size(); c++) {
            if(list1.get(a) == list2.get(b) && list1.get(a) == list3.get(c) ) {
                System.out.println(list1.get(a));
                break out;
            }
        }
    } 
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM