我需要编写一个Java程序来查找任意数量的列表或整数数组(任意长度)的交集(公共元素)。我想Java Lists可能有一个有用的方法来实现这一点,但我正在看看API并且找不到它。
任何提示?
答案 0 :(得分:5)
您可以通过将一个列表的元素复制到新列表中并使用retainAll
来找到两个列表之间的公共元素:
List<T> commonElements = new ArrayList<>(list1);
commonElements.retainAll(list2);
这可以扩展到n
列表,因为n
列表中的常见元素是[第一个n-1
列表的常见元素]和[元素]的共同元素n
- 列表]:
commonElements.retainAll(list3);
commonElements.retainAll(list4);
...
e.g。
<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.hasNext()
之前添加it.next()
检查,可以直接处理此情况。
答案 1 :(得分:1)
您可以使用属于Java fbLoginManager.loginBehavior = FBSDKLoginBehavior.Browser
类的retainAll()
方法:
Collections
如果您需要针对任意数量的列表进行汇总,则只需致电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
答案 2 :(得分:0)
您可以尝试使用此方法查找intersection / common -
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);
答案 3 :(得分:0)
在使用任何方法retainAll
,removeAll
或containsAll
与ArrayList
之前,您应该仔细考虑非常,因为{{1} } contains
时间复杂度ArrayList
。如果O(n)
和a
都是b
长度ArrayList
,则n
的时间复杂度为a.retainAll(b)
。如果在循环中使用O(n^2)
,则生成的算法很快变得完全不切实际。
另一种解决方案是将a.retainAll(b)
转换为ArrayList
s。 HashSet
的{{1}}时间复杂度为contains
。
HashSet
当然,如果有少量短O(1)
s,则上述代码中的所有复制都会使此版本比@ AndyTurner慢。您使用的版本取决于您的具体情况。
这些解决方案的另一个问题是它们如何处理多重性。假设第一个列表是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);
}
,第二个列表是List
。对这些列表的交集最合理的解释是[1, 1, 1]
。但是,@ AndyTurner的版本将生成[1, 1]
,上述版本将生成[1, 1]
。
这是一个正确处理多重性的版本。方法引用和[1, 1, 1]
需要Java 8。
[1]
您可以按照以下方式进行测试
Map.merge
输出:
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
。此外,您可以将multiplicities
替换为listCount
,以便一旦发现交叉点为空,算法就会立即停止。