Collections工具类详解—顺带介绍下Comparable和Comparator

前面介绍了各种Java集合,那么想利用集合实现一些功能,比如排序、求两个集合的交集、找出集合中最大、最小的元素等功能,如果自己实现也能实现,不过代码看着比较臃肿而且效率你懂的,JDK的开发人员也想到了这一层,所以在JDK 1.2的时候就提供了Collections工具类,直接调用其方法就可以实现诸如上述功能。

一、Collection和Collections的区别

ps:我印象中前几年这个问题在面试的时候还比较火。。。

  • Collection:提供了对集合对象进行基本操作的通用接口方法,是List、Set、Queue等的父接口。

  • Collections:是集合的工具类,提供有关集合操作的静态方法,服务于Collection框架,可以提升很多处理集合类的效率。

Collections工具类常用方法有:

  • 集合排序

  • 高效搜索

  • 集合比较

  • 极值操作

  • 同步控制(不推荐)

二、常用方法

2.1 排序

相关方法(全是public static修饰-这里忽略):

//对列表中的元素进行自然顺序排序(由小到大)
<T extends Comparable<? super T>> void sort(List<T> list)
//使用指定的比较器对列表中的元素进行排序
<T> void sort(List<T> list, Comparator<? super T> c)
//反转列表中元素的顺序
void reverse(List<?> list)
//随机打乱列表中元素的顺序
void shuffle(List<?> list)
//交换列表中指定位置的元素
void swap(List<?> list, int i, int j)

2.1.1 自然排序

示例:

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>(){{
        add(2);
        add(5);
        add(3);
        add(1);
        add(4);
    }};
    System.out.println("排序前:"+list);
    Collections.sort(list);
    System.out.println("排序后:"+list);
}

结果:

排序前:[2, 5, 3, 1, 4]
排序后:[1, 2, 3, 4, 5]

这里有一个知识点说一下:

  • JDK 1.6,Collections.sort方法使用得是MergeSort(归并排序),JDK 1.7的时候内部实现变为了TimeSort。

  • TimeSort结合了合并排序和插入排序,核心过程如下:

    • 数组长度小于32的情况用二分插入排序。

    • 数组长度大于等于32,计算出一个合适的大小,将数组按计算出的大小分为小的run快,使用插入排序使其递增,然后进行两个run块的合并,合并的结果保存到栈中,直到合并完所有的run块,最后归并所有的run得到排序结果。

2.1.2 自定义排序

示例:

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>(){{
        add(2);
        add(5);
        add(3);
        add(1);
        add(4);
    }};
    System.out.println("排序前:"+list);
    //由大到小自定义排序
    Collections.sort(list, new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2-o1;
        }
    });
    System.out.println("排序后:"+list);
}

结果:

排序前:[2, 5, 3, 1, 4]
排序后:[5, 4, 3, 2, 1]

ps:关于Comparator比较器下面细说。

2.1.3 列表反转

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>(){{
        add(2);
        add(5);
        add(3);
        add(1);
        add(4);
    }};
    System.out.println("反转前:"+list);
    Collections.reverse(list);
    System.out.println("反转后:"+list);
}

结果: 

反转前:[2, 5, 3, 1, 4]
反转后:[4, 1, 3, 5, 2]

2.1.4 随机打乱

示例:

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>(){{
        add(2);
        add(5);
        add(3);
        add(1);
        add(4);
    }};
    System.out.println("打乱前:"+list);
    Collections.shuffle(list);
    System.out.println("打乱后:"+list);
}

结果(每次执行的结果都不一样):

打乱前:[2, 5, 3, 1, 4]
打乱后:[1, 2, 4, 5, 3]

2.1.5 交换元素位置

示例:

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>(){{
        add(2);
        add(5);
        add(3);
        add(1);
        add(4);
    }};
    System.out.println("交换前:"+list);
    //下标0和4的元素交换位置
    Collections.swap(list,0,4);
    System.out.println("交换后:"+list);
}

结果:

交换前:[2, 5, 3, 1, 4]
交换后:[4, 5, 3, 1, 2]

2.2 高效搜索

//对List进行二分查找,返回索引,List必须是有序的
int binarySearch(List list, Object key)
//用指定的元素代替指定list中的所有元素
void fill(List list, Object obj)
//统计元素出现次数
int frequency(Collection c, Object o)
//查找子列表在主列表第一次出现的下标,找不到则返回-1
int indexOfSubList(List list, List target)
//用新元素替换旧元素
boolean replaceAll(List list, Object oldVal, Object newVal)

2.2.1 查找key对应的下标

示例:

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>(){{
        add(2);
        add(5);
        add(3);
        add(1);
        add(4);
    }};
    Collections.sort(list);
    System.out.println("排序后:"+list);
    int i = Collections.binarySearch(list, 3);
    System.out.println("下标为:"+i);
}

结果:

排序后:[1, 2, 3, 4, 5]
下标为:2

2.2.2 用指定的值替换所有元素

示例:

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>(){{
        add(2);
        add(5);
        add(3);
        add(1);
        add(4);
    }};
    System.out.println("替换前:"+list);
    Collections.fill(list,0);
    System.out.println("替换后:"+list);
}

结果:

替换前:[2, 5, 3, 1, 4]
替换后:[0, 0, 0, 0, 0]

2.2.3 统计元素出现的次数

示例:

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>(){{
        add(2);
        add(5);
        add(2);
        add(1);
        add(2);
    }};
    System.out.println("list:"+list);
    int frequency = Collections.frequency(list, 2);
    System.out.println("2出现的次数为:"+frequency);
}

结果:

list:[2, 5, 2, 1, 2]
2出现的次数为:3

2.2.4 查找子列表在主列表第一次出现的下标

示例:

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>(){{
        add(2);
        add(5);
        add(2);
        add(1);
        add(2);
    }};
    List<Integer> list1=new ArrayList<Integer>(){{
        add(2);
        add(1);
    }};
    int index = Collections.indexOfSubList(list,list1 );
    System.out.println("list1子列表第一次出现的下标为:" + index);
    List<Integer> list2=new ArrayList<Integer>(){{
        add(2);
        add(2);
    }};
    int index1 = Collections.indexOfSubList(list,list2 );
    System.out.println("list2子列表第一次出现的下标为:" + index1);
}

结果:

list1子列表第一次出现的下标为:2
list2子列表第一次出现的下标为:-1

2.2.5 新元素替换旧元素

示例:

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>(){{
        add(2);
        add(5);
        add(2);
        add(1);
        add(2);
    }};
    System.out.println("替换前:"+list);
    boolean b = Collections.replaceAll(list, 2, 0);
    if (b){
        System.out.println("替换后:" + list);
    }
}

结果:

替换前:[2, 5, 2, 1, 2]
替换后:[0, 5, 0, 1, 0]

2.3 集合比较

//比较两个集合是否有交集,false-有交集 true-没交集
boolean disjoint(Collection<?> c1, Collection<?> c2)

示例:

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>(){{
        add(2);
        add(5);
        add(2);
        add(1);
        add(2);
    }};
    List<Integer> list1=new ArrayList<Integer>(){{
        add(2);
        add(3);
    }};
    List<Integer> list2=new ArrayList<Integer>(){{
        add(3);
        add(3);
    }};
    boolean disjoint = Collections.disjoint(list, list1);
    System.out.println("list和list1比较结果:"+disjoint);
    boolean disjoint1 = Collections.disjoint(list, list2);
    System.out.println("list和list2比较结果:"+disjoint1);
}

结果:

list和list1比较结果:false
list和list2比较结果:true

2.4 极值操作

示例:

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>(){{
        add(2);
        add(5);
        add(2);
        add(1);
        add(2);
    }};
    Integer max = Collections.max(list);
    System.out.println("max = " + max);
    Integer min = Collections.min(list);
    System.out.println("min = " + min);
}

结果:

max = 5
min = 1

2.5 同步控制

//返回指定Collection支持的线程安全的Collection
<T> Collection<T> synchronizedCollection(Collection<T> c) 
//返回指定List支持的线程安全的List
<T> List<T> synchronizedList(List<T> list)
//返回由指定Map支持的线程安全的Map
<K,V> Map<K,V> synchronizedMap(Map<K,V> m)
//返回指定Set支持的线程安全的set
<T> Set<T> synchronizedSet(Set<T> s) 

不推荐,因为用这种方式创建的集合,所有操作都加了synchronized锁。

三、Comparable和Comparator比较器

把这个拿过来:

//对列表中的元素进行自然顺序排序(由小到大)
<T extends Comparable<? super T>> void sort(List<T> list)
//使用指定的比较器对列表中的元素进行排序
<T> void sort(List<T> list, Comparator<? super T> c)

Collections.sort方法有两种重载的形式,第一种如果是对象比较的话就必须实现Comparable接口,第二种要求传一个Comparator比较器 ,那么它俩有啥区别呢?

3.1 Comparable和Comparator的区别

首先这俩接口都可以实现对象的排序,功能都一样。像String、Integer这些基本类型都实现了Comparable接口,所以可以直接调用第一种进行排序,但像我们自定义的一些对象,如果没有实现Comparable接口就不能进行排序。

要说区别的话:

  • 重写的方法:Comparable重写的是compareTo方法,Comparator重写的是compare方法。

  • 接口所在包:Comparable在java.lang,Comparator在java.util。

  • 耦合性:Comparable比较强,要对象实现Comparable,Comparator弱,不需要修改原有代码。

3.2 举个例子

3.2.1 Comparable

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Comparable<Student>{
​
    private String name;
    private Integer age;
​
    @Override
    public int compareTo(Student o) {
        //先按名字升序排序
        int flag = this.name.compareTo(o.name);
        if(flag==0){
            //名字相同按年龄升序排序
            flag=this.age-o.age;
        }
        return flag;
    }
}

测试:

public static void main(String[] args) {
    List<Student> list=new ArrayList<Student>(){{
        add(new Student("张三",18));
        add(new Student("张三",17));
        add(new Student("李四",19));
    }};
​
    Collections.sort(list);
    System.out.println("排序后:"+list);
}

结果:

排序后:[Student(name=张三, age=17), Student(name=张三, age=18), Student(name=李四, age=19)]

3.2.2 Comparator

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student{
    private String name;
    private Integer age;
}

测试:

public static void main(String[] args) {
    List<Student> list=new ArrayList<Student>(){{
        add(new Student("张三",18));
        add(new Student("张三",17));
        add(new Student("李四",19));
    }};
​
    Collections.sort(list, new Comparator<Student>() {
        @Override
        public int compare(Student o1, Student o2) {
            int flag = o1.getName().compareTo(o2.getName());
            if(flag==0){
                flag=o1.getAge()-o2.getAge();
            }
            return flag;
        }
    });
    //可以优化为:Collections.sort(list, Comparator.comparing(Student::getName).thenComparingInt(Student::getAge));
    System.out.println("排序后:"+list);
}

结果:

排序后:[Student(name=张三, age=17), Student(name=张三, age=18), Student(name=李四, age=19)]

3.2.3 有了Comparable为什么还需要Comparator

Comparable用于使某个类具备排序的能力,比如上述的Student类。

不过仍存在一些类没有实现Comparable的类,但是也需要有一定的排序能力的类,此时就得使用Comparator接口,该接口主要作用就是给不具备排序能力的对象进行排序。

 End:希望对大家有所帮助,如果有纰漏或者更好的想法,请您一定不要吝啬你的赐教🙋。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橡 皮 人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值