20Java基础之集合进阶

集合进阶

  • 集合ArrayList是一种容器,用来装数据的,类似于数组,但集合的大小可变,开发中也经常使用。

集合体系结构

  1. Collection:单列集合
    image
    2.Map:双列集合
    image
  • Collection代表单列集合,每个元素(数据)只包含一个值。
  • Map代表双列集合,每个集合元素包含两个值(键值对)

Collection集合体系

image

Collection集合特点

  • List系列集合:添加的元素是有序的、可重复的、有索引的。
    • ArrayList、LinkedList:有序、可重复、有索引。
  • Set系列集合: 添加的元素是无序的、不重复的、无索引。
    • HashSet:无序、不重复、无索引
    • LinkedHashSet:有序、不重复、无索引。
    • TreeSet:按照大小默认升序排序、不重复、无索引。

案例

//目标:认识集合的特点。
public class CollectionDemo01 {
    public static void main(String[] args) {
        //1. 创建ArrayList集合对象:有序、可重复、有索引
        ArrayList<String> list = new ArrayList<>();
        list.add("李世成");
        list.add("李世成");
        list.add("朱老六");
        list.add("周志琴");
        list.add("赵敏");
        list.add("李世民");
        list.add("李老六");
        System.out.println(list);

        //2. set集合:无序、不重复、无索引
        // 批量修改变量名的快捷键:选中变量名,按shift + F6
        HashSet<String> set = new HashSet<>();
        set.add("李世成");
        set.add("李世成");
        set.add("朱老六");
        set.add("周志琴");
        set.add("赵敏");
        set.add("李世民");
        set.add("李老六");
        System.out.println(set);
    }

输出结果:
[李世成, 李世成, 朱老六, 周志琴, 赵敏, 李世民, 李老六]
[李世民, 李世成, 李老六, 赵敏, 周志琴, 朱老六]

什么要先学Collection的常用方法?

  • Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。
  • Collection的常见方法如下:
    image
    案例:
//目标:掌握Collection提供的常用方法,是全部单列集合都可以直接用的。
public class CollectionAPIDemo01 {
    public static void main(String[] args) {
        Collection<String> list = new ArrayList<>();
        //1. 添加数据boolean add(E e)
        list.add("Java1");
        list.add("Java1");
        list.add("王小蒙");
        list.add("赵天师");
        list.add("灭绝师太");
        System.out.println(list);

        //2. 清空集合
//        list.clear();
        System.out.println(list);

        //3. 判断集合是否为空
        System.out.println(list.isEmpty());

        //4. 直接删除集合中的某个数据:默认只能删除第一个java1
        System.out.println(list.remove("java1"));
        System.out.println(list);

        //5. 判断集合中是否包含某个数据
        System.out.println(list.contains("java1"));
        System.out.println(list.contains("java1"));

        //6.获取集合的大小(元素个数)
        System.out.println(list.size());

        //7.把集合转化成数组
        Object[] arr = list.toArray();
        System.out.println(Arrays.toString(arr));
        //拓展
        String[] arrays = list.toArray(String[]:: new);
        System.out.println(Arrays.toString(arrays));

        //8.拓展一下:把别人集合的数据加给自己
        Collection<String> c1 = new ArrayList<>();
        Collection<String> c2 = new ArrayList<>();
        c1.add("java1");
        c1.add("java2");
        c2.add("java3");
        c2.add("java4");
        //把c2集合的数据全部导入c1集合
        c1.addAll(c2);
        System.out.println(c1);
        System.out.println(c2);
    }
}

输出结果:
[Java1, Java1, 王小蒙, 赵天师, 灭绝师太]
[Java1, Java1, 王小蒙, 赵天师, 灭绝师太]
false
false
[Java1, Java1, 王小蒙, 赵天师, 灭绝师太]
false
false
5
[Java1, Java1, 王小蒙, 赵天师, 灭绝师太]
[Java1, Java1, 王小蒙, 赵天师, 灭绝师太]
[java1, java2, java3, java4]
[java3, java4]

Collection的遍历方式

  • 迭代器
    • 迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表Iterator.

Colletion集合获取迭代器的方法
image

  • Iterator iterator():得到迭代器对象,默认指向当前集合的索引0.

Iterator迭代器中的常用方法
image

注意:如果迭代器获取集合的元素越界,会出现NoSuchElementException异常。
代码案例:

//目标:掌握collection集合的遍历方式:迭代器遍历
public class CollectionDemo01 {
    public static void main(String[] args) {
        //1.准备一个集合
        ArrayList<String> list = new ArrayList<>();
        list.add("赵敏");
        list.add("小昭");
        list.add("殷素素");
        list.add("周芷若");
        list.add("古力娜扎");

        System.out.println(list);

        // 2. 得到这个集合对象的迭代器对象
        // 迭代器,默认指向集合的第一个元素。
        Iterator<String> it = list.iterator();
        //3. 使用循环改进
        while(it.hasNext()){
            String res = it.next();
            System.out.println(res);
        }

//        System.out.println(it.next());
//        System.out.println(it.next());
//        System.out.println(it.next());
    }
}

增强for循环
-格式:
for(元素的数据类型 变量名:数组或者集合){
...
}
例如:
Collection c = new ArrayList<>();
for(String s: c){
System.out.println(s);
}

  • 增强for可以用来遍历集合或者数组。
  • 增强for遍历集合,本质就是迭代器遍历集合的简化写法。
    案例:
//目标:掌握collection集合的遍历方式:迭代器遍历
public class CollectionDemo02 {
    public static void main(String[] args) {
        //1.准备一个集合
        ArrayList<String> list = new ArrayList<>();
        list.add("赵敏");
        list.add("小昭");
        list.add("殷素素");
        list.add("周芷若");
        list.add("古力娜扎");

        //2. 增强for循环遍历集合
        for(String s: list){
            System.out.println(s);
        }

        //3. 增强for循环遍历数组
        int[] arr = {1,2,3,4,5};
        for(int i: arr){
            System.out.println(i);
        }
    }
}

Lambda表达式

  • 得益于JDK8开始的新技术Lambda表达式,提供了一种更简单、更直接的方式来遍历集合。
  • 需要使用Collection的如下方法来完成
    image
    代码案例:
//目标:掌握collection集合的遍历方式:迭代器遍历
public class CollectionDemo03 {
    public static void main(String[] args) {
        //1.准备一个集合
        ArrayList<String> list = new ArrayList<>();
        list.add("赵敏");
        list.add("小昭");
        list.add("殷素素");
        list.add("周芷若");
        list.add("古力娜扎");

        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });

        //简化上面代码
        list.forEach(s -> System.out.println(s));

        //再简化
        list.forEach(System.out::println);
    }
}

案例:遍历集合中的自定义对象

电影类:
public class Movie {
    private String name;
    private String actor;
    private double score;

    //2. 提供空参和全参构造
    public Movie() {
    }

    public Movie(String name, String actor, double score) {
        this.name = name;
        this.actor = actor;
        this.score = score;
    }

    public String getName() {
        return name;
    }

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

    public String getActor() {
        return actor;
    }

    public void setActor(String actor) {
        this.actor = actor;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Movie{" +
                "name='" + name + '\'' +
                ", actor='" + actor + '\'' +
                ", score=" + score +
                '}'+"\n";
    }
}

测试:
public class Test {
    public static void main(String[] args) {
        //1. 创建集合
        ArrayList<Movie> movies = new ArrayList<>();

        //2. 存入电影对象
        movies.add(new Movie("《消失的她》", "文咏珊, 倪妮", 9.5));
        movies.add(new Movie("《八角笼中》", "王宝强", 7.5));
        movies.add(new Movie("《三万里》", "李白", 8.5));

        //3.遍历集合中的每个电影对象
        for (Movie m : movies) {
            System.out.println(m);
        }
    }
}

集合的并发修改异常

  • 使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。
  • 由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误。

怎么保证遍历集合同时删除数据时不出bug?

  • 使用迭代器遍历集合,但用迭代器自己的删除方法删除数据即可
  • 如果能用for循环遍历时:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做i--操作
    代码案例:
//目标:三种遍历可能出现的并发修改异常问题。
public class CollectionDemo05 {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>();
        names.add("Java入门");
        names.add("宁夏枸杞");
        names.add("黑枸杞");
        names.add("人字拖");
        names.add("特级枸杞");
        names.add("枸杞子");

        //1. 使用迭代器遍历集合并删除枸杞
        // 注意1:如果使用迭代器遍历,并用集合删除数据,会出现并发修改异常,程序出现bug。
        Iterator<String> it = names.iterator();
        while(it.hasNext()){
            String name = it.next();
            if(name.contains("枸杞")){
//                names.remove(name);
                it.remove();    //必须调用迭代器自己的删除方法,才会避免产生并发修改异常。
            }
        }
        System.out.println(names);

        //2. 使用增强for遍历集合并删除枸杞(本质就是迭代器):一定会出错,且无法解决。
        ArrayList<String> names1 = new ArrayList<>();
        names1.add("Java入门");
        names1.add("宁夏枸杞");
        names1.add("黑枸杞");
        names1.add("人字拖");
        names1.add("特级枸杞");
        names1.add("枸杞子");

        /*for (String name : names1) {
            if(name.contains("枸杞")){
                names1.remove(name);
            }
        }
        System.out.println(names1);*/

        //3. Lambda遍历集合并删除枸杞(本质就是迭代器):一定会出错,且无法解决。
        names1.forEach(name-> {
            if(name.contains("枸杞"))
                names1.remove(name);
        });
        System.out.println(names1);

        // 注意:如果是ArrayList带索引的集合,我们也可以使用for循环删除每次退一步,或者从后面倒着遍历并删除。
    }
}

List集合

List集合的特有方法

  • List集合因为支持索引,所以多了很多于索引相关的方法,当然collection的功能List也都继承了。
    image
    案例代码:
//目标:掌握List的特有方法。
public class ListDemo01 {
    public static void main(String[] args) {
        //1. 创建一个List集合对象:(一行经典代码)
        List<String> lst = new ArrayList<>();
        lst.add("杨婷婷");
        lst.add("李晨曦");
        lst.add("石梅");
        lst.add("周芷若");
        System.out.println(lst);

        //2. 给某个位置插入一个数据
        lst.add(2, "王强");
        System.out.println(lst);

        //3. 根据索引删除数据
        String name = lst.remove(3);
        System.out.println(lst);
        System.out.println(name);

        //4. 修改索引位置处的数据
        lst.set(2, "刘大乐");
        System.out.println(lst);

        //5. 根据索引取数据
        System.out.println(lst.get(2));
    }
}

List系列集合特点

  • 有序、可重复、有索引
  • ArrayList和LinkedList特点都一样。但是底层采用的数据结构不同,应用场景不同。

ArrayList集合的底层原理

  • 基于数组实现的。具有数组的特点。
  • 查询速度快(注意:是根据索引查询数据块):查询数据通过地址值和索引定位,查询任意数据耗时相同。
  • 删除效率低:可能需要把后面很多的数据进行前移。
  • 添加效率极低:可能需要把后面很多的数据后移,再添加元素;或者也可能进行数组的扩容。
  1. 利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组。
  2. 添加第一个元素时,底层会创建一个新的长度为10的数组。
    image

ArrayList集合适合的应用场景

  1. ArrayList适合:根据索引查询数据。比如根据随机索引取数据(高效)!或者数据量不是很大时!
    2.ArrayList不适合:数据量大的同时,又要频繁的进行增删操作。

LinkedList集合的底层原理

  • 基于双链表实现的。
  1. 什么是链表,有什么特点呢?
    image
  • 链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数值和下一个结点的地址。
  • 链表的特点:
    1. 查询慢,无论查询哪个数据都要从头开始找。
    2. 链表增删相对快。
      image

双链表的特点:

  • 查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的。
    LinkedList新增了:很多首尾操作的特有方法。
    image

LinkedList的应用场景之一

  • 可以用来设计队列。队列只是在首尾增删元素,用LinkedList来实现很合适。
  • 队列的特点就是先进先出。
    代码案例:
//目标:掌握linkedList的应用
public class QueueDemo01 {
    public static void main(String[] args) {
        //1.做队列
        LinkedList<String> queue = new LinkedList<>();
        //2. 入队
        queue.addLast("1号客人");
        queue.addLast("2号客人");
        queue.addLast("3号客人");
        queue.addLast("4号客人");
        queue.addLast("5号客人");

        System.out.println(queue);

        //3.出队
        queue.removeFirst();
        queue.removeFirst();
        System.out.println(queue);
    }
}

输出结果:
[1号客人, 2号客人, 3号客人, 4号客人, 5号客人]
[3号客人, 4号客人, 5号客人]

LinkedList的应用场景之一:可以用来设计栈。

  • 栈的特点:后进先出,先进后出。
  • 只是在首部增删元素,用LinkedList来实现很合适。
  • 数据进入栈模型的过程称为:压/进栈(push)
  • 数据离开栈模型的过程称为:弹/出栈(pop)

案例代码:

//目标:掌握linkedList的应用
public class QueueDemo01 {
    public static void main(String[] args) {
        //1.定义栈
        LinkedList<String> stack = new LinkedList<>();

        //2.入栈
        stack.addFirst("1号子弹");
        stack.addFirst("2号子弹");
        stack.addFirst("3号子弹");
        stack.addFirst("4号子弹");
        stack.addFirst("5号子弹");
        System.out.println(stack);

        //3.出栈
        stack.removeFirst();
        stack.removeFirst();
        System.out.println(stack);
    }
}

单链表实现

//目标:单链表的实现。
public class MyLinkedList<E> {
    private int size = 0;
    MyLinkedList.Node<E> first; // 头指针

    public static class Node<E>{
        E item;
        Node<E> next; //下个节点地址

        public Node(E item, Node<E> next) {
            this.item = item;
            this.next = next;
        }
    }

    public boolean add(E e){
        //维护单链表
        //第一个节点,或者是后面的节点
        //1. 创建一个节点对象
        Node<E> newnode = new Node<>(e, null);
        //2. 判断这个节点是否是第一个节点
        if(first == null){
            first = newnode;
        }
        else{
            //把这个节点加入到当前最后一个节点的后面
            //如何找到最后一个节点对象?
            //定义一个临时节点
            Node<E> temp = first;
            while(temp.next != null){
                temp = temp.next;
            }

            temp.next = newnode;
        }
        size++;
        return true;
    }

    public boolean remove(E e){
        Node<E> prev = null;//记录节点的前驱节点
        Node<E> temp = first;

        while(temp != null){
            // 处理 item 为 null 的情况(避免空指针)
            boolean isEqual = (e == null) ? (temp.item == null) : e.equals(temp.item);
            if(isEqual){
                // 情况1:删除头节点
                if (prev == null) {
                    first = temp.next;  // 头指针后移
                }
                // 情况2:删除中间/尾节点
                else {
                    prev.next = temp.next;  // 前驱节点直接指向后继节点,断开当前节点
                }
                size--;
                return true;
            }
            // 未找到时,更新前驱和当前节点
            prev = temp;
            temp = temp.next;
        }
        return false;
    }

    @Override
    public String toString() {
        StringJoiner sj = new StringJoiner(",", "[", "]");
        Node<E> temp = first;

        while(temp != null){
            sj.add(temp.item + "");
            temp = temp.next;
        }

        return sj.toString();
    }

    public int size(){
        return size;
    }
}

class Test{
    public static void main(String[] args) {
        MyLinkedList<String> lst = new MyLinkedList<String>();
        lst.add("JAVA1");
        lst.add("JAVA2");
        lst.add("JAVA3");
        lst.add("JAVA4");
        lst.add("JAVA5");
        lst.add("JAVA6");
        lst.add("JAVA7");
        lst.add("JAVA8");
        lst.add("JAVA9");
        lst.add("JAVA10");

        System.out.println(lst);

        lst.remove("JAVA10");
        System.out.println(lst);

        lst.remove("JAVA5");
        System.out.println(lst);
    }
}

Set系列集合

  • 特点:

    • 无序:添加数据的顺序和获取出的顺序不一致;
    • 不重复;
    • 无索引;
  • HashSet:无序、不重复、无索引。

  • LinkedHashSet:有序、不重复、无索引

  • TreeSet:排序、不重复、无索引

注意:Set要用的常用方法,基本上就是Collection提供的!!自己几乎没有额外新增一些常用功能。
代码案例

// 目标:了解Set家族的特点:无序、不重复、无索引
public class SetDemo01 {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>(); //多态,一行经典代码
        set.add("张无忌");
        set.add("张无忌");
        set.add("张真人");
        set.add("周芷若");
        set.add("小昭");
        set.add("赵敏");
        System.out.println(set);
    }
}

在正式了解HashSet集合的底层原理前,我们需要先搞清楚一个前置知识:哈希值。

哈希值

  • 就是一个int类型的数值,Java中每个对象都有一个哈希值。
  • Java中所有对象,都可以调用Object类提供的hashCode方法,返回该对象自己的哈希值。

对象哈希值的特点

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
  • 不同的对象,它们的哈希值一般不相同,但也有可能相同(发生哈希碰撞)
    代码案例
// 目标:拿到对象的哈希值
public class SetDemo02 {
    public static void main(String[] args) {
        String name = "adf";
        String name1 = "dac";

        //不同对象的哈希值大概率不相同,但可能存在相同情况
        System.out.println(name.hashCode());
        System.out.println(name.hashCode());
        System.out.println(name1.hashCode());
    }
}

HashSet集合的底层原理

  • 基于哈希表实现。
  • 哈希表是一种增删改查数据,性能都较好的数据结构。

哈希表

  • JDK8之前,哈希表=数组+链表
  • JDK8开始,哈希表=数组+链表+红黑树

JDK8之前HashSet集合的底层原理,基于哈希表:数组+链表
image

  1. 创建一个默认长度16的数组,默认加载因子为0.75,数组名table。
  2. 使用元素的哈希值数组的长度做运算计算出应存入的位置。
  3. 判断当前位置是否为null,如果是null直接存入。
  4. 如果不为null表示有元素,则调用equals方法比较。相等,则不存;不相等,则存入数组。
  • JDK8之前,新元素存入数组,占老元素位置,老元素挂下面。
  • JDK之后,新元素直接挂在老元素的下面。

哈希表是一种增删改查数据性能较好的结构。

  1. JDK8开始,当链表长度超过8,且数组长度>=64时,自动将链表转成红黑树。
    image

小结

  • JDK8开始后,哈希表中引入了红黑树后,进一步提高了操作数据的性能。

了解数据结构(树)
二叉树
image
image

  • 二叉树中,任意节点的度<=2。
  • 度:每个节点子节点数量。
  • 树高:树的总层数。
  • 根节点:最顶层的节点。
    image
  • 规则:小的存左边,大的存右边,一样的不存。
  • 二叉查找树存在的问题:如果一组数本身就是排好序的,它在存放的时候容易变成单腿结构。导致查询的性能与单链表一样,查询速度变慢。
    image

平衡二叉树

  • 在满足查找二叉树的大小规则下,让树尽可能矮小,以此提高查数据的性能。
    image
  • 红黑树,就是平衡的二叉树。
  • 红黑树是一种增删改查数据性能相对都较好的结构。

深入理解HashSet集合去重的机制

  • HashSet集合默认不能对内容一样的两个不同对象去重复。
  • 比如内容一样的两个学生对象存入到HashSet集合中去,HashSet集合是不能去重复的。

结论:如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法。
案例代码

学生类:
public class Student {
    private String name;
    private char sex;
    private String hobby;

    // 只要两个对象内容一样结果就是true
    @Override
    public boolean equals(Object o){
        if(this == o) return true;
        if(o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return sex == student.sex && name.equals(student.name) && hobby.equals(student.hobby);
    }

    // 只要两个对象的内容一样,返回的哈希值就是一样的。
    @Override
    public int hashCode() {
        return Objects.hash(name, sex, hobby);
    }

    public Student() {
    }

    public Student(String name, char sex, String hobby) {
        this.name = name;
        this.sex = sex;
        this.hobby = hobby;
    }

    public String getName() {
        return name;
    }

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

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

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

测试:
// 目标:理解HashSet集合去重复。
public class SetDemo03 {
    public static void main(String[] args) {
        Set<Student> sets = new HashSet<>();
        Student s1 = new Student("刘德发", '男', "打游戏");
        Student s2 = new Student("张德顺", '男', "聊天");
        Student s3 = new Student("李志谦", '男', "看电影");
        Student s4 = new Student("李志谦", '男', "看电影");
        Student s5 = new Student("王璇", '女', "逛街");

        System.out.println(s3.hashCode());
        System.out.println(s4.hashCode());

        sets.add(s1);
        sets.add(s2);
        sets.add(s3);
        sets.add(s4);
        sets.add(s5);
        System.out.println(sets);
    }
}

结果:
-549765396
-549765396
[Student{name='刘德发', sex=男, hobby='打游戏'}
, Student{name='张德顺', sex=男, hobby='聊天'}
, Student{name='李志谦', sex=男, hobby='看电影'}
, Student{name='王璇', sex=女, hobby='逛街'}
]

LinkedHashSet底层原理

  • 依然是基于哈希表(数组、链表、红黑树)实现的。
  • 但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置
    代码案例
//目标:了解Set家族的特点:无序、不重复、无索引
public class SetDemo04 {
    public static void main(String[] args) {
        Set<String> set = new LinkedHashSet<>();// 有序的、不重复、无索引
        set.add("java1");
        set.add("java1");
        set.add("html");
        set.add("html");
        set.add("css");
        set.add("js");
        System.out.println(set);
    }
}
输出结果:
[java1, html, css, js]

TreeSet

  • 特点:不重复、无索引、可排序(默认升序排序,按照元素的大小,由小到大排序)
  • 底层是基于红黑树实现的排序。

注意

  • 对于数值类型:Integer、Double,默认按照数值本身的大小进行升序排序。
  • 对于字符串类型:默认按照首字符的编号升序排序。
  • 对于自定义类型如Student对象,TreeSet默认是无法直接排序的。

自定义排序规则

  • TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则。
  • 方式一:
    • 让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。
  • 方式二:
    • 通过调用TreeSet集合有参数构造器,可以设置comparator对象(比较器对象,用于指定比较规则)。
      Public TreeSet(Comparator<? super E> comparator)

案例代码

女孩类:
// 方法一:类实现comparable接口,重写compareTo方法
public class Girl implements Comparable<Girl>{
    private String name;
    private char sex;
    private int age;
    private double height;

    public Girl() {
    }

    public Girl(String name, char sex, int age, double height) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.height = height;
    }

    public String getName() {
        return name;
    }

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

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

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

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    @Override
    public String toString() {
        return "Girl{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                ", age=" + age +
                ", height=" + height +
                '}' + '\n';
    }

    @Override
    public int compareTo(Girl o) {
        return o.age - this.age >= 0 ? 1 : -1;
    }
}

测试:
//目标:TreeSet排序对象
public class SetDemo05 {
    public static void main(String[] args) {
        // 方式二:TreeSet集合自带比较器对象Comparator
//        Set<Girl> sets = new TreeSet<>(); //排序,不重复,无索引
        /*Set<Girl> sets = new TreeSet<>(new Comparator<Girl>() {
            @Override
            public int compare(Girl o1, Girl o2) {
                return Double.compare(o2.getHeight(),o1.getHeight());
            }
        }); //排序,不重复,无索引*/

        Set<Girl> sets = new TreeSet<>((o1,o2)-> Double.compare(o2.getHeight(),o1.getHeight())); //排序,不重复,无索引

        sets.add(new Girl("艾莎", '女', 18, 1.65));
        sets.add(new Girl("佩奇", '女', 8, 1.31));
        sets.add(new Girl("白雪公主", '女', 21, 1.70));
        sets.add(new Girl("白发魔女", '女', 21, 1.72));
        sets.add(new Girl("灰姑娘", '女', 17, 1.75));

        System.out.println(sets);
    }
}

总结

  1. 如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据

    • 用ArrayList集合(有序、可重复、有索引),底层基于数组的。(常用)
  2. 如果希望记住元素的添加顺序,且增删首尾数据的情况较多?

  • 用LinkedList集合(有序、可重复、有索引),底层基于双链表实现的。
  1. 如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快?
  • 用HashSet集合(无序,不重复,无索引),底层基于哈希表实现的。(常用)
  1. 如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快?
  • 用LinkedHashSet集合(有序,不重复,无索引),底层基于哈希表和双链表。
  1. 如果要对元素进行排序,也没有重复元素需要存储?且希望增删改查都快?
  • 用TreeSet集合,基于红黑树实现。
posted @ 2025-08-11 11:27  狂风将军  阅读(13)  评论(0)    收藏  举报