java代码(12) ---CollectionUtils工具类
CollectionUtils工具类
CollectionUtils工具类是在apache下的,而不是springframework下的CollectionUtils
个人觉得在真实项目中CollectionUtils,可以使你的代码更加简洁和安全
下面我们就一起来探究一下
先从maven官方找到最新jar包如下:
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
|
一、API常用方法 |
/**
* 1、除非元素为null,否则向集合添加元素
*/
CollectionUtils.addIgnoreNull(personList,null);
/**
* 2、将两个已排序的集合a和b合并为一个已排序的列表,以便保留元素的自然顺序
*/
CollectionUtils.collate(Iterable<? extends O> a, Iterable<? extends O> b)
/**
* 3、将两个已排序的集合a和b合并到一个已排序的列表中,以便保留根据Comparator c的元素顺序。
*/
CollectionUtils.collate(Iterable<? extends O> a, Iterable<? extends O> b, Comparator<? super O> c)
/**
* 4、返回该个集合中是否含有至少有一个元素
*/
CollectionUtils.containsAny(Collection<?> coll1, T... coll2)
/**
* 5、如果参数是null,则返回不可变的空集合,否则返回参数本身。(很实用 ,最终返回List EMPTY_LIST = new EmptyList<>())
*/
CollectionUtils.emptyIfNull(Collection<T> collection)
/**
* 6、空安全检查指定的集合是否为空
*/
CollectionUtils.isEmpty(Collection<?> coll)
/**
* 7、 空安全检查指定的集合是否为空。
*/
CollectionUtils.isNotEmpty(Collection<?> coll)
/**
* 8、反转给定数组的顺序。
*/
CollectionUtils.reverseArray(Object[] array);
/**
* 9、差集
*/
CollectionUtils.subtract(Iterable<? extends O> a, Iterable<? extends O> b)
/**
* 10、并集
*/
CollectionUtils.union(Iterable<? extends O> a, Iterable<? extends O> b)
/**
* 11、交集
*/
CollectionUtils.intersection(Collection a, Collection b)
/**
*12、 交集的补集(析取)
*/
CollectionUtils.disjunction(Collection a, Collection b)
| 二、非对象集合交、并、差处理 |
对于集合取交集、并集的处理其实有很多种方式,这里就介绍3种
- 第一种 是CollectionUtils工具类
- 第二种 是List自带方法
- 第三种 是JDK1.8 stream新特性
1、CollectionUtils工具类
下面对于基本数据(包括String)类型中的集合进行demo示例
public static void main(String[] args) {
String[] arrayA = new String[]{"1","2","3","4"};
String[] arrayB = new String[]{"3","4","5","6"};
List<String> listA = Arrays.asList(arrayA);
List<String> listB = Arrays.asList(arrayB);
//1、并集 union
System.out.println(CollectionUtils.union(listA,listB));
//输出:[1, 2, 3, 4, 5, 6]
//2、交集intersection
System.out.println(CollectionUtils.intersection(listA,listB));
//输出:[3, 4]
//3、交集的补集(析取)disjunction
System.out.println(CollectionUtils.disjunction(listA,listB));
//输出:[1, 2, 5, 6]
//4、差集(扣除)
System.out.println(CollectionUtils.subtract(listA,listB));
//[1, 2]
}
2、List自带方法
public static void main(String[] args) {
String[] arrayA = new String[]{"1","2","3","4"};
String[] arrayB = new String[]{"3","4","5","6"};
List<String> listA = Arrays.asList(arrayA);
List<String> listB = Arrays.asList(arrayB);
//1交集
List<String> jiaoJiList = new ArrayList<>(listA);
jiaoJiList.retainAll(listB);
System.out.println(jiaoJiList);
//输出:[3, 4]
//2差集
List<String> chaJiList = new ArrayList<>(listA);
chaJiList.removeAll(listB);
System.out.println(chaJiList);
//输出:[1, 2]
//3并集(先做差集在做添加所有)
List<String> bingList = new ArrayList<>(listA);
bingList.removeAll(listB);//[1,2]
bingList.addAll(listB);//[3,4,5,6]
System.out.println(bingList);
//输出:[1, 2, 3, 4, 5, 6]
}
注意:intersection和retainAll的差别
要注意的是它们返回类型是不一样的,intersection返回的是一个新的List集合,而retainAll返回是Bollean类型就说明retainAll方法是对原有集合进行处理再返回原有集合,会改变原有集合中的内容
1:从性能角度来考虑的话,List自带会高点,因为它不用再创建新的集合,2:需要注意的是,因为retainAll因为会改变原有集合,所以该集合需要多次使用就不适合用retainAll
注意:Arrays.asList将数据转集合不能进行add和remove操作
原因:调用Arrays.asList()生产的List的add、remove方法时报异常,这是由Arrays.asList()返回的是Arrays的内部类ArrayList,而不是java.util.ArrayList.Arrays的内部类ArrayList和java.util.ArrayList都是继承AbstractList,remove,add
等方法AbstractList中是默认throw UnsupportedOperationException而且不做任何操作,java.util.ArrayList重新了这些方法而Arrays的内部类没有重新,所以会抛出异常
所以正确做法如下:
String[] array = {"1","2","3"};
List<String> list = Arrays.asList(array);
List arrList = new ArrayList(list);
arrList.add("4");
3、JDK1.8 stream新特性
public static void main(String[] args) {
String[] arrayA=new String[]{"1","2","3","4"};
String[] arrayB=new String[]{"3","4","5","6"};
List<String> listA = Arrays.asList(arrayA);
List<String> listB = Arrays.asList(arrayB);
//交集
List<String> intersection = listA.stream().filter(item -> listB.contains(item)).collect(Collectors.toList());
System.out.println(intersection);
//输出:[3, 4]
//并集(新建集合:1、是因为不影响原始集合,2、Arrays.asList不能add和remove操作)
List<String> listAll = listA.parallelStream().collect(Collectors.toList());
List<String> listAll2 = listB.parallelStream().collect(Collectors.toList());
listAll.addAll(listAll2);
System.out.println(listAll);
//输出:[1, 2, 3, 4, 3, 4, 5, 6]
//差集
List<String> reduceList = listA.stream().filter(item -> !listB.contains(item)).collect(Collectors.toList());
System.out.println(reduceList);
//输出:[1, 2]
//去重并集
ArrayList<String> list = new ArrayList<>(listA);
list.addAll(listB);
List<String> listAllDistinct = list.stream().distinct().collect(Collectors.toList());
System.out.println(listAllDistinct);
//输出:[1, 2, 3, 4, 5, 6]
}
总结:三种方式,第一种稍微好用一些,第二种还需要确定该集合是否被多次调用,第三种可读性不高
| 三、对象集合交、并、差处理 |
因为对象的equals比较是比较两个对象的内存地址,所以除非是同一对象,否则equals返回的永远是false
但实际开发中,业务系统中判断对象有时候需要的不是一种严格意义上的相等,而是业务上的对象相等,在这种情况下,原生的equals方法就不能满足我们的需求了,所以这个时候我们需要重写equals方法
说明:String为什么可以使用equals方法,只要字符串相等就为true,那是因为String类重写了equals和hashCode方法,比较的是值
1、Person对象
public static void main(String[] args) {
List<Person> personList = Lists.newArrayList();
Person person1 = new Person("小小", 15);
Person person2 = new Person("中中", 16);
personList.add(person1);
personList.add(person2);
List<Person> person1List = Lists.newArrayList();
Person person3 = new Person("中中", 16);
Person person4 = new Person("大大", 17);
person1List.add(person3);
person1List.add(person4);
/**
* 1、差集
*/
System.out.println(CollectionUtils.subtract(personList,person1List));
//输出:[Person{name='小小', age=15}]
/**
* 2、交集
*/
System.out.println(CollectionUtils.intersection(personList,person1List));
//输出:[Person{name='中中', age=16}]
/**
* 3、并集
*/
System.out.println(CollectionUtils.union(personList,person1List));
//输出:[Person{name='小小', age=15}, Person{name='大大', age=17}, Person{name='中中', age=16}]
/**
* 4、交集的补集(析取)
*/
System.out.println(CollectionUtils.disjunction(personList,person1List));
//输出:[Person{name='小小', age=15}, Person{name='大大', age=17}]
}
其他两种方式就不测了,因为都一样
| 四、为什么要重写equals方法一定要重写hashCode方法 |
1、源码
其实上面的Person类我可以只重写equals方法而不写hashCode方法,一样能达到上面的效果,但为什么建议写上呢?官方说法:对象的equals方法被重写,那么对象的hashCode的也尽量重写
重写equals方法就必须重写hashCode的方法的原因,从源头Object类讲起就更好理解了
先来看Object关于hashCode()和equals()的源码:
public native int hashCode();
public boolean equals(Object obj){
return (this == obj);
}
光从代码中可以知道,hashCode方法是一个本地native方法,返回的是对象引用中存储对象的内存地址,而equals方法是利用==来比较也是对象的内存地址,从上面看出,hashCode方法和equals方法是一致的,还有最关键的一点
我们来Object类中关于hashCode方法的注释
1.在java应用程序运行期间,在对同一对象多次调用hashCode方法时,必须一致地返回相同的整数,前提是将对象进行equals比较时所用的信息没有被修改。
2.如果根据equals(Object)方法,两个对象是相等的,那么对这两个对象调用hashCode方法都必须生成相同的整数结果。
3.如果根据equals(java.lang.Object)方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode方法不 要求一定生成不同的整数结果,但是,我们应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能
整理:hashCode和equals保持一致,如果equals方法返回true,那么两个对象的hashCode返回值必须一样,如果equals方法返回false,hashCode可以不一样,但是这样不利于哈希表的性能,一般我们也不要这样做
假设两个对象,重写了其equals方法,其相等条件是某属性相等,就返回true,如果不重写hashCode方法,其返回的依然是两个对象的内存地址值,必然不相等,这就出现了equals方法相等,但是hashCode不相等的情况,这不符合hashCode的规则
2、HashSet和Map集合类型
重写equals方法就必须重写hashCode方法主要是针对HashSet和Map集合类型,而对于List集合到没有什么影响
原因:在向HashSet集合中存入一个元素时,HashSet会调用该对象(存入对象)的hashCode方法来得到该对象的hashCode值,然后根据该hashCode值决定该对象在HashSet中存储的位置,简单的说:
HashSet集合判断两个元素相等的标准是:两个对象通过equals方法比较相等,并且两个对象hashCode方法返回值也相等,如果两个元素通过equals方法比较返回true,但是它们的hashCode方法返回值不同,HashSet会把它们存储在不同的位置,依然可以
添加成功
这就是问题所在:就是如果你只重写equals方法,而不重写hashCode,如果equals为true,而它们的hashCode方法返回值肯定不一样,因为它们都不是同一对象所以内存地址肯定不一样,所以它还是添加成功了,那么其实你写的equals方法根本没啥软用
3、代码示例
1、People类
重写equals方法,但并没有hashCode方法
public class People {
private String name;
private Integer age;
public People(String name, Integer age) {
this.name = name;
this.age = age;
}
/**
* 重写equals方法
* @param o
* @return
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
People people = (People) o;
return Objects.equals(name, people.name) &&
Objects.equals(age, people.age);
}
/**
* 重写toString方法
* @return
*/
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public static void main(String[] args) {
HashSet<People> hashSet = Sets.newHashSet();
People people1 = new People("小小",3);
People people2 = new People("中中",4);
People people3 = new People("中中",4);
People people4 = new People("大大",5);
hashSet.add(people1);
hashSet.add(people2);
hashSet.add(people3);
hashSet.add(people4);
System.out.println(hashSet);
//输出:[People{name='大大', age=5}, People{name='中中', age=4}, People{name='小小', age=3}, People{name='中中', age=4}]
}
很明显,我重写equals方法,那么people2和people3的equals应该相同,所以不能放入HashSet,但它们的hashCode方法返回不同,所以导致同样能放入HashSet
重点:对于Set集合必须要同时重写这两个方法,要不然Set的特性就被破坏了
java代码(12) ---CollectionUtils工具类的更多相关文章
- Java代码封装redis工具类
maven依赖关系: <dependency> <groupId>redis.clients</groupId> <artifactId>jedis&l ...
- java代码之美(12)---CollectionUtils工具类
java代码之美(12)---CollectionUtils工具类 这篇讲的CollectionUtils工具类是在apache下的, 而不是springframework下的CollectionUt ...
- 从 Java 代码逆向工程生成 UML 类图和序列图
from:http://blog.itpub.net/14780914/viewspace-588975/ 本文面向于那些软件架构师,设计师和开发人员,他们想使用 IBM® Rational® Sof ...
- Java 后台验证的工具类
Java 后台验证的工具类 public class ValidationUtil { //手机号 public static String mobile = "^( ...
- Redis 工具类 java 实现的redis 工具类
最近了解了一下非关系型数据库 redis 会使用简单的命令 在自己本地电脑 使用时必须先启动服务器端 在启动客户端 redis 简介 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内 ...
- 最全的Java操作Redis的工具类,使用StringRedisTemplate实现,封装了对Redis五种基本类型的各种操作!
转载自:https://github.com/whvcse/RedisUtil 代码 ProtoStuffSerializerUtil.java import java.io.ByteArrayInp ...
- CollectionUtils工具类
CollectionUtils工具类 这篇讲的CollectionUtils工具类是在apache下的,可以使代码更加简洁和安全. 使用前需导入依赖 <dependency> <gr ...
- java中常用的工具类(一)
我们java程序员在开发项目的是常常会用到一些工具类.今天我汇总了一下java中常用的工具方法.大家可以在项目中使用.可以收藏!加入IT江湖官方群:383126909 我们一起成长 一.String工 ...
- Java学习-041-颜色工具类(RGB,HEX)
在日常的网页开发中,经常需要进行颜色数值获取.转换,例如获取红色,获取蓝色,获取绿色,RGB转十六进制颜色,十六进制颜色转RGB等,因而在学习过程中,写了一个小工具类,仅供各位小主参考! 多不闲言,直 ...
随机推荐
- linux --vim 补充 .vimrc
1.今天发现了一个新的功能.vimrc 1.这个是一个个人配置文件,可以在这个里面首先对vim进行一些设置呢 如果系统没有.vimrc文件,可以自己创建一个,touch .vimrc 举个栗子,我在. ...
- box-sizing 可以使border padding不影响设置的盒子尺寸
- java 四舍五入 保留n为数
double x1 = 0.026;String format = String.format("%.2f", x1);System.out.println(format); St ...
- vue学习-第三个DEMO(计算属性和监视) v-model基础用法
<div id="demo"> 姓:<input type="text" placeholder="First Name" ...
- maven and dubbo
maven是什么 maven是一个项目管理和构建自动化工具. 核心概念 pom (project object model) mvn compile 编译 mvn package 编译成一个jar的包 ...
- kafka消费者重试逻辑的实现
背景 在kafka的消费者中,如果消费某条消息出错,会导致该条消息不会被ack,该消息会被不断的重试,阻塞该分区的其他消息的消费,因此,为了保证消息队列不被阻塞,在出现异常的情况下,我们一般还是会ac ...
- Python列表,元组,字典,字符串方法笔记
01. 列表 1.1 列表的定义 List(列表) 是 Python 中使用 最频繁 的数据类型,在其他语言中通常叫做 数组 专门用于存储 一串 信息 列表用 [] 定义,数据 之间使用 , 分隔 列 ...
- php RSA和AES加密算法
一.RSA加密 RSA只说PHP中的应用,详细的算法原理解释,请自行百度,或者参考(RSA加密算法-详细解释以及公钥加密为什么每次都不一样) 总结:公钥加密.私钥解密.私钥签名.公钥验签. 注意: 1 ...
- 【Java_集合框架Set】HashSet、LinkedHashSet、TreeSet使用区别
HashSet:哈希表是通过使用称为散列法的机制来存储信息的,元素并没有以某种特定顺序来存放: LinkedHashSet:以元素插入的顺序来维护集合的链接表,允许以插入的顺序在集合中迭代: Tree ...
- docker-compose 命令详解
1.docker-compose的使用非常类似于docker命令的使用,但是需要注意的是大部分的compose命令都需要到docker-compose.yml文件所在的目录下才能执行. 2.[Linu ...