从两个字符串数组返回公共元素的最有效方法

时间:2011-12-18 23:44:14

标签: java arrays compare

在Java中,从两个String Arrays返回公共元素的最有效方法是什么?我可以用一对for循环来做,但这似乎不是非常有效。根据我对similar SO question的评论,我能想出的最好的结果是转换为List,然后应用retainAll

List<String> compareList = Arrays.asList(strArr1);
List<String> baseList = Arrays.asList(strArr2);
baseList.retainAll(compareList);

6 个答案:

答案 0 :(得分:5)

EDITED:

这是一个单行:

compareList.retainAll(new HashSet<String>(baseList));

retainAll impl(在AbstractCollection中)迭代this,并在参数上使用contains()。将参数转换为HashSet将导致快速查找,因此retainAll中的循环将尽快执行。

此外,名称baseList暗示它是一个常量,因此如果缓存此值,您将获得显着的性能提升:

static final Set<String> BASE = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList("one", "two", "three", "etc")));

static void retainCommonWithBase(Collection<String> strings) {
    strings.retainAll(BASE);
}

如果要保留原始列表,请执行以下操作:

static List<String> retainCommonWithBase(List<String> strings) {
   List<String> result = new ArrayList<String>(strings);
   result.retainAll(BASE);
   return result;
}

答案 1 :(得分:3)

然后我会使用HashSets(和retainAll),这将使整个检查O(n)(对于第一组查找中的每个元素,如果它存在(contains()),这是O (1)对于HashSet)。 List的创建速度更快(HashSet可能需要处理冲突......)。

请记住,SetList具有不同的语义(列表允许重复元素,空值......)。

答案 2 :(得分:3)

对两个数组进行排序。

排序后,您可以使用两个索引对两个已排序的数组进行一次迭代。

这将是O(NlogN)。

答案 3 :(得分:1)

列表不支持

保留所有内容。改用set:

import java.util.*;
public class Main {
    public static void main(String[] args) {
        String[] strings1={"a","b","b","c"},strings2={"b","c","c","d"};
        List<String> list=Arrays.asList(strings1);
        //list.retainAll(Arrays.asList(strings2)); // throws UnsupportedOperationException
        //System.out.println(list);
        Set<String> set=new LinkedHashSet<String>(Arrays.asList(strings1));
        set.retainAll(Arrays.asList(strings2));
        System.out.println(set);
    }
}

答案 4 :(得分:1)

你想要的是交集。 看到: Intersection and union of ArrayLists in Java

使用基于哈希的集合提供了一个非常快的contains()方法,特别是对于具有优化哈希码的字符串。


如果您可以导入库,可以考虑使用Guava的Sets.intersection。


修改

不知道retainAll方法。

请注意,似乎未覆盖HashSets和LinkedHashSets的AbstractCollection实现是:

public boolean retainAll(Collection c){        boolean modified = false;        Iterator it = iterator();        while(it.hasNext()){            if(!c.contains(it.next())){                it.remove();                modified = true;            }        }        返回修改;    }

这意味着您在集合参数上调用contains()! 这意味着如果你传递一个List参数,那么对于每次迭代,你都会在列表的很多项上进行等于调用!

这就是为什么我不认为使用retainAll的上述实现是好的。

public <T> List<T> intersection(List<T> list1, List<T> list2) {
    boolean firstIsBigger = list1.size() > list2.size();
    List<T> big =  firstIsBigger ? list1:list2;
    Set<T> small =  firstIsBigger ? new HashSet<T>(list2) : new HashSet<T>(list1);
    return big.retainsAll(small)
}

选择将Set用作最小的列表,因为它可以更快地构建集合,并且一个大的列表很好地迭代...

请注意,原始列表参数之一可能会被修改,由您自行制作副本...

答案 5 :(得分:0)

我接受了采访,这个问题是他们在技术面试中问我的问题。我的回答是遵循以下代码:

public static void main(String[] args) {

        String[] temp1 = {"a", "b", "c"};
        String[] temp2 = {"c", "d", "a", "e", "f"};
        String[] temp3 = {"b", "c", "a", "a", "f"};

        ArrayList<String> list1 = new ArrayList<String>(Arrays.asList(temp1));
        System.out.println("list1: " + list1);
        ArrayList<String> list2 = new ArrayList<String>(Arrays.asList(temp2));
        System.out.println("list2: " + list2);
        ArrayList<String> list3 = new ArrayList<String>(Arrays.asList(temp3));
        System.out.println("list3: " + list3);

        list1.retainAll(list2);
        list1.retainAll(list3);
        for (String str : list1)
            System.out.println("Commons: " + str);
}

输出:

list1: [a, b, c]
list2: [c, d, a, e, f]
list3: [b, c, a, a, f]
Commons: a
Commons: c