在Java中查找N个列表之间的公共元素

时间:2015-11-09 12:02:40

标签: java arrays list collections intersection

我需要编写一个Java程序来查找任意数量的列表或整数数组(任意长度)的交集(公共元素)。我想Java Lists可能有一个有用的方法来实现这一点,但我正在看看API并且找不到它。

任何提示?

4 个答案:

答案 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)

在使用任何方法retainAllremoveAllcontainsAllArrayList之前,您应该仔细考虑非常,因为{{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较小,您可以交换listCountlistCount。此外,您可以将multiplicities替换为listCount,以便一旦发现交叉点为空,算法就会立即停止。