Java中List、Set、数据结构、Collections、可变参数

知识点回顾

  • 什么是迭代器:一个遍历集合的对象。
  • 如何获取迭代器:通过集合对象调取iterator方法获得。
  • 迭代器常用方法:
  1. boolean hasNext():判断当前指针指向位置是否有下一个元素,有返回true,否则false
  2. E next():先将指针下移指向下一个元素,并返回当前指针指向位置的元素。
  • 迭代器使用注意事项:
  1. hasNext和next方法必须成对出现
  2. 迭代器过程中不能增删集合中的元素
  • 增强for的作用:JDK1.5新特性,专门用来遍历集合和数组,本质:迭代器
  • 增强for的格式:for(数据类型  变量名:数组名或集合名){}
  • 什么是泛型方法:在定义方法时定义了泛型变量的方法
  • 泛型方法的定义格式:修饰符<T> 返回值类型 方法名(参数列表){}
  • 泛型方法注意事项:泛型变量的具体数据类型是在调用方法时根据参数决定。
  • 什么是泛型类:在定义类时定义了泛型变量的类
  • 泛型类的定义格式:class 类名<T>{}
  • 泛型类的注意事项:
  1. 泛型变量的具体数据类型在创建该类对象时由使用者指定,如果没有指定泛型变量的具体数据类型,默认Object。
  2. 静态方法不能使用类上定义的泛型变量,如果要使用泛型变量需要将该方法定义为泛型方法。
  • 什么是泛型接口:在定义接口时定义了泛型变量的接口
  • 泛型接口的定义格式:interface  接口名<T> {}
  • 泛型接口的实现方式:
  1. 方式1:实现接口的 同时指定泛型变量的具体数据类型。(不够灵活)
  2. 方式2:实现接口时不指定泛型变量的具体数据类型,将实现类定义为泛型类,由使用者创建泛型类对象时指定泛型变量的具体数据类型。
  • 泛型上限格式: ? extend Number;可以接收Number或Number的子类类型的数据
  • 泛型下限格式:? super Integer:可以接收Integer或Integer的父类类型数据
  • 小结:泛型的使用使我们的代码更加具有通用性,不会导致定义了一种类型之后其他的类型都无法使用该代码。
  • 知识补充:什么是泛型:数据类型参数化。
  • 伪泛型:泛型只会存在编译期,编译完成之后就会被擦除。

1.1  数据结构有什么用

  • 当你用着Java里面的容器类很爽的时候,你有没有想过,怎么ArrayList就像一个无限扩充的数组,也好像链表之类的。好用吗?好用,这就是数据结构的用处,只不过你在不知不觉之中使用了。
  • 现实世界的存储,我们使用的工具和建模。每种数据结构 都有自己的优点和缺点,想想如果Google的数据用的是数组的存储,我们还能方法地查询到所需要的数据吗?而算法,在这么多的数据中是如何做到最快的插入,查找,删除,也是在追求更快。
  • 我们的Java是面向对象的语言,就好似自动挡轿车,C语言好似手动挡吉普。数据结构呢?是变速箱的工作原理。你完全可以不知道变速箱怎样工作,就把自动挡车子从A点开到B点,而且未必就比懂的人慢。写程序这件事,和开车一样,经验可以起到很大的作用,但如果你不知道底层是怎么工作的,就永远只会开车,既不会修车,也不能造车。
  • 数据存储的常用结构有:栈、队列、数组、链表和红黑树


1.2 常见的数据结构

  • 栈:先进后出(First In Last FILO)
  • 队列:先进先出(First In First Out FIFO)
  • 数组
  1. 增删慢:每次增删元素时需要创建新的数组,需要复制旧数组到新的数组中。
  2. 查询快:可以根据索引查询指定的元素。
  • 链表:
  1. 增删快:每次增删元素时不需要移动元素的位置,只需要修改上一个元素记住下一个元素的地址值。
  2. 查询慢:每次根据索引查询元素时都需要从链表头或链表尾部开始遍历查询
  • 红黑树(了解)==>树 ==>二叉树

 栈

  • 栈:stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作,不允许在其他位置进行任何查找、删除等操作。
  • 简单地说:采用该结构的集合,对元素的存取有如下特点:
  • 先进后出:即存进去的元素,要在它后面的元素依次取出之后,才能取出该元素。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。
  • 栈的入口和出口都是栈的顶端位置

  • 压栈:就是存元素。即把元素存储到栈的顶端位置。
  •  弹栈:就是取元素。即,把栈的顶端位置元素取出。

队列

  •  queue,简称队列,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。
  • 存取特点:
  1. 先进先出
  2. 队列的入口和出口各占一侧。例如,下图中的左侧为入口,右侧为出口。

 数组

  • 数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。
  • 查找元素快:通过索引,可以快速访问指定位置的元素。
  • 增删元素慢:指定索引位置增加元素:需要创建一个新的数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应的索引位置。
  • 指定索引位置删除元素:需要创建一个新数组,把原数组元素根据索引,复制到新数组对应的索引位置,原数组中指定索引位置元素不复制到新数组中。

 链表

  • 链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时动态形成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点的地址的指针域,我们常说的链表结构有单向链表与双向链表,这里介绍单向链表
  • 多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下一个人的左手,依次类推,这样多个人就连在一起了。

  •  查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素
  • 增删元素快:
  1. 增加元素:只需要修改连接下个元素的地址即可。
  2. 删除元素:只需要修改连接下个元素的地址即可。

红黑树

  •  二叉树:binary tree,是每个结点不超过2的有序 树。
  • 简单理解,就是一种类似于我们生活中的树,只不过每个结点上都最多只能有两个子结点。
  • 二叉树是每个节点最多有两个子树的结构。顶上的叫根结点,两边被称作“左子树”和“右子树”。


List集合

  • 2.1 List集合特点:有序(存取顺序一致),有索引,元素可重复。

2.1 List接口中特有方法

  • 1.public void add(int index,E element):将元素添加到指定的位置
  • 2.public E  get(int index):根据索引获得指定位置元素
  • 3.public E remove(int index):删除指定位置的元素,返回被删除的元素。
  • 4.boolean  remove(Object O):删除指定的元素,删除成功返回true,否则false
  • 5.public E set(int index,E element):将指定位置的元素修改为指定值。
  • 注意:3和4都是remove方法,在4方法中,删除指定的元素,删除成功返回true,否则false。而3方法是通过索引返回被删除的元素,那么在输出的时候如何区分开来呢?例如,在一个例子中可以这样输出,System.out.println(list.remove(Integer.valueOf(1)));这样就不会把1当成索引,而是看成元素

import java.util.ArrayList;
import java.util.List;

public class ListDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        List<Integer>  list=new ArrayList<>();

        //添加元素
        list.add(1);
        list.add(2);
        list.add(3);
        System.out.println(list);//[1, 2, 3]

        //将元素4添加到索引为2的位置
        list.add(2,4);
        System.out.println(list);//[1, 2, 4, 3]

        //根据索引获得指定位置的元素
        System.out.println(list.get(2));//4

        //删除指定位置的元素
        System.out.println(list.remove(0));//1

        //将指定位置的元素修改为指定值
        list.set(1,10);
        System.out.println(list);//[2, 10, 3]

        //删除指定的元素,删除成功返回true,否则返回false
        System.out.println(list.remove(Integer.valueOf(10)));//true
    }
}

2.3 List接口常用子类

2.3.1 ArrayList集合

  • ArrayList集合特点:
  1. 有序(存取顺序一致),有索引,元素可重复
  2. 底层结构是数组:增删慢,查询快( 有指定索引),线程不安全,效率高。
import java.util.ArrayList;

public class ArrayListDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        ArrayList<String> list=new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        list.add("dd");
        System.out.println(list);//[aa, bb, cc, dd]
    }
}
  •  第一次添加元素的时候(如上面list.add("aa"))才会创建长度为10的数组,可以通过源码分析出来,因为如果有很多个集合,假如先不存储元素,先创建长度会极大地浪费空间

 2.3.2 LinkedList集合

LinKedList集合特点

  • 有序(存取顺序一致),有索引,元素可重复。
  • 底层结构是链表:增删快,查询慢,线程不安全,效率高。

LinkedList集合特有的方法

  • public void addFirst(E e):将元素添加链表头
  • publiv void addLast(E e):将元素添加链表尾
  • public E getFirst():获得链表头元素
  • public E getLast():获得链表尾元素
  • public E removeFirst():删除链表头元素,返回被删除的元素
  • public E removelast():删除链表尾元素,返回被删除的元素
import java.util.LinkedList;

public class LinkedListDemo01 {
    public static void main(String[] args) {
        //创建LinkedList集合
        LinkedList<String> list=new LinkedList<>();

        //添加元素
        list.add("aa");
        list.add("bb");
        list.add("cc");

        //根据索引获取元素
        System.out.println(list.get(0));//aa

        //将元素添加到链表头
        list.addFirst("dd");
        System.out.println(list);//[dd, aa, bb, cc]

        //将元素添加到链表尾
        list.addLast("ee");
        System.out.println(list);//[dd, aa, bb, cc, ee]

        //获得链表头元素
        System.out.println(list.getFirst());//dd

        //获得链表尾元素
        System.out.println(list.getLast());//ee

        //删除链表头元素
        list.removeFirst();
        System.out.println(list);//[aa, bb, cc, ee]

        //删除链表尾元素
        list.removeLast();
        System.out.println(list);//[aa, bb, cc]
    }
}
  •  注意:链表根据索引获得元素从链表头还是链表尾部开始遍历查询
  1. 当索引小于元素个数的一半, 则从链表头开始遍历查询,提高效率。
  2. 当索引大于等于集合元素个数的一半,则从链表尾部开始遍历查询。

ArrayList和LinkedList的选择(依据底层结构特点去选择)

  1. 如果不需要执行增删操作,则选择ArrayList
  2. 如果需要执行大量的增删操作,则选择LinkedList

3.1 set接口介绍

  •  set集合的特点:无序(存取顺序不一致),无索引,元素不可重复。
  • set集合常用子类:
  1. HashSet
  2. LinkedHashSet
  • Set集合遍历方式:(因为没有索引,不能通过普通for方法遍历集合)
  1. 增强for
  2. 迭代器

3.2 对象的哈希值概述 

  • 什么是对象哈希值?
  1. 哈希值就是一个十进制的整数,每一个对象都会有一个对应的哈希值。
  2. 哈希值默认是对象在内存中的地址值。
  • 如何获得对象的哈希值?
  1. 通过对象调用hashCode方法获得。(通过查API可得:hashCode方法在Object类中)
  • 哈希值的作用?
  1. 哈希值是对象存储到哈希表的重要依据。
  • 在实际开发中,自定义对象一般都会重写hashCode方法,目的是让对象的哈希值和地址没有关系,一般都是通风成员变量来计算哈希值。
  •  字符串哈希值小结:
  1. 字符串内容相同,哈希值一定相同。
  2. 字符串内容不同,哈希值也可能相同。
  • 示例代码1: 
public class HashCodeDemo01 {
    public static void main(String[] args) {
        //创建字符串
        String s1="abc";
        String s2="abc";

        //获取字符串的哈希值
        System.out.println(s1.hashCode());//96354
        System.out.println(s2.hashCode());//96354

        String s3="Aa";
        String s4="BB";

        //获得字符串的哈希值
        System.out.println(s3.hashCode());//2112
        System.out.println(s4.hashCode());//2112
        
    }
}
  •  字符串哈希值小结
  1. 字符串内容相同,哈希值一定相同。(因为底层算法一样)
  2. 字符串内容不同,哈希值也可能相同。 如上面示例代码中的"Aa"和“BB”(底层算法的原因)
  • 扩展
  • public native int hashCode(); 
  • native修饰的方法,依赖操作系统的底层代码,不同的操作系统结果是不同的,而不是源码的原因。
import java.util.Objects;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {

        return Objects.hash(name, age);
    }
}

public class StudentDemo {
    public static void main(String[] args) {
        //创建学生对象
        Student stu1=new Student("Jack",23);
        Student stu2=new Student("Jack",23);

        //没有重写hashCode方法之前,获得对象的哈希值并输出
        System.out.println(stu1.hashCode());//1967205423
        System.out.println(stu2.hashCode());//42121758

        //重写了hashCode方法之后,获得对象的哈希值并输出
        System.out.println(stu1.hashCode());//71329721
        System.out.println(stu2.hashCode());//71329721
    }
}
  •  从输出结果可以看出,当对象没有重写hashCode方法时,不同对象的哈希值是不同的,当重写了hashCode方法之后,只要两个对象的成员变量值相同,它们的哈希值就相同了。

 3.2 HashSet集合

HashSet集合特点

  • HashCode集合的特点?
  1. 无序(存取顺序不一致),无索引,元素不可重复。
  2. 底层结构是哈希表。
  • 什么是哈希表?
  1. JDK1.8之前:哈希表是数组和链表的结合体
  2. JDK1.8之后,哈希表是数组+链表+红黑树的结合体
import java.util.HashSet;

public class hashSetDemo01 {
    public static void main(String[] args) {
        //创建Set集合对象
        HashSet<String> set=new HashSet<>();
        //添加元素
        set.add("abc");
        set.add("bcd");
        set.add("XXX");
        set.add(new String("abc"));

        System.out.println(set);//[bcd, abc, XXX]
    }
}

3.3 哈希表存储元素过程分析

3.3.1 什么是哈希表

  • 在JDK1.8 之前,哈希表是数组+链表
  • 在JDK1.8之后,哈希表是数组+链表+红黑树。当链表长度大于等于8时,将链表转换为红黑树,目的是为了提高查询效率。
  • 简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示:

3.3.2 哈希表的存储过程

  • 每存入一个新的元素都要走以下五步:
  1. 调用对象的hashCode()方法,获得要存储元素的哈希值。
  2. 将哈希值与表的长度(即数组的长度)进行求余运算得到一个整数值,该值就是新元素要存放的位置(即使索引值)。
  • 如果索引值对应的位置上没有存储任何元素,则直接将元素存储到该位置上。
  • 如果索引值对应的位置上已经存储了元素,则执行第2步。

    3.遍历该位置上的所有旧元素,依次比较每个旧元素的哈希值和新元素的哈希值是否相同。

  • 如果有哈希值相同的旧元素,则执行第4步。
  •  如果没有哈希值相同的旧元素,则执行第5步。

   4.比较新元素和旧元素的地址是否相同

  • 如果地址值相同则用新的元素替换老的元素。停止比较。
  • 如果地址值不同,则新元素调用equals方法与旧元素比较内容是否相同。
  • 如果相同返回true,用新的元素替换老的元素,停止比较。
  • 如果返回false,则回到第3步继续遍历下一个旧元素。

  5.说明没有重复,则将新元素存放到该位置上并让新元素记住之前该位置的元素。

  • 总结哈希表的存储过程:
  • 1.先比较哈希值,如果相同,再比较地址值,如果不同,则比较内容(内容相同则不存,内容不同意味着不同的元素,则需要存)
  • 2.先比较哈希值,如果相同,再比较地址值,如果相同,则不比较了,说明内容相同则不存。 
  •  HashSet存储自定义对象
  • 需求如下:定义一个学生类,包含姓名和年龄两个成员变量。
  • 在测试类中创建多个学生对象存储到HashSet集合中
  • 要求相同姓名和年龄的学生对象只存储一个。

代码实现

import java.util.HashSet;

/*
    哈希表存储自定义对象
        存储学生对象,如果姓名和年龄相同只存储一个
    哈希表存储元素底层依赖元素的hashCode和equals方法
 */
public class hashSetDemo02 {
    public static void main(String[] args) {
        //创建set集合
        HashSet<Student> set=new HashSet<>();

        set.add(new Student("jack",20));
        set.add(new Student("rose",22));
        set.add(new Student("lily",24));
        set.add(new Student("jack",20));

        for (Student student : set) {
            System.out.println(student);
        }
        /*
            Student{name='rose', age=22}
            Student{name='jack', age=20}
            Student{name='lily', age=24}
         */
    }
}
  •  HashSet存储元素的过程是根据对象的哈希值来确定元素在集合中的存储位置,底层主要依赖于元素的:hasCode与equals方法。所有当存储自定义对象时,如果希望两个对象成员变量值都相同只存储一个,则需要在自定义类中重写hashCode和equals方法。(hashCode具有去重效果
import java.util.Objects;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {

        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
import java.util.HashSet;

public class HashSetDemo02 {
    public static void main(String[] args) {
        //创建集合对象,该对象存储Student类型
        HashSet<Student> set=new HashSet<Student>();

        set.add(new Student("张国荣",18));
        set.add(new Student("张国荣",18));
        set.add(new Student("王菲",20));
        set.add(new Student("成龙",32));

        for(Student stu:set){
            System.out.println(stu);
        }
    }
}
/*
    Student{name='张国荣', age=18}
    Student{name='成龙', age=32}
    Student{name='王菲', age=20}
 */

3.4 LinkedHashSet集合

LinkedHashSet集合的特点

  • LinkedHashSet集合特点:
  1. 继承HashSet,能够保证存取顺序一致。
  2. 底层结构是:哈希表+链表。
  • HashSet和LinkedHashSet选择:如果要求存取顺序一致,则选LinkedHashSet,否则推荐HashSet。
import java.util.LinkedHashSet;

public class LinkedHashSetDemo01 {
    public static void main(String[] args) {
        LinkedHashSet<String> linkedHashSet=new LinkedHashSet<>();

        linkedHashSet.add("sdfsf");
        linkedHashSet.add("dfsfds");
        linkedHashSet.add("qweqwfsd");
        System.out.println(linkedHashSet);//有序[sdfsf, dfsfds, qweqwfsd]
    }
}

 3.5 单列集合小结

  • List:有序,有索引,元素可重复。
  1. ArrayList:底层结构:数组、增删慢,查询快,线程不安全,效率高。
  2. LinkedList:底层结构:链表,增删快,查询慢,线程不安全,效率高。
  • Set:无序,无索引,元素不可重复。
  1. HashSet:底层结构:哈希表:数组+链表+红黑树,线程不安全,效率高。
  2. LinkedHashSet:底层结构:哈希表+链表,能够保证存取有序,线程不安全,效率高。
  • 单列集合的选择
  • 如果要求元素可重复,则在List体系下选择
  1. 如果只执行查询操作,则选择ArrayList
  2. 如果要执行增删操作,则选择LinkedList
  • 如果要求元素不可重复,则在Set体系下选择
  1. 如果要求存取顺序一致,则选择LinkHashSet,否则选择HashSet

4.可变参数概述

  • 概述:JDK 1.5新特性,参数个数可以是任意个(0到n个)
  1. 格式:数据类型...变量名
  2. 本质:数组 
  • 可变参数注意事项:方法中最多只能有一个可变参数且必须出现在参数列表最后面。
public class Demo01 {
    public static void main(String[] args) {
        System.out.println(sum(1));
        System.out.println(sum(1,34,45,34,1,343,54,23));
        System.out.println(sum(23,34,23,4,5,23,23,23));

    }


    /*
        原始写法:求两个数之和、求三个数之和
     */
   /* public static int sum(int a,int b){
        int sum = a+b;
        return sum;
    }

    public static int sum(int a,int b,int c){
        int sum = a+b+c;
        return sum;
    }*/

    /*
        可变参数写法
     */
    public static int sum(int a,int...arr){
        //定义求和变量
        int result=0;
        //遍历数组
        for(int i:arr){
            result += i;
        }
        return result;
    }
}

5.1 Collections工具类

  • 常用方法
  1. public static <T> boolean addAll(Collection<T> c,T...elements):往集合中添加一些元素。
  2. public static void shuffle(List<?> list)打乱顺序:打乱集合顺序。
  3. public static <T> void sort(List<T>list):将集合中元素按照默认规则排序。
  4. public static <T> void sort(List<T> list,Comparator<? super T>c):使用自定义比较器对集合中的元素进行排序。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class CollectionsDemo {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        /*//原来写法
        list.add(12);
        list.add(43);
        list.add(3245);*/
        //采用工具类 完成往集合中添加元素
        Collections.addAll(list, 12, 43, 3245);
        System.out.println(list);//[12, 43, 3245]

        //对集合元素乱序
        Collections.shuffle(list);
        System.out.println(list);//[43, 3245, 12]

        //排序方法:升序
        Collections.sort(list);
        System.out.println(list);//[12, 43, 3245]

        //使用自定义比较器排序:降序
        Collections.sort(list,new MyComparator());
        System.out.println(list);//[3245, 43, 12]
    }
    /*
        自定义比较器
     */
    static class MyComparator implements Comparator<Integer>{

        @Override
        public int compare(Integer o1, Integer o2) {
            return o2-o1;
        }
    }
}

5.2 Comparator比较器

  • public static <T> void sort(List<T> list):将集合中的元素按照默认规则排序。
  • 我们使用的是默认规则完成整型数据的排序,那么默认规则是怎么定义出来的呢?
  1. 说到排序,简单来说就是两个对象之间比较大小,那么Java中提供了两个比较实现的方式,一种是比较死板的采用java.lang.Comparable接口去实现,一种灵活的当我需要做排序的时候再去选择的java.util.Comparator接口完成。
  2. 那么我们采用的public static <T> void sort(List<T> list)这个方法完成的排序,实际上要求了被排序的类型需要实现Comparable接口完成比较的功能,在Integer类型上为:
  • public final class Integer extends Number implements Comparable<Integer>{}
  • Integer类实现了这个接口,并完成了比较规则的定义,但是这样就把规则写死了,那比如我想要整型数据按照从大到小排序呢?那么这样就要修改String的源代码,这是不可能的了,那么我们这个时候我们可以使用:
  • public static <T> void sort(List<T> list,Comparator<? super T>)方法灵活的完成,这里面就涉及到了Comparator这个接口,位于java.util包下,排序是Comparator能实现的功能之一,该接口代表一个比较器,比较器具有可比性,顾名思义就是做排序的,那么比较的方法就是:
  • public int compare(String 01,String o2):比较两个参数的顺序。
  • 两个对象比较的结果有三种:大于,等于,小于。
  • 如果要按照升序排序,则O1<O2,返回(负数),相等返回0,O1>O2返回正数。
  • 如果要 降序进行排序,O1<O2,返回正数,相等返回0,O1>O2返回负数。
import java.util.Comparator;
    /*
       自定义比较器
    */
class MyComparator implements Comparator<Integer> {

        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
       }
}



import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class CollectionsDemo01 {
    public static void main(String[] args) {
        ArrayList<Integer> list=new ArrayList<Integer>();
        //采用工具类 完成往集合中添加元素
        Collections.addAll(list,5,1000,43,23,2);
        //使用自定义比较器排序:降序
        Collections.sort(list,new MyComparator());
        System.out.println(list);//[1000, 43, 23, 5, 2]
    }
}

 示例代码

/*
    创建一个学生类,存储到ArrayList集合中完成指定排序操作。
 */
public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        return o.age-this.age;//降序
    }
}




import java.util.ArrayList;

public class Demo {
    public static void main(String[] args) {
        //创建4个学生对象,存储到集合中
        ArrayList<Student> list=new ArrayList<Student>();

        list.add(new Student("rose",18));
        list.add(new Student("jack",16));
        list.add(new Student("abc",16));
        list.add(new Student("ace",17));
        list.add(new Student("mark",16));

        //让学生按照年龄降序排列
        for(Student student:list){
            System.out.println(student);
        }

    }
}

自定义比较器(采取匿名内部类): 

import java.util.Collections;

/*
    创建一个学生类,存储到ArrayList集合中完成指定排序操作。
 */
public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    /*@Override
    public int compareTo(Student o) {
        return o.age-this.age;//降序
    }*/
}



import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Demo {
    public static void main(String[] args) {
        //创建4个学生对象,存储到集合中
        ArrayList<Student> list=new ArrayList<Student>();

        list.add(new Student("rose",18));
        list.add(new Student("jack",16));
        list.add(new Student("abc",16));
        list.add(new Student("ace",17));
        list.add(new Student("mark",16));

       /*
        如果在使用的时候,想要独立的定义规则,可以采用Collections.sort(List list,Comparetor c)方式
        自己定义规则(匿名内部类方式)
     */
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o2.getAge()-o1.getAge();//以学生的年龄降序
            }
        });
        System.out.println(list);

    }
}
  • Comparable与Comparator区别
  1. Comparable与Comparator都是Java的接口,用来定义排序规则
  2. Comparable是将排序规则定义在类内部,如果想换一种比较规则,那么就必须修改内部的代码。
  3. Comparator是将排序规则定义在类外部,对List排序时必须同时传入数据和比较器,
  • 如Collections.sort(list,new MyComparator());
  • 如果想换一种比较规则,则需要修改比较器MyComparator,而类内部代码不需要改变。
  •  Comparable与Comparator选择:
  1. 如果排序规则有且只有一种且规定不变,则推荐使用Comparable接口。
  2. 如果排序规则不止一种,则推荐使用Comparator接口。

猜你喜欢

转载自blog.csdn.net/Huangyuhua068/article/details/81346355