在许多列表中检查公共元素的最佳方法是什么?

时间:2016-08-05 07:23:17

标签: java algorithm performance optimization iteration

我了解ArrayList<>搜索速度最快(O(1)O(n)),LinkedList<>的插入速度最快;正在删除(O(1)O(n))。

我的问题是,如果使用这两者的组合,检查多个列表(&gt; 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);
}

4 个答案:

答案 0 :(得分:2)

假设元素实现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)预期时间内进行查找。

请注意,对于小输入列表,使用此方法可以使性能更差。

答案 1 :(得分: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。

这样做的一个问题是,这是迭代列表中的元素,这些元素将高于公共元素。如果我们可以遍历公共元素并在列表中查找,那会好得多。但为此,您可能必须将列表中的数据保存在散列烘焙列表/集中或维护排序列表。否则查找将是昂贵的。

答案 2 :(得分:0)

您可以使用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 <&lt;&lt;那么,复杂性(n)

答案 3 :(得分:0)

使用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;
            }
        }
    } 
}