【学习日记2023.4.4】之初识数据结构_集合(Set及其实现类底层原理)_可变参数_Collections工具类

本文详细介绍了数据结构中的数组、单链表和双向链表,以及Java集合框架中的Set实现,包括HashSet、LinkedHashSet和TreeSet的特性和底层原理。此外,文章还讨论了可变参数的使用和Collections工具类的常见方法。对于HashSet,特别强调了自定义类型存储时重写equals和hashCode方法的重要性。
摘要由CSDN通过智能技术生成

1. 数据结构

  • 什么是数据结构: 指存储和组织数据的方式

数组(ArrayList中基于数组的实现)

  • 多个元素之间的空间是连续的
  • 根据索引的查询速度快: 查询数据通过地址值和索引值(list.get(i))定位,查询任意数据耗时相同
  • 删除效率低: 可能需要把后面的数据前移
  • 添加效率低: 可能需要把后面的数据后移,再添加元素(插队添加);或者也可能需要进行数组的扩容
    请添加图片描述

单链表

  • 链表中的节点是独立的对象,在内存中不连续,每个节点包含数据值和下一个节点的地址值
  • 特点
    • 查询慢,无论查询哪个数据都要从头开始找
    • 增删相对较快
      请添加图片描述

双向链表

  • 一个节点包含三个值
    • 数据值
    • 前一个节点的地址值
    • 后一个节点的地址值
  • 头节点: 始终被记录着,头节点没有前一个节点的地址
  • 尾节点: 始终被记录着,尾节点没有后一个节点的地址
    请添加图片描述

  • 先进后出,后进先出,像弹夹
  • 比如栈内存
    请添加图片描述

队列

  • 先进先出,后进后出,像排队
  • 讲线程池-阻塞队列
    请添加图片描述

2. Set集合

2.1 Set集合的特点

  • HashSet: 存取无序,不重复,无索引
  • LinkedHashSet: 存取有序,不重复,无索引
  • TreeSet: 排序,不重复,无索引
public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
//        Set<Integer> set = new LinkedHashSet<>();
//        Set<Integer> set = new TreeSet<>();
        set.add(666);
        set.add(555);
        set.add(555);
        set.add(888);
        set.add(888);
        set.add(777);
        set.add(777);
        System.out.println(set);
    }

2.2 HashSet存储自定义类型

2.2.1 现象

  • 需要重写equals和hashCode方法,才能去重
public class Student {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", 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;
    }

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

    public Student() {
    }

    @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 static void main(String[] args) {
    Student stu1 = new Student("张三",23);
    Student stu2 = new Student("李四",24);
    Student stu3 = new Student("李四",24);
    Student stu4 = new Student("王五",25);
    Set<Student> set = new HashSet<>();
    set.add(stu1);
    set.add(stu2);
    set.add(stu3);
    set.add(stu4);
    System.out.println(set);
}

2.2.2 hashCode方法和哈希值

Object中的hashCode方法
  • 自定义类型默认使用父类Object中的hashCode方法
    • public native int hashCode();
      • 此方法为本地方法,C++写的,采用哈希算法
      • 此方法的返回值为int数值,也被称为哈希值
    • 哈希值:
      • 每个对象都可以通过hashCode方法获取
      • 特点: 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
      • 不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。
重写的hashCode方法
  • 根据成员变量的值计算
@Override
public int hashCode() {
    // 根据成员变量的值计算的
    return Objects.hash(name, age);
}

2.2.3 HashSet底层原理

JDK8以前
  • 数据结构: 哈希表:数组 + 链表
  • 使用空参构造创建HashSet集合,第一次添加元素时。创建一个默认长度为16的数组,默认加载因子为0.75
    • 加载因子: 当集合中元素的个数达到 16 * 0.75(=12) 扩容2倍
  • 添加元素时,使用元素的哈希值和数组长度计算应存入的索引位置
    • 判断当前索引位置是否为null
      • 是null 存入数组
      • 不是null 表示有元素,调用equals方法逐个比较
        • 相同不存
        • 不同,JDK8以前将新元素存入数组,老元素挂在新元素的下面,形成链表
JDK8开始
  • 数据结构: 哈希表:数组 + 链表 + 红黑树
  • 使用空参构造创建HashSet集合,第一次添加元素时创建一个默认长度为16的数组,默认加载因子为0.75
    • 加载因子: 当数组中元素的个数达到 16 * 0.75 扩容2倍
  • 添加元素时,使用元素的哈希值和数组长度计算应存入的索引位置
    • 判断当前索引位置是否为null
      • 是null 存入数组
      • 不是null 表示有元素,调用equals方法逐个比较
        • 相同不存
        • 不同,JDK8开始-新元素挂在老元素的下面,形成链表
        • JDK8开始-如果链表的长度超过8,且数组的长度>=64,自动将链表转成红黑树(提升操作数据效率)
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
HashSet存储自定义类型
  • 如果想要认为内容相同就是相同的对象
    • 需要重写hashCode方法(通过成员变量的值计算),决定存入哈希表中数组的索引位置
    • 需要重写equals方法(比较成员变量的值),相同就去重

2.2.4 数据结构-树形结构

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
整理总结

请添加图片描述

在线图形化测试用例:
  • https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
  • 7 4 10 2 5 1
    请添加图片描述

2.3 LinkedHashSet

2.3.1 特点

  • 存取有序,不能重复,没有索引

2.3.2 原理

  • 不能重复: 底层的数据结构: 哈希表
  • 有序: 每一个元素都额外的多了一个双向链表机制,记录它前后元素的位置
    请添加图片描述

2.4 TreeSet

2.4.1 特点

  • 排序(默认升序),不重复,无索引
  • TreeSet存储自定义类型需要指定排序规则
    • 自然排序: 自定义类实现Comparable接口,重写compareTo方法
    • 比较器排序: 使用TreeSet带参数构造方法(Comparator),在重写compare方法中指定排序规则

2.4.2 原理

  • 数据结构: 红黑树
    • https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
    • 默认升序
      • 小的存左边
      • 大的存右边
      • 相同的不存

3. Collection单列集合小结

单列集合构造方法

构造方法说明
public ArrayList(Collection<? extends E> c)将任意单例集合中的元素添加到新的ArrayList集合中
public LinkedList(Collection<? extends E> c)将任意单例集合中的元素添加到新的LinkedList集合中
public HashSet(Collection<? extends E> c)将任意单例集合中的元素添加到新的HashSet集合中
public LinkedHashSet(Collection<? extends E> c)将任意单例集合中的元素添加到新的LinkedHashSet集合中
public TreeSet(Collection<? extends E> c)将任意单例集合中的元素添加到新的TreeSet集合中
请添加图片描述

4. 可变参数

  • 格式: 是一种特殊的形参,定义在方法的形参列表: 数据类型… 变量名
public void test(数据类型... 变量名){
}
public void test(int... nums){
}
  • 特点: 调用方法时
    • 可以不传参
    • 可以传一个或多个参数
    • 也可以传一个数组
  • 好处: 可以灵活的接收数据
  • 本质: 在方法的内部,本质就是一个数组
  • 注意事项
    • 方法的形参中,只能有一个可变参数
    • 如果有多个参数,可变参数必须放在最后
public static void main(String[] args) {
    test(); // 不传参
    test(10); // 传1个参数
    test(10,20,30); // 传多个参数
    test(new int[]{100,200,300}); // 传一个数组
}

//    public static void test(int... nums,String... strings){ // 只能有一个可变参数
//    public static void test(int a, int... nums){ // 如果参数有多个,可变参数只能写在最后
public static void test(int... nums){
    // 方法内部 可变参数本质是一个数组
    System.out.println(nums.length);
    System.out.println(Arrays.toString(nums));
    System.out.println("----------------");
}

5. Collections工具类

  • 常见方法
方法名说明
public static boolean addAll(Collection<? super T> c, T… elements)给集合批量添加元素
public static void shuffle(List<?> list)打乱List集合中的元素顺序
public static void reverse(List<?> list)List集合数据反转
public static void swap(List<?> list, int i, int j)List集合指定索引位置交换数据
public static boolean replaceAll(List list, T oldVal, T newVal)List集合数据替换
public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    Collections.addAll(list,1,2,3,4,5,6);
    System.out.println(list);

    Collections.shuffle(list);
    System.out.println(list);

    Collections.reverse(list);
    System.out.println(list);

    Collections.swap(list,1,2);
    System.out.println(list);


    Collections.replaceAll(list,1,100);
    System.out.println(list);

}

练习

  • 模拟斗地主游戏: 做牌,洗牌,发牌,看牌
  • 分析: 面向对象
    • 房间
      • 房间中有一副扑克牌(54张牌)
        • 扑克牌有数值 3 4 5 6 7 8 9 10 J Q K A 2
        • 扑克牌有花色 “♠”, “♥”, “♣”, “♦” “大王”,“小王”
        • 扑克牌有大小
      • 房间中有3个玩家(每人17张牌)
      • 发牌时要留3张底牌
package com.itheima.test;

/**
 * @Auther: Yishooo
 * @Date: 2023-4-4 - 04 - 04 - 15:50
 * @Description: 表示一张牌
 */

public class Card implements Comparable<Card>{
    private String number;//数值
    private String color;//花色
    private int size;//3 4 5 6 (0 1 2 3)

    public Card() {
    }

    public Card(String number, String color, int size) {
        this.number = number;
        this.color = color;
        this.size = size;
    }

    @Override
    public String toString() {
        return color+number;//+"->"+size
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }


    @Override
    public int compareTo(Card o) {
        return this.size-o.size;
    }
}
package com.itheima.test;

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

/**
 * @Auther: Yishooo
 * @Date: 2023-4-4 - 04 - 04 - 15:52
 * @Description: 房间
 */
public class Room {
    //一副牌  集合
    private List<Card> list = new ArrayList<>();//创建长度为0的集合  准备记录54张牌
    //三个玩家(三个集合)
    private List<Card> player1 = new ArrayList<>();// 17张牌的一位玩家
    private List<Card> player2 = new ArrayList<>();// 17张牌的一位玩家
    private List<Card> player3 = new ArrayList<>();// 17张牌的一位玩家

    public Room() {
        //list中装54张牌
        init();
        System.out.println("洗牌前:"+list);
    }
    //1.做牌
    /* 初始化集合 用于做54张牌:将54个Card存入到集合中  */
    public void init(){
        String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
        String[] colors = {"♠", "♥", "♣", "♦"};
        for (int i = 0; i < numbers.length; i++) {
            for (int j = 0; j < colors.length; j++) {
                Card c = new Card(numbers[i],colors[j],i);//将索引当作牌的大小
                list.add(c);
            }
        }
        //单独存储大王小王
        Collections.addAll(list,new Card("","小王",numbers.length),new Card("","大王",numbers.length+1));
    }

    /* 开始游戏 */
    public void start(){
        //2.洗牌
        Collections.shuffle(list);
        System.out.println("洗牌后:"+list);
        //3.发牌:每次每人一张牌:剩3张底牌

        // i % 3 = 0 1 2
        for (int i = 0; i < list.size()-3; i++) {
            int num = i % 3;
            switch (num){
                case 0://发给第一个玩家
                    player1.add(list.get(i));
                    break;
                case 1://发给第二个玩家
                    player2.add(list.get(i));
                    break;
                case 2://发给第三个玩家
                    player3.add(list.get(i));
                    break;
            }
        }
        //按照牌的大小排序
        sort();

        //4.玩家看牌
        System.out.println("玩家一:"+player1);
        System.out.println("玩家二:"+player2);
        System.out.println("玩家三:"+player3);
    }

    public void sort(){
        //对三个玩家的集合排序
        player1.sort((o1, o2) -> o1.getSize()-o2.getSize());
        player2.sort((o1, o2) -> o1.getSize()-o2.getSize());
        player3.sort((o1, o2) -> o1.getSize()-o2.getSize());
    }
}
package com.itheima.test;

/**
 * @Auther: Yishooo
 * @Date: 2023-4-4 - 04 - 04 - 15:49
 * @Description:
 */
/*
    - 模拟斗地主游戏:  做牌,洗牌,发牌,看牌
    - 分析: 面向对象
      - 房间
        - 房间中有一副扑克牌(54张牌)
          - 扑克牌有数值 3 4 5 6 7 8 9 10 J Q K A 2
          - 扑克牌有花色 "♠", "♥", "♣", "♦" "大王","小王"
          - 扑克牌有大小
        - 房间中有3个玩家(每人17张牌)
        - 发牌时要留3张底牌
 */
public class Demo {
    public static void main(String[] args) {
        Room room = new Room();
        room.start();
    }
}

重点内容

  • 掌握单列集合每种实现类的特点,使用
  • 知道每种实现类底层原理(数据结构)
  • 掌握可变参数的使用

预习内容

  • Map双列集合
  • Stream-一套API
  • 方法引用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值