1. 集合的交集、并集、补集、差集
1.1 概念
Java List 集合取 交集、并集、差集、补集的概念定义:
交集: 取2个集合中,相同的部分 (A 交集 B,取 A和B 中相同的元素)
并集: 将2个集合,合并为一个集合中
差集: 取2个集合中,不相同的部分 (A差集 B,取 A中与B不相同的元素)
补集: 取2个集合中,不相同的部分 ,组成新集合 ( A 差集 B 并 B 差集 A)
专业词汇
并集 = union
交集 = intersection
补集 = complement
析取,补集 = disjunction
减去 = subtract
数据准备:
首先我们定义两个list集合, 通过循环都可以实现上述集合运算,因此通过循环方法本文就不列出了。
ArrayList<Integer> list1 = new ArrayList<Integer>(Arrays.asList(1, 2, 3));
ArrayList<Integer> list2 = new ArrayList<Integer>( Arrays.asList(3,4,5));
// 引入的包
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
通过CollectionUtils工具包 (推荐),需要引入CollectionUtils 工具包
1.2 交集
1. 通过CollUtil的工具类
(1)不去重交集
public static Collection intersection(Collection coll1, Collection coll2);
解析:
两个集合的交集 针对一个集合中存在多个相同元素的情况,计算两个集合中此元素的个数,保留最少的个数
例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c] 结果:[a, b, c, c],此结果中只保留了两个c
Params: coll1 – 集合1, coll2 – 集合2
Type parameters: <T> – 集合元素类型
Returns: 交集的集合,返回 ArrayList
案例:
@Test
public void test12(){
List<String> A = Arrays.asList("a", "b", "c", "c", "c");
List<String> B = Arrays.asList("a", "b", "c", "c");
Collection<String> result = CollUtil.intersection(A, B);
System.out.println("result: "+result);
}
result: [a, b, c, c]
(2)去重交集
public static Set intersectionDistinct(Collection coll1,Collection coll2, Collection… otherColls)
两个集合的交集,仅仅保留两个集合中相同的部分,每个相同的元素只保留一个,即去重
例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c] 结果:[a, b, c],此结果中只保留了a,b,c三个元素
Params: coll1 – 集合1, coll2 – 集合2
Type parameters: <T> – 集合元素类型
Returns: 交集的集合,返回 ArrayList
案例:
@Test
public void test13(){
List<String> A = Arrays.asList("a", "b", "c", "c", "c");
List<String> B = Arrays.asList("a", "b", "c", "c");
Set<String> result = CollUtil.intersectionDistinct(A, B);
System.out.println("result: "+result);
}
result: [a, b, c]
(3)求多个集合的交集
public static Collection intersection(Collection coll1, Collection coll2, Collection… otherColls)
两多集合的交集,仅仅保留多个集合中相同的部分,每个相同的元素只保留一个,即去重
例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c] ,集合2:[a, b, c, c] ,集合2:[e, b, g, c]
结果:[b, c],此结果中只保留了b和c元素
Params: coll1 – 集合1, coll2 – 集合2,otherColl-是一个可变参数,可以同时传多个集合进行求并集
Type parameters: <T> – 集合元素类型
Returns: 交集的集合,返回 ArrayList
案例:
@Test
public void test15(){
List<String> A = Arrays.asList("a", "b", "c", "c", "c");
List<String> B = Arrays.asList("a", "b", "c", "c");
List<String> C = Arrays.asList("a", "b", "c", "c");
List<String> D = Arrays.asList("e", "b", "g", "c");
Collection<String> result = CollUtil.intersection(A, B, C, D);
System.out.println("result: "+result);
}
result: [b, c]
2. 通过CollectionUtils的工具类
Collection intersection = CollectionUtils.intersection(list1, list2);
System.out.println("intersection CollectionUtils 交集结果是: " + intersection);
------------
intersection CollectionUtils 交集结果是: [3]
注意:使用这种方式并不能做到取完交集后去重重复的元素
List<String> A = Arrays.asList("a", "b", "c", "c", "c");
List<String> B = Arrays.asList("a", "b", "c", "c");
Collection<String> result = CollectionUtils.intersection(A, B);
System.out.println("result: "+result);
result: [a, b, c, c]
3. 通过List集合的retainAll方法
注意:使用这种方式并不能做到取完交集后去重重复的元素
@Test
public void test10(){
ArrayList<Integer> list1 = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 3));
ArrayList<Integer> list2 = new ArrayList<Integer>( Arrays.asList(3,4,5));
boolean b = list1.retainAll(list2);
System.out.println("list1 retainAll 方法 交集结果是: " + list1);
System.out.println("list2: " + list2);
}
----------
list1 retainAll 方法 交集结果是: [3, 3]
list2: [3, 4, 5]
注意:通过retainAll方法,会修改原集合。即修改调用retainAll集合的内容,最终的结果是谁调用retainAll方法,谁的最终结果就是交集后的结果
4. 其它求交集的方式(了解)
(1)内置函数实现
1、removeAll方法:从list中删除指定集合中包含的所有元素。
2、retainAll方法:从list中删除指定集合中不包含的所有元素。
3、addAll方法:用来向Set集合添加另一个集合对象所包含的所有内容
public class CollectionTest {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
list1.add("a");
list1.add("b");
list1.add("c");
list2.add("b");
list2.add("e");
list2.add("d");
//求交集
list1.retainAll(list2);
System.out.println(list1.toString()); // b
//求并集
list1.removeAll(list2);
list1.addAll(list2);
System.out.println(list1.toString()); // a c b e d
//求差集(list1有而list2没有)
list1.removeAll(list2); // a c
}
}
(2)for循环遍历实现 求两个集合的交集
利用集合的contains方法,来对比第二个集合中是否存在相同的元素的方法,同时将交集结果返回。时间复杂度是O(n)。
contains方法:用于判断list集合是否包含某个元素。
public List<Object> intersectionForList_1(List<Object> arr1, List<Object> arr2) {
long startTime = System.currentTimeMillis();
List<Object> result = new ArrayList<>();
for (Object arr : arr1) {
if (arr2.contains(arr)) {
result.add(arr);
}
}
long endTime = System.currentTimeMillis();
log.info("intersectionForList_1:" + (endTime - startTime));
return result;
}
(3)、使用Java 8 Stream API 求两个集合的交集
首先把两个集合转换成Stream,然后使用filter()方法筛选出交集元素,最后把结果转换成集合。 时间复杂度是O(n)。
.stream().filter()方法:来筛选出满足条件的对象,最终返回的筛选过后的列表。
Collectors.toList()和Collectors.toSet()方法都是将map后的stream转换为一个列表对象,toSet()会去重重复的对象
Set<Integer> set1 = new HashSet<>();
set1.add(1);
set1.add(2);
set1.add(3);
set1.add(4);
Set<Integer> set2 = new HashSet<>();
set2.add(3);
set2.add(4);
set2.add(5);
Set<Integer> intersection = set1.stream().filter(set2::contains).collect(Collectors.toSet());
System.out.println(intersection); // 输出 [3, 4]
(4)使用迭代器求两个集合的交集
public List<Object> intersectionForList_2(List<Object> arr1, List<Object> arr2) {
List<Object> resultList = new ArrayList<>();
List<Object> maxList;
List<Object> minList;
if (arr1.size()>arr2.size()){
maxList = arr1;
minList = arr2;
}else {
maxList = arr2;
minList = arr1;
}
Iterator<Object> iterator = maxList.iterator();
while (iterator.hasNext()){
Object next = iterator.next();
if (minList.contains(next)){
resultList.add(next);
}
}
return resultList;
}
(5)使用map 求两个集合的交集
首先将list集合中的元素依次存入一个map中去,然后再以map的get方法来判断是否存在这样的元素。
这种方法的效率最高,在10万条数据的测试下,这种方法耗时仅仅在十几毫秒,而其他方法在将近一百多毫秒。
在百万条数据进行对比处理的时候,使用map特性的方法时间消耗大概在40毫秒左右,但是其他方法要将近10秒钟,
所以在对于大量数据的处理过程中,还是非常建议使用这种方法的。
public List<Object> intersectionForList_3(List<Object> arr1, List<Object> arr2) {
List<Object> resultList = new ArrayList<>();
Map<String,Object> map = new HashMap<>();
arr1.forEach(a1->{
map.put(a1+"",a1);
});
arr2.forEach(a2->{
Object obj = map.get(a2 + "");
if (obj!=null){
resultList.add(obj);
}
});
return resultList;
}
(6)取交集 — 手动遍历集合实现
/**@Description: 取交集
* @version v1.0
* @author wu
* @date 2022/7/30 22:41
*/
@Test
public void intersectionTest1(){
ArrayList<Object> destList = Lists.newArrayList();
for (Integer e1 : list1) {
for (Integer e2 : list2) {
if(e1.equals(e2)){
destList.add(e1);
}
}
}
System.out.println("intersectionTest1 交集结果是: " + destList);
list1.retainAll(list2);
System.out.println("intersectionTest1 retainAll 方法 交集结果是: " + list1);
}
(7)取交集 — 通用方法 实现
list1 交集 list2 ,不用生成新的list ,使用 迭代器 Iterator 删除 list1中和list2 不相同的元素。
/**
* Description: 测试 通用List集合取值交集的方法
* @return void
* @version v1.0
* @author wu
* @date 2022/11/15 22:15
*/
@Test
public void intersectionListTest() throws Exception {
System.out.println("srcList : \n"+ JSON.toJSONString(list));
System.out.println("subList : \n"+ JSON.toJSONString(subList));
intersectionList(list,subList,(t1,t2)->{
if(t1.equals(t2)){
return 0;
}
return -1;
});
}
/**
* Description: 通用List集合取值交集的方法
* @param srcList
* @param subList
* @param comparator
* @return void
* @version v1.0
* @author wu
* @date 2022/11/15 22:05
*/
public <E>void intersectionList(List<E> srcList , List<E> subList , Comparator<E> comparator){
for (Iterator<E> it = srcList.iterator(); it.hasNext(); ) {
final E next = it.next();
boolean isMatch = false;
for (E e : subList) {
if(comparator.compare(next,e) == 0){
isMatch = true;
break;
}
}
if(!isMatch){
it.remove();
}
}
System.out.println("srcList 交集后: \n"+ JSON.toJSONString(list));
System.out.println("subList 交集后: \n"+ JSON.toJSONString(subList));
}
1.3 并集
1. 通过CollUtil的工具类
(1)不去重并集
API
public static Collection union(Collection coll1,Collection coll2);
解析:
两个集合的并集 针对一个集合中存在多个相同元素的情况,计算两个集合中此元素的个数,保留最多的个数
例如:集合1:[a, b, c, c, c],集合2:[a, b, e, c] 结果:[a, b, c, c,