08_Collection集合2

List 集合

特点与方法

List 系列集合:添加的元素是有序、可重复、有索引

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        // 1. 创建一个 ArrayList 集合对象
        List<String> lst = new ArrayList<>();
        lst.add("Jack");
        lst.add("Peter");
        lst.add("Tony");
        System.out.println(lst);  // [Jack, Peter, Tony]

        // 2. 在某索引位置,插入元素
        lst.add(1,"Tomato");
        System.out.println(lst);  // [Jack, Tomato, Peter, Tony]

        // 3. 带走某个索引的元素
        System.out.println(lst.remove(1));  // Tomato
        System.out.println(lst);  // [Jack, Peter, Tony]

        // 4. 返回某索引的元素
        System.out.println(lst.get(2));  // Tony

        // 5. 修改索引位置处的元素,修改成功后,会返回原来的数据
        System.out.println(lst.set(2, "Wang"));  // Tony
        System.out.println(lst);  // [Jack, Peter, Wang]

    }
}
遍历方式
  • for 循环(因为 List 集合有索引)
  • 迭代器
  • 增强 for 循环
  • Lambda 表达式

具体代码演示省略,请参照其父类

底层原理

ArrayList 集合的底层原理

  • 基于数组实现

  • 特点

    查询速度快(通过索引)

    删除效率低

    添加效率低

LinkedList 集合的底层原理

  • 基于双链表实现

  • 特点

    查询速度慢

    增加和删除相对较快

  • 应用场景:可以用来设计队列,也可以设计栈

java 复制代码
// 模拟队列

import java.util.LinkedList;

public class Test {
    public static void main(String[] args) {
        // 1. 创建一个队列
        LinkedList<String> queue = new LinkedList<>();

        // 入队
        queue.addLast("第1号人");
        queue.addLast("第2号人");
        queue.addLast("第3号人");
        queue.addLast("第4号人");
        System.out.println(queue);
        
        // 出队
        System.out.println(queue.removeFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue);
    }
}
java 复制代码
// 模拟栈

import java.util.LinkedList;

public class Test {
    public static void main(String[] args) {
        // 1. 创建一个栈
        LinkedList<String> stack = new LinkedList<>();

        // 入栈(push)
        stack.addFirst("第1颗子弹");  // 等价于stack.push("第1颗子弹")
        stack.addFirst("第2颗子弹");  // 注意,官方的push背后的本质就是stack.addFirst()
        stack.addFirst("第3颗子弹");
        stack.addFirst("第4颗子弹");
        System.out.println(stack);
        
        // 出栈(pop)
        System.out.println(stack.removeFirst());  // 等价于stack.pop()
        System.out.println(stack.removeFirst());  // 官方pop背后的本质就是stack.removeFirst()
        System.out.println(stack.removeFirst());
        System.out.println(stack);
    }
}

Set 集合

特点
  • 无序、不重复、无索引
  • Set 要用到的常用方法,基本上就是 Collection 提供的!自己几乎没有额外新增的常用功能
HashSet 集合的底层原理

HashSet 集合:无序、不重复、无索引

哈希值

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

对象哈希值的特点

  • 同一个对象多次调用 hashCode() 方法返回的哈希值是相同的
  • 不同的对象,它们的哈希值一般不相同,但也有可能相同(哈希碰撞)

底层原理

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

哈希表

  • JDK 8 之前:哈希表 = 数值 + 链表
  • JDK 8 之后:哈希表 = 数值 + 链表 + 红黑树

补充:如果希望 Set 集合认为2个内容一样的对象是重复的,必须重写对象的 hashCode() 和 equals() 方法

java 复制代码
import java.util.Objects;

public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("Jack", 20);
        Student s2 = new Student("Tony", 25);
        Student s3 = new Student("Tony", 25);
        // 哈希值
        System.out.println(s1.hashCode());  // 71329718
        System.out.println(s2.hashCode());  // 80993012
        System.out.println(s3.hashCode());  // 80993012
        System.out.println(s2.hashCode() == s3.hashCode());  // true
        
    }
}

class Student {
    private String name;
    private int age;

    // 重写 equals 方法
    @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);
    }

    // 重写 hashCode 方法
    @Override
    public int hashCode() {
        return Objects.hash(name, 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;
    }
}
LinkedHashSet 集合

LinkedHashSet 集合:有序、不重复、无索引

底层原理

  • 依然是基于哈希表(数值、链表、红黑数)实现的
  • 但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置
TreeSet 集合

TreeSet 集合:不重复、无索引、可排序(默认升序排序)

底层是基于红黑树实现的排序

java 复制代码
import java.util.Set;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        Set<Integer> set1 = new TreeSet<>();
        set1.add(6);
        set1.add(5);
        set1.add(5);
        set1.add(7);
        System.out.println(set1);  // [5, 6, 7]
    }
}

对于自定义类型,如 Student 对象,TreeSet 默认是无法直接排序的(会报错)

java 复制代码
import java.util.Set;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        // 未简化的
//        Set<Student> set1 = new TreeSet<>(new Comparator<Student>() {
//            @Override
//            public int compare(Student o1, Student o2) {
//                return Double.compare(o1.getHeight(), o2.getHeight());
//            }
//        });

        // lambda 简化后
        Set<Student> set1 = new TreeSet<>((o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight()));

        set1.add(new Student("Jack", 25, 51.2));
        set1.add(new Student("Tony", 18, 77.9));
        set1.add(new Student("Peter", 33, 65.8));
        System.out.println(set1);
        // [Student{name='Jack', age=25, height=51.2}, Student{name='Peter', age=33, height=65.8}, Student{name='Tony', age=18, height=77.9}]
    }
}

class Student {
    private String name;
    private int age;
    private double height;

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

    public Student() {
    }

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

    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;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }
}
注意事项:集合的并发修改异常问题

注意:使用迭代器遍历集合的时候,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。

产生问题的原因:当我们正向的进行"迭代"或者 for 循环时,变量 i 指向了需要被删除的元素,我们成功删除对象后,其后面的所有元素会立刻前移,此时变量 i 的指向却还是停留原地,这就导致了指向的不匹配!这个问题,在 for 循环中不会报错,但是无法完成业务,而且较难发现问题所在,为了避免这个问题的难以发现,迭代器就贴心的为我们进行了报错,

解决办法

  • 如果能使用 for 循环遍历:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做 i - - 操作
  • 使用迭代器遍历集合,但用迭代器自己的删除方法删除数据即可(其方法的本质上是做了类似 i - - 的操作)

Collection 其他内容

可变参数
java 复制代码
import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        // 可变参数
        run();
        run(10);
        run(10, 20, 30);
        run(10, 20, 30, 40);
        
        // 运行结果:
        // []
        // [10]
        // [10, 20, 30]
        // [10, 20, 30, 40]
    }

    // 注意事项:
    // 1. 一个形参列表中,只能有一个可变参数
    // 2. 可变参数必须放在形参列表的最后面
    public static void run(int... nums) {
        // 可变参数在方法内部,本质就是数组
        System.out.println(Arrays.toString(nums));
    }
}
Collections
  • 注意!它不是 Collection 集合,它多了个"s"
  • Collections 是一个用来操作集合的工具类
java 复制代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        // 1. 为集合批量添加数据
        List<String> lst = new ArrayList<>();
        Collections.addAll(lst, "Jack", "Peter", "Tony");
        System.out.println(lst);  // [Jack, Peter, Tony]

        // 2. 打乱List集合里面的元素顺序
        Collections.shuffle(lst);
        System.out.println(lst);  // [Peter, Tony, Jack]

        // 3. 对List集合里面的元素进行升序排序
        List<Integer> lst2 = new ArrayList<>();
        lst2.add(3);
        lst2.add(5);
        lst2.add(1);
        System.out.println(lst2);  // [3, 5, 1]
        Collections.sort(lst2);
        System.out.println(lst2);  // [1, 3, 5]

        // 4. 自定义的对象如何排序
        List<Student> lst3 = new ArrayList<>();
        lst3.add(new Student("Jack", 25, 51.2));
        lst3.add(new Student("Tony", 18, 77.9));
        lst3.add(new Student("Peter", 33, 65.8));

        Collections.sort(lst3, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return Double.compare(o1.getHeight(), o2.getHeight());
            }
        });
    }
}

class Student {
    private String name;
    private int age;
    private double height;

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

    public Student() {
    }

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

    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;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }
}
相关推荐
风铃儿~19 分钟前
Spring AI 入门:Java 开发者的生成式 AI 实践之路
java·人工智能·spring
斯普信专业组24 分钟前
Tomcat全方位监控实施方案指南
java·tomcat
忆雾屿35 分钟前
云原生时代 Kafka 深度实践:06原理剖析与源码解读
java·后端·云原生·kafka
武昌库里写JAVA1 小时前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
gaoliheng0061 小时前
Redis看门狗机制
java·数据库·redis
我是唐青枫1 小时前
.NET AOT 详解
java·服务器·.net
Su米苏1 小时前
Axios请求超时重发机制
java
本郡主是喵3 小时前
并发编程 - go版
java·服务器·开发语言
南风lof3 小时前
源码赏析:Java线程池中的那些细节
java·源码阅读