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

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

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

编辑:

这是一个单行:

 compareList.retainAll(new HashSet(baseList)); 

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

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

 static final Set BASE = Collections.unmodifiableSet(new HashSet(Arrays.asList("one", "two", "three", "etc"))); static void retainCommonWithBase(Collection strings) { strings.retainAll(BASE); } 

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

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

对两个数组排序。

排序后,您可以使用两个索引将两个已排序的数组完全迭代一次。

这将是O(NlogN)。

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

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

列表不支持保留所有内容。 使用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 list=Arrays.asList(strings1); //list.retainAll(Arrays.asList(strings2)); // throws UnsupportedOperationException //System.out.println(list); Set set=new LinkedHashSet(Arrays.asList(strings1)); set.retainAll(Arrays.asList(strings2)); System.out.println(set); } } 

你想要的是交叉点。 请参阅: Java中的ArrayLists的交集和并集

使用基于哈希的集合提供了一个非常快的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  List intersection(List list1, List list2) { boolean firstIsBigger = list1.size() > list2.size(); List big = firstIsBigger ? list1:list2; Set small = firstIsBigger ? new HashSet(list2) : new HashSet(list1); return big.retainsAll(small) } 

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

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

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

 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 list1 = new ArrayList(Arrays.asList(temp1)); System.out.println("list1: " + list1); ArrayList list2 = new ArrayList(Arrays.asList(temp2)); System.out.println("list2: " + list2); ArrayList list3 = new ArrayList(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