簡體   English   中英

在Java中查找N個列表之間的公共元素

[英]Finding the common elements between N lists in Java

我需要編寫一個Java程序來查找任意數量的整數(任意長度)的列表或數組的交集(公共元素)。 我想Java列表可能有一個有用的方法可以實現此目的,但是我正在看一下API卻找不到它。

有什么提示嗎?

您可以通過將一個列表的元素復制到新列表中並使用retainAll來找到兩個列表之間的公共元素:

List<T> commonElements = new ArrayList<>(list1);
commonElements.retainAll(list2);

這可以擴展到n列表,因為n列表中的公共元素是[前n-1列表的公共元素]和[第n個列表的元素]的公共元素:

commonElements.retainAll(list3);
commonElements.retainAll(list4);
...

例如

<T> List<T> commonElements(Iterable<? extends List<? extends T>> lists) {
  Iterator<? extends List<? extends T>> it = lists.iterator();
  List<T> commonElements = new ArrayList<T>(it.next());
  while (it.hasNext()) {
    commonElements.retainAll(it.next());
  }
  return commonElements;
}

請注意,如果列表為空,則將失敗,並顯示NoSuchElementException 通過在第一個it.next()之前添加對it.hasNext()的檢查,可以it.hasNext()處理這種情況。

您可以使用Java Collections類的一部分的retainAll()方法:

List<Integer> list1 = new ArrayList<Integer>();
list1.add(1);
list1.add(2);
list1.add(3);
System.out.println("First list has elements: " + list1);

List<Integer> list2 = new ArrayList<Integer>();
list2.add(2);
list2.add(3);
list2.add(4);
System.out.println("Second list has elements: " + list2);

list1.retainAll(list2);
System.out.println("Intersection between the lists is: " + list1);

如果需要將其聚合為任意數量的列表,則可以簡單地調用list1.retainAll(listn) ,其中listn是另一個List

輸出:

First list has elements: [1, 2, 3]
Second list has elements: [2, 3, 4]
Intersection between the lists is: [2, 3]

您可以嘗試使用這種方法來找到交集/公共-

public <T> List<T> common(List<T> list1, List<T> list2) {
        List<T> commonList = new ArrayList<T>();

        for (T t : list1) {
            if(list2.contains(t)) {
                list.add(t);
            }
        }


        return commonList;
    }

或者,您可以使用retainAll()方法-

list1.retainAll(list2); 

你應該仔細考慮使用任何方法之前retainAllremoveAllcontainsAllArrayList是因為contains了一個ArrayListO(n)時間復雜度。 如果ab均為長度為n ArrayList ,則a.retainAll(b)時間復雜度為O(n^2) 如果在循環中使用a.retainAll(b) ,則生成的算法很快將變得完全不切實際。

另一種解決方案是將ArrayList轉換為HashSet HashSet contains的時間復雜度為O(1)

static <T> List<T> commonElements(Iterable<? extends List<? extends T>> lists) {
    Iterator<? extends List<? extends T>> it = lists.iterator();
    Set<T> commonElements = new HashSet<>(it.next());
    while (it.hasNext())
        commonElements.retainAll(new HashSet<>(it.next()));
    return new ArrayList<>(commonElements);
}

當然,如果short List的數量很少,則上面代碼中的所有復制操作都會使此版本的運行速度比@AndyTurner的慢。 您使用哪個版本取決於您的實際情況。

這些解決方案的另一個問題是它們如何處理多重性。 假設第一個列表為[1, 1, 1] 1,1,1 [1, 1, 1] ,第二個列表為[1, 1] 這些列表的交集最合理的解釋是[1, 1] 但是,@ AndyTurner的版本將產生[1, 1, 1] 1,1,1 [1, 1, 1] ,而上述版本將產生[1]

這是一個可以正確處理多重性的版本。 方法參考和Map.merge需要Java 8。

static <T> List<T> commonElements(Iterable<? extends List<? extends T>> lists) {
    Iterator<? extends List<? extends T>> iterator = lists.iterator();
    Map<T, Integer> multiplicities = count(iterator.next());
    while (iterator.hasNext()) {
        Map<T, Integer> listCount = count(iterator.next());
        for (Iterator<Map.Entry<T, Integer>> it = multiplicities.entrySet().iterator(); it.hasNext();) {
            Map.Entry<T, Integer> e = it.next();
            T key = e.getKey();
            Integer count = listCount.get(key);
            if (count == null)
                it.remove();
            else
                e.setValue(Math.min(count, e.getValue()));
        }
    }
    List<T> result = new ArrayList<>();
    for (Map.Entry<T, Integer> e : multiplicities.entrySet())
        result.addAll(Collections.nCopies(e.getValue(), e.getKey()));
    return result;
}

private static <T> Map<T, Integer> count(List<? extends T> list) {
    Map<T, Integer> result = new HashMap<>();
    for (T t : list)
        result.merge(t, 1, Integer::sum);
    return result;
}

您可以如下進行測試

List<Integer> list1 = Arrays.asList(1, 1, 2, 2, 2, 3, 4);
List<Integer> list2 = Arrays.asList(1, 1, 1, 2, 2, 3, 5);
List<Integer> common = commonElements(Arrays.asList(list1, list2));
System.out.println(common);

輸出:

[1, 1, 2, 2, 3]

有許多方法可以改進上述方法。 例如,您可以先處理最小的List以使multiplicities盡可能小。 同樣,在計算listCount ,如果listCount較小,則可以交換listCountmultiplicities 也可以用while (!multiplicities.isEmpty() && iterator.hasNext())替換while ,以便一旦發現交集為空,該算法立即停止。

暫無
暫無

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

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