Java的CollectionUtils工具类详解

 更新时间:2023年05月16日 10:46:43   作者:逆流°只是风景-bjhxcc  
这篇文章主要介绍了Java的CollectionUtils工具类详解,CollectionUtils工具类是在apache下的,而不是springframework下的,个人觉得在真实项目中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 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 +
                '}';
    }
}

main:

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的CollectionUtils工具类详解的文章就介绍到这了,更多相关Java CollectionUtils内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Boot 实现 WebSocket 的代码示例

    Spring Boot 实现 WebSocket 的代码示例

    WebSocket 协议是独立的基于 TCP 协议。它与 HTTP 的唯一关系是,它的握手会被 HTTP 服务器解释为 Upgrade 请求,接下来通过本文给大家介绍Spring Boot 实现 WebSocket 示例详解,需要的朋友可以参考下
    2022-04-04
  • Java GUI进阶之流式布局管理器FlowLayout专项精讲

    Java GUI进阶之流式布局管理器FlowLayout专项精讲

    FlowLayout-流式布局管理器,按水平方向依次排列放置组件,排满一行,换下一行继续排列。排列方向(左到右 或 右到左)取决于容器的componentOrientation属性
    2022-04-04
  • 浅谈Java中IO和NIO的本质和区别

    浅谈Java中IO和NIO的本质和区别

    IO的全称是input output,是java程序跟外部世界交流的桥梁,IO指的是java.io包中的所有类,他们是从java1.0开始就存在的。NIO叫做new IO,是在java1.4中引入的新一代IO。IO的本质是什么呢?它和NIO有什么区别呢?我们该怎么学习IO和NIO呢?看完这篇文章一切都有答案。
    2021-06-06
  • java 查找替换pdf中的指定文本

    java 查找替换pdf中的指定文本

    这篇文章主要介绍了java 如何查找替换pdf中的指定文本,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-12-12
  • SpringBoot中SmartLifecycle的使用解析

    SpringBoot中SmartLifecycle的使用解析

    这篇文章主要介绍了SpringBoot中SmartLifecycle的使用解析,SmartLifecycle是一个扩展了Lifecycle接口,可以跟踪spring容器ApplicationContext刷新或者关闭的接口,实现该接口的实现类有特定的执行顺序,需要的朋友可以参考下
    2023-11-11
  • 深入理解JavaWeb中过滤器与监听器的应用

    深入理解JavaWeb中过滤器与监听器的应用

    这篇文章主要介绍了JavaWeb中过滤器与监听器的应用,过滤器能够对匹配的请求到达目标之前或返回响应之后增加一些处理代码,监听器是一个接口内容由我们实现,会在特定时间被调用,感兴趣想要详细了解可以参考下文
    2023-05-05
  • Java HashMap原理及实例解析

    Java HashMap原理及实例解析

    这篇文章主要介绍了Java HashMap原理及实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • 浅谈Java工程读取resources中资源文件路径的问题

    浅谈Java工程读取resources中资源文件路径的问题

    下面小编就为大家带来一篇浅谈Java工程读取resources中资源文件路径的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • String实例化及static final修饰符实现方法解析

    String实例化及static final修饰符实现方法解析

    这篇文章主要介绍了String实例化及static final修饰符实现方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • IntelliJ IDEA搜索整个项目进行全局替换(有危险慎用)

    IntelliJ IDEA搜索整个项目进行全局替换(有危险慎用)

    今天小编就为大家分享一篇关于IntelliJ IDEA搜索整个项目进行全局替换(有危险慎用),小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-10-10

最新评论