数组列表在 Java 中的交集和并集

有什么办法吗? 我一直在找,但是找不到。

另一个问题: 我需要这些方法来过滤文件。 有些是 AND过滤器,有些是 OR过滤器(如集合论中的过滤器) ,所以我需要根据所有文件和包含这些文件的统一/交叉数组列表进行过滤。

我是否应该使用不同的数据结构来保存文件?还有什么能提供更好的运行时吗?

342449 次浏览

您可以从 Apache Commons使用 CollectionUtils

list1.retainAll(list2) - is intersection

联合将是 removeAll然后是 addAll

在 Collection 文档中查找更多信息(ArrayList 是一个集合) Http://download.oracle.com/javase/1.5.0/docs/api/java/util/collection.html

Collection (因此数组列表也是如此)具有:

col.retainAll(otherCol) // for intersection
col.addAll(otherCol) // for union

如果接受重复,则使用 List 实现; 如果不接受重复,则使用 Set 实现:

Collection<String> col1 = new ArrayList<String>(); // {a, b, c}
// Collection<String> col1 = new TreeSet<String>();
col1.add("a");
col1.add("b");
col1.add("c");


Collection<String> col2 = new ArrayList<String>(); // {b, c, d, e}
// Collection<String> col2 = new TreeSet<String>();
col2.add("b");
col2.add("c");
col2.add("d");
col2.add("e");


col1.addAll(col2);
System.out.println(col1);
//output for ArrayList: [a, b, c, b, c, d, e]
//output for TreeSet: [a, b, c, d, e]

如果您有您的数据集,您可以使用番石榴的 Sets类。

我认为你应该使用 Set来保存文件,如果你想做交叉和联合对他们。然后,您可以使用 番石榴集合类来执行 unionintersectionPredicate过滤。这些方法与其他建议的区别在于,所有这些方法都创建两个集合的并集、交集等的惰性 观点。ApacheCommons 创建一个新的集合并将数据复制到它。retainAll通过从一个集合中删除元素来更改它。

联合和交集只定义为集合,而不是列表。如您所提到的。

检查 番石榴库中的过滤器。番石榴也提供了真正的 交叉点和联合点

 static <E> Sets.SetView<E >union(Set<? extends E> set1, Set<? extends E> set2)
static <E> Sets.SetView<E> intersection(Set<E> set1, Set<?> set2)

下面是一个不使用任何第三方库的普通实现。与 retainAllremoveAlladdAll相比,这些方法的主要优势在于它们不会修改方法的原始列表输入。

public class Test {


public static void main(String... args) throws Exception {


List<String> list1 = new ArrayList<String>(Arrays.asList("A", "B", "C"));
List<String> list2 = new ArrayList<String>(Arrays.asList("B", "C", "D", "E", "F"));


System.out.println(new Test().intersection(list1, list2));
System.out.println(new Test().union(list1, list2));
}


public <T> List<T> union(List<T> list1, List<T> list2) {
Set<T> set = new HashSet<T>();


set.addAll(list1);
set.addAll(list2);


return new ArrayList<T>(set);
}


public <T> List<T> intersection(List<T> list1, List<T> list2) {
List<T> list = new ArrayList<T>();


for (T t : list1) {
if(list2.contains(t)) {
list.add(t);
}
}


return list;
}
}

标记的解决方案效率不高。它具有 O (n ^ 2)时间复杂度。我们所能做的就是对两个列表进行排序,然后执行一个如下所示的交集算法。

private  static ArrayList<Integer> interesect(ArrayList<Integer> f, ArrayList<Integer> s) {
ArrayList<Integer> res = new ArrayList<Integer>();


int i = 0, j = 0;
while (i != f.size() && j != s.size()) {


if (f.get(i) < s.get(j)) {
i ++;
} else if (f.get(i) > s.get(j)) {
j ++;
} else {
res.add(f.get(i));
i ++;  j ++;
}
}




return res;
}

这个复杂度是 O (n logn + n) ,它的复杂度是 O (n logn)。 联合是以类似的方式完成的。只要确保对 if-elseif-else 语句进行了适当的修改。

如果愿意,你也可以使用迭代器(我知道它们在 C + + 中效率更高,但我不知道在 Java 中是否也是如此)。

这里有一种方法可以让你与流交叉(记住你必须使用 java 8来处理流) :

List<foo> fooList1 = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<foo> fooList2 = new ArrayList<>(Arrays.asList(new foo(), new foo()));
fooList1.stream().filter(f -> fooList2.contains(f)).collect(Collectors.toList());

具有不同类型的列表的示例。如果你在 foo 和 bar 之间有一个关系,并且你可以从 foo 中得到 bar-object,那么你就可以修改你的数据流:

List<foo> fooList = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<bar> barList = new ArrayList<>(Arrays.asList(new bar(), new bar()));


fooList.stream().filter(f -> barList.contains(f.getBar()).collect(Collectors.toList());
  • RetainAll 将修改您的列表
  • 番石榴没有针对 List 的 API (仅针对 set)

我发现 ListUtils 对这个用例非常有用。

如果不想修改现有列表,可以使用 org.apache.commons 集合中的 ListUtils。

ListUtils.intersection(list1, list2)

这篇文章是相当老,但它仍然是第一个弹出在谷歌时,寻找这个主题。

我想给出一个更新,使用 Java8流在一行中做(基本上)同样的事情:

List<T> intersect = list1.stream()
.filter(list2::contains)
.collect(Collectors.toList());


List<T> union = Stream.concat(list1.stream(), list2.stream())
.distinct()
.collect(Collectors.toList());

如果有人有更好/更快的解决方案,请让我知道,但这个解决方案是一个很好的一行程序,可以很容易地包含在一个方法中,而不需要添加不必要的助手类/方法,同时仍然保持可读性。

如果列表中的对象是散列的(即有一个不错的 hashCode 和 equals 函数) ,那么表之间最快的方法大约是。Size > 20是为两个列表中较大的一个构造一个 HashSet。

public static <T> ArrayList<T> intersection(Collection<T> a, Collection<T> b) {
if (b.size() > a.size()) {
return intersection(b, a);
} else {
if (b.size() > 20 && !(a instanceof HashSet)) {
a = new HashSet(a);
}
ArrayList<T> result = new ArrayList();
for (T objb : b) {
if (a.contains(objb)) {
result.add(objb);
}
}
return result;
}
}

最终解决方案:

//all sorted items from both
public <T> List<T> getListReunion(List<T> list1, List<T> list2) {
Set<T> set = new HashSet<T>();
set.addAll(list1);
set.addAll(list2);
return new ArrayList<T>(set);
}


//common items from both
public <T> List<T> getListIntersection(List<T> list1, List<T> list2) {
list1.retainAll(list2);
return list1;
}


//common items from list1 not present in list2
public <T> List<T> getListDifference(List<T> list1, List<T> list2) {
list1.removeAll(list2);
return list1;
}

我也在处理类似的情况,到这里寻求帮助。最终找到了我自己的数组解决方案。 ArrayList 缺席日期 = new ArrayList () ;//将存储 Array1-Array2

注意: 如果可以帮助某人到达这个页面寻求帮助,请发表这篇文章。

ArrayList<String> AbsentDates = new ArrayList<String>();//This Array will store difference
public void AbsentDays() {
findDates("April", "2017");//Array one with dates in Month April 2017
findPresentDays();//Array two carrying some dates which are subset of Dates in Month April 2017


for (int i = 0; i < Dates.size(); i++) {


for (int j = 0; j < PresentDates.size(); j++) {


if (Dates.get(i).equals(PresentDates.get(j))) {


Dates.remove(i);
}


}
AbsentDates = Dates;
}
System.out.println(AbsentDates );
}

如果数字匹配比我正在检查它是第一次出现或不在“ indexOf ()”的帮助下,如果数字匹配第一次然后打印和保存到一个字符串,这样,当下一次相同的数字匹配时,它将不会打印,因为“ indexOf ()”条件将是假的。

class Intersection
{
public static void main(String[] args)
{
String s="";
int[] array1 = {1, 2, 5, 5, 8, 9, 7,2,3512451,4,4,5 ,10};
int[] array2 = {1, 0, 6, 15, 6, 5,4, 1,7, 0,5,4,5,2,3,8,5,3512451};




for (int i = 0; i < array1.length; i++)
{
for (int j = 0; j < array2.length; j++)
{
char c=(char)(array1[i]);
if(array1[i] == (array2[j])&&s.indexOf(c)==-1)
{
System.out.println("Common element is : "+(array1[i]));
s+=c;
}
}
}
}

}

首先,我将数组的所有值复制到一个数组中,然后将重复的值移除到数组中。第12行,解释如果相同的数字出现的时间超过了时间,然后将一些额外的垃圾值放入“ j”位置。最后,从开始到结束遍历,检查是否出现相同的垃圾值,然后丢弃。

public class Union {
public static void main(String[] args){


int arr1[]={1,3,3,2,4,2,3,3,5,2,1,99};
int arr2[]={1,3,2,1,3,2,4,6,3,4};
int arr3[]=new int[arr1.length+arr2.length];


for(int i=0;i<arr1.length;i++)
arr3[i]=arr1[i];


for(int i=0;i<arr2.length;i++)
arr3[arr1.length+i]=arr2[i];
System.out.println(Arrays.toString(arr3));


for(int i=0;i<arr3.length;i++)
{
for(int j=i+1;j<arr3.length;j++)
{
if(arr3[i]==arr3[j])
arr3[j]=99999999;          //line  12
}
}
for(int i=0;i<arr3.length;i++)
{
if(arr3[i]!=99999999)
System.out.print(arr3[i]+" ");
}
}
}

在 Java8中,我使用如下简单的 helper 方法:

public static <T> Collection<T> getIntersection(Collection<T> coll1, Collection<T> coll2){
return Stream.concat(coll1.stream(), coll2.stream())
.filter(coll1::contains)
.filter(coll2::contains)
.collect(Collectors.toSet());
}


public static <T> Collection<T> getMinus(Collection<T> coll1, Collection<T> coll2){
return coll1.stream().filter(not(coll2::contains)).collect(Collectors.toSet());
}


public static <T> Predicate<T> not(Predicate<T> t) {
return t.negate();
}

您可以使用 commons-Collections4 工具箱

Collection<Integer> collection1 = Arrays.asList(1, 2, 4, 5, 7, 8);
Collection<Integer> collection2 = Arrays.asList(2, 3, 4, 6, 8);


Collection<Integer> intersection = CollectionUtils.intersection(collection1, collection2);
System.out.println(intersection); // [2, 4, 8]


Collection<Integer> union = CollectionUtils.union(collection1, collection2);
System.out.println(union); // [1, 2, 3, 4, 5, 6, 7, 8]


Collection<Integer> subtract = CollectionUtils.subtract(collection1, collection2);
System.out.println(subtract); // [1, 5, 7]

经过测试,这是我最好的交叉方法。

比纯 HashSet 方法更快的速度。下面的 HashSet 和 HashMap 对于超过100万条记录的数组具有类似的性能。

至于 Java8Stream 方法,对于大于10k 的数组来说,速度相当慢。

希望这个能帮上忙。

public static List<String> hashMapIntersection(List<String> target, List<String> support) {
List<String> r = new ArrayList<String>();
Map<String, Integer> map = new HashMap<String, Integer>();
for (String s : support) {
map.put(s, 0);
}
for (String s : target) {
if (map.containsKey(s)) {
r.add(s);
}
}
return r;
}
public static List<String> hashSetIntersection(List<String> a, List<String> b) {
Long start = System.currentTimeMillis();


List<String> r = new ArrayList<String>();
Set<String> set = new HashSet<String>(b);


for (String s : a) {
if (set.contains(s)) {
r.add(s);
}
}
print("intersection:" + r.size() + "-" + String.valueOf(System.currentTimeMillis() - start));
return r;
}


public static void union(List<String> a, List<String> b) {
Long start = System.currentTimeMillis();
Set<String> r= new HashSet<String>(a);
r.addAll(b);
print("union:" + r.size() + "-" + String.valueOf(System.currentTimeMillis() - start));
}

基于通用键的两个不同对象列表的交集-Java8

 private List<User> intersection(List<User> users, List<OtherUser> list) {


return list.stream()
.flatMap(OtherUser -> users.stream()
.filter(user -> user.getId()
.equalsIgnoreCase(OtherUser.getId())))
.collect(Collectors.toList());
}
public static <T> Set<T> intersectCollections(Collection<T> col1, Collection<T> col2) {
Set<T> set1, set2;
if (col1 instanceof Set) {
set1 = (Set) col1;
} else {
set1 = new HashSet<>(col1);
}


if (col2 instanceof Set) {
set2 = (Set) col2;
} else {
set2 = new HashSet<>(col2);
}


Set<T> intersection = new HashSet<>(Math.min(set1.size(), set2.size()));


for (T t : set1) {
if (set2.contains(t)) {
intersection.add(t);
}
}


return intersection;
}

JDK8 + (可能是最好的性能)

public static <T> Set<T> intersectCollections(Collection<T> col1, Collection<T> col2) {
boolean isCol1Larger = col1.size() > col2.size();
Set<T> largerSet;
Collection<T> smallerCol;


if (isCol1Larger) {
if (col1 instanceof Set) {
largerSet = (Set<T>) col1;
} else {
largerSet = new HashSet<>(col1);
}
smallerCol = col2;
} else {
if (col2 instanceof Set) {
largerSet = (Set<T>) col2;
} else {
largerSet = new HashSet<>(col2);
}
smallerCol = col1;
}


return smallerCol.stream()
.filter(largerSet::contains)
.collect(Collectors.toSet());
}

如果您不关心性能,而是更喜欢小代码,那么只需使用:

col1.stream().filter(col2::contains).collect(Collectors.toList());

RetainAll ()方法用于查找公共元素. . 即交集 List1.retainAll (list2)

JAVA 8之后的俏皮话

工会

如果没有副本:

  return concat(a.stream(), b.stream()).collect(toList());

联合和独特的:

  return concat(a.stream(), b.stream()).distinct().collect(toList());

如果集合/集合返回类型为:

  return concat(a.stream(), b.stream()).collect(toSet());

互联系统

如无副本:

  return a.stream().filter(b::contains).collect(toList());

性能 : 如果收集 b很大而不是 O (1) ,那么在 return之前加一行来预先优化滤波器性能: 复制到 HasSet (import java.util.Set;):

... b = Set.copOf (b) ;

相互交叉,截然不同:

  return a.stream().distinct().filter(b::contains).collect(toList());

进口

导入静态 java.util.stream. Stream.concat;
导入静态 java.util.stream. Collectors.toList;
导入静态 java.util.stream. Collectors.toSet;

你可使用以下方法:

CollectionUtils.containsAnyCollectionUtils.containsAll

Apache Commons