[英]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);
你應該仔細考慮使用任何方法之前retainAll
, removeAll
或containsAll
與ArrayList
是因為contains
了一個ArrayList
有O(n)
時間復雜度。 如果a
和b
均為長度為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
較小,則可以交換listCount
和multiplicities
。 也可以用while (!multiplicities.isEmpty() && iterator.hasNext())
替換while
,以便一旦發現交集為空,該算法立即停止。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.