Java基础-集合_上

news2024/11/18 22:39:25

文章目录

    • 1.基本介绍
    • 2.集合的框架体系(单列、双列)
        • 单列集合
        • 双列集合
        • 比较
    • 3.Collection接口和常用方法
        • 1.Collection接口实现类的特点
        • 2.常用方法(使用ArrayList演示)
          • 代码
          • 结果
        • 3.迭代器遍历
          • 基本介绍
          • 代码
          • 结果
        • 4.增强for循环遍历
          • 代码
          • 结果
        • 5.对于方法返回的理解
          • 返回类型是基本数据类型
          • 返回类型是引用类型
        • 6.课堂测试
          • 题目
          • 代码
          • 答案
    • 4.List接口和常用方法
        • 基本介绍
        • 1.常用方法
          • 代码
          • 结果
        • 2.课堂练习
          • 题目
          • 代码
          • 结果
        • 3.List三种遍历方式
          • 代码
          • 结果
        • 4.List课堂练习
          • 题目
          • 代码
          • 结果
    • 5.ArrayList类
        • 1.注意事项
        • 2.ArrayList扩容机制
          • 结论
          • 解析源码(使用无参构造器来创建和使用ArrayList)
    • 6.Vector类
        • 1.注意事项
        • 2.Vector扩容机制
          • 结论
          • 解析源码
        • 3.ArrayList和Vector比较
    • 7.LinkedList类
        • 1.LinkedList简要说明
        • 2.LinkedList底层操作机制
        • 3.LinkedList底层源码
          • add方法底层源码
        • 4.ArrayList和LinkedList比较
    • 8.Set接口
        • 1.基本介绍
        • 2.常用方法
          • 代码
          • 结果
        • 3.全面介绍
          • 代码实例
          • 结果
        • 4.HashSet底层机制
          • 结论
          • HashSet添加机制
          • 练习题目1
          • **答案**
          • 思考题
          • 答案
    • 9.LinkedHashSet类
        • 1.基本介绍
        • 2.LinkedHashSet底层机制
        • 3.练习
          • 题目
          • 代码
          • 结果

1.基本介绍

image-20240103140103299

2.集合的框架体系(单列、双列)

单列集合

image-20240103141332875

双列集合

image-20240103141427070

比较

image-20240103141844038

3.Collection接口和常用方法

1.Collection接口实现类的特点
  1. Collection实现子类可以存放多个对象,如果是基本数据类型,则会自动装箱
  2. 有些Collection的实现类,可以存放重复的元素,有些不可以
  3. 有些Collection的实现类,有些是有序的(List),有些是无序的(Set)
  4. Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的
2.常用方法(使用ArrayList演示)
代码
package collection;

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

/**
 * @author 孙显圣
 * @version 1.0
 */
public class CollectionMethod {
    public static void main(String[] args) {

        List list = new ArrayList(); //这里使用List接收
        // add 添加单个元素
        list.add("jack");
        list.add(10); //这里会自动装箱,转换为Integer
        list.add(true);

        // remove 删除指定元素
        list.remove("jack"); //这里是删除指定对象,返回的是一个boolean类型
        list.remove(0); //这里是删除指定索引的内容,返回的是删除的那个元素的值
        System.out.println(list);
        list.add("jack");
        list.add(10); //这里会自动装箱,转换为Integer

        // contain 查找元素是否存在
        System.out.println(list.contains("jack"));

        // size 获取元素个数
        System.out.println(list.size());

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

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

        // addAll 添加多个元素,也就是添加一个集合
        ArrayList list1 = new ArrayList();
        list1.add("红楼梦");
        list1.add("三国演义");
        list1.add("水浒传");

        list.addAll(list1);
        System.out.println("添加后的集合:" + list);

        // removeAll 删除多个元素,也就是删除一个集合的所有元素
        ArrayList list2 = new ArrayList();
        list2.add("水浒传");
        list2.add("三国演义");
        list.removeAll(list2);

        System.out.println(list);

        // 查找多个元素是否都存在
        System.out.println(list.containsAll(list2));
    }
}

结果

image-20240103145922227

3.迭代器遍历
基本介绍

image-20240103152002846

  1. Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
  2. 所有实现了Collection接口的集合类都有一个iterator()方法,用于返回一个实现了Iterator接口的对象,即可以返回一个迭代器
  3. Iterator的结构image-20240103152331459
  4. Iterator仅用于集合遍历,Iterator本身不存放对象
代码
package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class CollectionIterator {
    public static void main(String[] args) {
        Collection col = new ArrayList();
        //添加三条数据
        col.add(new Book("三国演义", "罗贯中", 10.1));
        col.add(new Book("红楼梦", "曹雪芹", 10.2));
        col.add(new Book("西游记", "吴承恩", 10.3));
        //获取迭代器对象
        Iterator iterator = col.iterator();
        //使用itit快捷键
        while (iterator.hasNext()) {
            Object next =  iterator.next(); //这个next方法返回的是一个Object类型的引用,指向的是具体的对象
            System.out.println(next); //输出会默认调用toString方法,根据动态绑定,调用的是运行类型即Book类型的toString方法
        }
        //如果要重新遍历,需要重新获取迭代器,因为此时的迭代器已经指向最后一个元素了
        iterator.next(); //会报错

    }
}
class Book {
    private String name;
    private String author;
    private double price;

    public Book(String name, String author, double price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }

    public String getName() {
        return name;
    }

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

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        //输出详细信息
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}

结果

image-20240103154513139

4.增强for循环遍历
代码
package collection;

import java.util.ArrayList;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class CollectionFor {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        //添加三条数据
        list.add(new Book("三国演义", "罗贯中", 10.1));
        list.add(new Book("红楼梦", "曹雪芹", 10.2));
        list.add(new Book("西游记", "吴承恩", 10.3));
        //使用增强for循环遍历,快捷键是 I
        for (Object o : list) {
            System.out.println(o);
        }
        //底层还是调用的迭代器,可以理解为简化版本的迭代器

    }
}

结果

image-20240103155917072

5.对于方法返回的理解
返回类型是基本数据类型
public int add(int a, int b) {
    int sum = a + b;
    return sum;  // 这里返回的是sum的值
}
//此时如果调用这个方法,x.add(1,2)则这个东西就是sum的值
返回类型是引用类型
public class MyCustomClass {
    private int value;

    public MyCustomClass(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

class MyClass {
    public Object createCustomObject(int value) {
        MyCustomClass customObj = new MyCustomClass(value);
        return customObj;  // 返回的是MyCustomClass类型的对象引用
    }
}
//在这里如果调用这个方法, x.createCustomObject(3)则代表的是一个Object类型的引用,指向的是返回的customObj这个实例
6.课堂测试
题目

image-20240103160637444

代码
package collection;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class CollectionExercise {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        //添加三条数据
        list.add(new Dog("小黑", 3));
        list.add(new Dog("小白", 4));
        list.add(new Dog("小黄", 5));

        //迭代器遍历
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println(next);
        }

        //增强for遍历
        for (Object o : list) {
            System.out.println(o);
        }

    }
}
class Dog {
    private String name;
    private int age;

    public Dog(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() {
        //输出格式为:名字:xxx,年龄:xxx
        return "名字:" + name + ",年龄:" + age;
    }
}
答案

image-20240103161230524

4.List接口和常用方法

基本介绍
  1. List接口是Collection接口的子接口
  2. List集合类中元素有序,并且可以重复
  3. List集合中的每个元素都有其对应的顺序索引,即支持索引
1.常用方法
代码
package collection;

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

/**
 * @author 孙显圣
 * @version 1.0
 */
public class ListMethod {
    public static void main(String[] args) {
        List list = new ArrayList();

        // add 添加元素
        list.add("张三丰");
        list.add("贾宝玉");

        // void add(int index, Object ele) 指定位置添加元素
        list.add(1,"韩顺平");
        System.out.println(list);

        // boolean addAll(int index, Collection eles) 指定位置添加多个元素
        ArrayList list1 = new ArrayList();
        list1.add("jack");
        list1.add("tom");
        list.addAll(1,list1);
        System.out.println(list);

        // Object get(int index) 获取指定位置的元素
        System.out.println(list.get(1));

        // int indexOf(Object obj) 返回obj在集合中首次出现的位置
        System.out.println(list.indexOf("tom"));

        // int lastIndexOf(Object obj) 返回obj在当前集合中末次出现的位置
        list.add("tom");
        System.out.println(list.lastIndexOf("tom"));

        // Object remove(int index) 移除指定index位置的元素,并返回此元素
        list.remove(0);
        System.out.println(list);

        // Object set(int index, Object ele) 设置指定位置的元素,相当于替换
        list.set(2, "jack");
        System.out.println(list);

        //List subList(int fromIndex, int toIndex) 返回从fromIndex到toIndex - 1位置的子集合
        System.out.println(list.subList(0, 4));
    }
}

结果

image-20240103181906525

2.课堂练习
题目

image-20240103182013585

代码
package collection;

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

/**
 * @author 孙显圣
 * @version 1.0
 */
public class ListExercise {
    public static void main(String[] args) {
        List list = new ArrayList();
        //添加十个元素,添加字符串
        for (int i = 0; i < 10; i++) {
            list.add("str" + i);
        }
        list.add(1,"韩顺平教育");
        System.out.println(list.get(4));
        list.remove(5);
        list.set(6,"已修改");
        Iterator iterator = list.iterator(); //获取迭代器
        while (iterator.hasNext()) { //判断是否有下一个元素
            Object next = iterator.next(); //如果有下一个元素,则向下移动,并取出下一个元素
            System.out.print(next + " "); //这里调用了String类型的toString,打印字符串的内容
        }
    }
}

结果

image-20240103183437804

3.List三种遍历方式
代码
package collection;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class ListFor {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        //添加五条数据
        list.add(new Book("三国演义", "罗贯中", 10.1));
        list.add(new Book("红楼梦", "曹雪芹", 10.2));
        list.add(new Book("西游记", "吴承恩", 10.3));
        list.add(new Book("水浒传", "施耐庵", 10.4));
        list.add(new Book("三国演义", "罗贯中", 10.1));
        //1.迭代器遍历
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println(next);
        }
        //2.增强for
        for (Object o : list) {
            System.out.println(o);
        }
        //3.普通for
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

    }
}

结果

image-20240103184946954

4.List课堂练习
题目

image-20240103195814174

代码
package collection;

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

/**
 * @author 孙显圣
 * @version 1.0
 */
public class ListExercise02 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(new Book("三国演义", "罗贯中", 12.1));
        list.add(new Book("红楼梦", "曹雪芹", 10.2));
        list.add(new Book("西游记", "吴承恩", 11.3));
        sort(list);
        System.out.println(list);
    }
    public static void sort(List list) {
        for (int i = list.size() - 1; i > 0 ; i--) {
            for (int j = 1; j <= i ; j++) {
                Book o1 = (Book) list.get(j); // o1指向的是j位置所指向的Book对象
                Book o2 = (Book) list.get(j - 1);  // o2指向的是j - 1位置所指向的Book对象
                if (o1.getPrice() < o2.getPrice()) { //交换
                    list.set(j, o2); //这样j位置存储的就是原来j - 1位置存储的Book对象
                    list.set(j - 1, o1); //j - 1位置存储的就是原来j位置存储的book对象
                }
            }
        }

    }
}

image-20240103195801818

结果

image-20240103200746188

5.ArrayList类

1.注意事项
  1. ArrayList可以存储任何类型的数据,还可以存储多个null
  2. ArrayList是由数组来实现数据存储的
  3. ArrayList基本等同于Vector,除了ArrayList是线程不安全的,多线程不建议使用ArrayList
2.ArrayList扩容机制
结论
  1. ArrayList中维护了一个Object类型的数组elementData. transient Object[] elementData,这里的transient表示该属性不会被序列化
  2. 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需再次扩容则扩容elementData为1.5倍
  3. 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍
解析源码(使用无参构造器来创建和使用ArrayList)
  1. 打断点,debug,跳入image-20240104095220294
  2. 下一步,可以看出,创建了一个空的elementDataimage-20240104095249194
  3. 跳出,下一步,再下一步,到了add方法image-20240104095518100
  4. 跳入,进入了Integer将1进行装箱image-20240104095644352
  5. 跳出,再跳入,进入add方法,先确认是否要扩容,然后再执行赋值操作image-20240104095814998
  6. 跳入,可以发现里面调用了calculateCapacity函数,这个函数返回最小容量,如果是第一次,即elementData是空的则返回10和minCapacity = 1的最大值,也就是10image-20240104101051066
  7. 跳入两次image-20240104101341673
  8. 下一步,如果最小容量大于目前长度,则需要扩容了image-20240104101524881
  9. 下一步,跳入,可以看出是先将当前的长度赋值给旧的容量image-20240104101626826
  10. 下一步,新的容量扩容到了旧容量的1.5倍,但是注意,第一次的旧容量是0,所以1.5倍还是0image-20240104101955864
  11. 下一步,进入判断语句,发现新容量(0)还是小于最小容量(10),再下一步,新容量变成了最小容量10image-20240104102327245
  12. 再下两步,因为不满足第二个判断条件,这次将elementData扩容到新容量的大小,后面没有值的会是空image-20240104102541758
  13. 下一步,查看数组,是十个空的image-20240104102642781
  14. 一直跳出,直到返回到add,此时的数组已经有容量了image-20240104102827907
  15. 下一步,发现这个数组已经有内容了image-20240104103027663
  16. 第二次扩容,直接在第二个循环打断点image-20240104103355304
  17. 下一步image-20240104103423391
  18. 跳入,跳出,再跳入(中间的步骤是用来装箱的)image-20240104103601899
  19. 跳入,此时调用calculateCapacity这个方法,直接返回最小容量11image-20240104103657413
  20. 跳入两次,进入ensureExplicitCapacityimage-20240104103842061
  21. 下一步,发现最小容量大于目前数组的长度,所以需要扩容image-20240104103904845
  22. 下一步,到grow函数,再跳入,开始进行数组扩容image-20240104104018178
  23. 下两步,发现新数组容量15大于最小数组容量11image-20240104104215584
  24. 下两步,到了数据扩容部分,再下一步数组扩容完毕,变成15个位置image-20240104104422358
  25. 一直跳出到add函数,此时数组已经有位置了image-20240104104444094
  26. 下一步,则会将元素赋值给数组的第11个位置,剩下的为nullimage-20240104104621295

6.Vector类

1.注意事项
  1. Vector类的定义image-20240104110549493
  2. Vector底层也是一个对象数组,protected Object[] elementData
  3. Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized
  4. 在开发中,需要线程同步安全时,考虑使用Vector
2.Vector扩容机制
结论
  1. 如果是无参,默认10,满了之后就按两倍扩容
  2. 如果是指定大小则每次按照两倍扩容
解析源码
  1. 打断点image-20240104141155772
  2. 两次跳入,发现无参构造方法,调用了有参的构造方法,并将值设为10,此时elementData的长度就被设置为10image-20240104141254055
  3. 一直下一步,直到进行到add方法image-20240104141337024
  4. 跳入,跳出然后再跳入(这一步是进行的自动装箱),然后下一步,这里是将元素数量加一并作为参数image-20240104141445044
  5. 跳入,发现最小容量(1)小于elementData的长度,所以不用扩容image-20240104141555071
  6. 两次下一步,此时如果再执行就会为elementData数组赋值了image-20240104141852927
  7. 一直下一步,直到执行到这里image-20240104142037981
  8. 跳入,跳出,再跳入image-20240104142103216
  9. 下一步,然后跳入这个方法,现在最小容量是大于这个数组的容量的,所以需要扩容image-20240104142211473
  10. 下一步,然后跳入,进入grow函数。这句话是将数组长度赋值给旧的容量image-20240104142243567
  11. 下一步,这句话是将新的容量设置为旧的容量的两倍image-20240104142356211
  12. 一直下一步,直到这里进行了数组扩容,此时的数组长度是20image-20240104142604994
  13. 一直下一步到add,这句话就会将元素添加给给这个数组image-20240104142650525
  14. 下一步,再查看数组容量就是20了image-20240104142836608
3.ArrayList和Vector比较

image-20240104155418266

7.LinkedList类

1.LinkedList简要说明

image-20240104155156821

2.LinkedList底层操作机制
  1. LinkedList底层维护了一个双向链表
  2. LinkedList中维护了两个属性,first和last分别指向首节点和尾节点
  3. 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个节点,通过next指向后一个节点,item用来存储数据,最终实现双向链表
  4. 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高
3.LinkedList底层源码
add方法底层源码
  1. 打断点image-20240104152516099
  2. 跳入,发现就是调用了一个LinkedList的无参构造方法,然后下一步,使用size记录节点数量0,first指向null,last指向nullimage-20240104152827503
  3. 下一步,到了构造方法的最后,因为刚才是完成了普通初始化,接下来才是构造方法image-20240104153225583
  4. 下一步,会回到创建对象的语句,一直下一步,直到add方法image-20240104153317026
  5. 跳入,跳出,然后再跳入(这里进行了自动装箱),则进入了add方法image-20240104153407628
  6. 跳入,此时的l指向last,也就是nullimage-20240104153503534
  7. 下一步创建一个节点,prev赋值为last即是null,item赋值为e(要添加的数据0),next赋值为nullimage-20240104153807420
  8. 下一步,last指向新节点image-20240104153848836
  9. 下一步,l是null的,所以first也指向新节点image-20240104153943450
  10. 下一步,size++image-20240104154005006
  11. 一直下一步到第二个addimage-20240104154046108
  12. 跳入,跳出,然后再跳入,到了add方法image-20240104154119318
  13. 跳入,l指向last就是刚才的节点,下一步,创建新节点,prev指向刚才的节点,item赋值为e(要添加的数据),next赋值为nullimage-20240104154422102
  14. 下一步,last指向新节点,first不变image-20240104154552886
  15. 下一步,由于l不是null,所以l.next指向新的节点,也就是将刚才的节点的next指向新节点image-20240104154716505
  16. 下一步,size ++image-20240104154738555
  17. 一直下一步到第三个add执行之前,发现已经添加进数据1了image-20240104154853338
4.ArrayList和LinkedList比较

image-20240104155245349

8.Set接口

1.基本介绍
  1. 无序(添加和取出的顺序不一致),没有索引
  2. 虽然取出顺序不一致,但是一旦第一次以一种顺序取出,第二次取出还是那个顺序
  3. 不允许重复元素,所以最多包含一个null
  4. 遍历方式不可以使用普通循环(因为没有索引),只能使用迭代器和增强for循环
2.常用方法
代码
package collection;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class SetMethod {
    public static void main(String[] args) {
        Set hashSet = new HashSet();

        //添加五条数据
        hashSet.add("cat");
        hashSet.add("dog");
        hashSet.add("pig");
        hashSet.add("mouse");
        hashSet.add("cat");

        //1.迭代器遍历
        Iterator iterator = hashSet.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println(next);
        }

        //增强for遍历
        for (Object o : hashSet) {
            System.out.println(o);
        }

    }
}

结果

image-20240104161519782

3.全面介绍
  1. HashSet实现了Set接口
  2. HashSet的构造器调用了HashMap
  3. 可以存放null值,但是只能有一个null值
  4. HashSet不保证元素是有序的
  5. 不能有重复元素/对象
代码实例
package collection;

import java.util.HashSet;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class HashSet01 {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();

        //添加五条数据
        //在添加数据的时候会赶回true或者false
        System.out.println(hashSet.add("cat"));
        System.out.println(hashSet.add("dog"));
        System.out.println(hashSet.add("pig"));
        System.out.println(hashSet.add("mouse"));
        System.out.println(hashSet.add("cat")); //F

        //删除数据
        hashSet.remove("pig");
        System.out.println(hashSet);

        HashSet set = new HashSet();
        set.add("luck");
        set.add("luck"); //F
        set.add(new Dog("tom",3));
        set.add(new Dog("tom",3)); //这是两个自定义对象,是不同的所以添加成功
        System.out.println("set = " + set);

        System.out.println(set.add(new String("lihua")));
        System.out.println(set.add(new String("lihua"))); //F,因为数据都是常量池的那一个,所以添加失败

    }
}

结果

image-20240104164004548

4.HashSet底层机制

image-20240104181357816

结论

image-20240104183742217

HashSet添加机制
  1. 首先调用一个实例的hashcode方法,根据某种算法计算出一个hash值(但是如果两个实例的hashcode值是相同的则计算出来的值也一定相同),就意味着他们一定会在同一行
  2. 根据这个值来计算索引值
  3. 如果索引的位置为空则直接添加,如果不是空的,则依次与这行的每个数据调用equals方法来比较是否相同,如果相同则添加失败
练习题目1

image-20240104201709850

答案
package collection;

import java.util.HashSet;
import java.util.Objects;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class HashSetExercise {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        //创建两个name和age相同的Employee实例
        Employee employee1 = new Employee("jack", 200);
        Employee employee2 = new Employee("jack", 200);
        //添加进HashSet,并输出true或者false
        System.out.println(hashSet.add(employee1));
        System.out.println(hashSet.add(employee2));
    }
}
class Employee {
    private String name;
    private int age;

    public Employee(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 int hashCode() {
        return Objects.hash(name,age); //这两个只要数值相同,返回的hashcode就相同
    }

    //比较两个实例的name和age值是否相同
    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof Employee)) {
            return false;
        }
        //此时这个obj一定指向Employee
        Employee employee = (Employee) obj;
        if (this.name.equals(employee.name) && employee.age == this.age) {
            return true;
        }
        else {
            return false;
        }

    }
}

image-20240104201903379

思考题

image-20240104211159590

答案
package collection;

import java.util.HashSet;
import java.util.Objects;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class HashSetExercise02 {
    public static void main(String[] args) {
        MyDate myDate1 = new MyDate("2002", "12", "08");
        MyDate myDate2 = new MyDate("2002", "12", "08");
        Employee_ employee1 = new Employee_("孙先生", 200, myDate1);
        Employee_ employee2 = new Employee_("孙先生", 300, myDate1);
        HashSet hashSet = new HashSet();
        System.out.println(hashSet.add(employee1));
        System.out.println(hashSet.add(employee2));
    }
}

class Employee_ {
    private String name;
    private double sal;
    private MyDate birthday;

    public Employee_(String name, double sal, MyDate birthday) {
        this.name = name;
        this.sal = sal;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

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

    public double getSal() {
        return sal;
    }

    public void setSal(double sal) {
        this.sal = sal;
    }

    public MyDate getBirthday() {
        return birthday;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }

    //重写hashcode方法,使得只要两个实例的name和birthday的hashcode相同则返回相同的hashcode
    //对于name来说,由于是基本数据类型,只要值相同,则hashcode相同
    //但是对于birthday来说,需要重写他的hashcode方法,使其在属性相同的前提下,hashcode也相同
    @Override
    public int hashCode() {
        return Objects.hash(name,birthday);
    }

    //现在只要name和birthday相同就可以使他们在同一行了
    //重写equals方法用来比较两个实例的name和birthday是否相同,如果相同则不可以添加到HashSet中
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Employee_)) {
            return false;
        }
        if (((Employee_) obj).name.equals(this.name) && birthday.equals(((Employee_) obj).birthday)) {
            return true;
        }
        else {
            return false;
        }

    }
}

class MyDate {
    private String year;
    private String month;
    private String day;

    public MyDate(String year, String month, String day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public String getYear() {
        return year;
    }

    public void setYear(String year) {
        this.year = year;
    }

    public String getMonth() {
        return month;
    }

    public void setMonth(String month) {
        this.month = month;
    }

    public String getDay() {
        return day;
    }

    public void setDay(String day) {
        this.day = day;
    }

    //这样会使这个类型的实例只要属性相同,返回的hashcode值是相同的
    @Override
    public int hashCode() {
        return Objects.hash(year,month,day);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof MyDate)) {
            return false;
        }
        MyDate myDate = (MyDate) obj;
        if (myDate.year.equals(this.year) && myDate.month.equals(this.month) && myDate.day.equals(this.day)) {
            return true;
        }
        else {
            return false;
        }
    }
}

image-20240104211230617

9.LinkedHashSet类

1.基本介绍
  1. LinkedHashSet是HashSet的子类
  2. LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表
  3. LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用双向链表维护元素的次序,使得看起来是以插入顺序保存的
  4. LinkedHashSet不允许添加重复元素
2.LinkedHashSet底层机制

image-20240105090730465

LinkedHashSet的添加机制跟HashSet是相同的,先根据hashcode计算索引值,如果在同一行就依次调用equals比较大小

3.练习
题目

image-20240105093814557

代码
package collection;

import java.util.LinkedHashSet;
import java.util.Objects;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class LinkedHashSetExercise {
    public static void main(String[] args) {
        LinkedHashSet linkedHashSet = new LinkedHashSet();
        Car bc = new Car("奔驰", 100000);
        Car bc_ = new Car("奔驰", 100000);
        linkedHashSet.add(bc);
        linkedHashSet.add(bc_);
        System.out.println(linkedHashSet);

    }
}
class Car {
    private String name;
    private double price;

    public Car(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

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

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    //当两个实例的属性相同,则返回true
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return Double.compare(car.price, price) == 0 && Objects.equals(name, car.name);
    }

    //当两个实例的属性相同,则返回的hashcode相同
    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }
}

结果

image-20240105093910562

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1521485.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

滑动窗口和螺旋矩阵

209. 长度最小的子数组 题目 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 连续 子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度**。**如果不存在符合条件的子数组&#xff0c;返回…

十八、多线程JUC

目录 一、什么是多线程二、多线程的两个概念&#xff08;并发和并行&#xff09;三、多线程的实现方式3.1 继承Thread类的方式进行实现3.2 实现Runnable接口的方式进行实现3.3 利用Callable接口和Future接口方式实现 四、常见的成员方法五、线程的生命周期六、线程安全的问题七…

Nacos安装与集群搭建

Nacos安装与集群搭建 Nacos安装指南1.Windows安装1.1.下载安装包1.2.解压1.3.端口配置1.4.启动1.5.访问 2.Linux安装2.1.安装JDK2.2.上传安装包2.3.解压2.4.端口配置2.5.启动 3.Nacos的依赖Nacos集群搭建1.集群结构图2.搭建集群2.1.初始化数据库2.2.配置Nacos2.3.启动2.4.nginx…

JavaScript进阶:js的一些学习笔记-this指向,call,apply,bind,防抖,节流

文章目录 1. this指向1. 箭头函数 this的指向 2. 改变this的指向1. call()2. apply()3. bind() 3. 防抖和节流1. 防抖2. 节流 1. this指向 1. 箭头函数 this的指向 箭头函数默认帮我们绑定外层this的值&#xff0c;所以在箭头函数中this的值和外层的this是一样的箭头函数中的…

Linux——线程池

线程池的概念 线程池也是一种池化技术&#xff0c;可以预先申请一批线程&#xff0c;当我们后续有任务的时候就可以直接用&#xff0c;这本质上是一种空间换时间的策略。 如果有任务来的时候再创建线程&#xff0c;那成本又要提高&#xff0c;又要初始化&#xff0c;又要创建数…

揭示数据在内存中存储的秘密!

** ** 悟已往之不谏&#xff0c;知来者犹可追 ** ** 创作不易&#xff0c;宝子们&#xff01;如果这篇文章对你们有帮助的话&#xff0c;别忘了给个免费的赞哟~ 整数在内存中的存储 整数的表达方式有三种&#xff1a;原码、反码、补码。 三种表示方法均有符号位和数值位两部分…

【Java】List, Set, Queue, Map 区别?

目录 List, Set, Queue, Map 区别&#xff1f; Collection和Collections List ArrayList 和 Array区别&#xff1f; ArrayList与LinkedList区别? ArrayList 能添加null吗&#xff1f; ArrayList 插入和删除时间复杂度&#xff1f; LinkedList 插入和删除时间复杂度&…

51单片机基础篇系列-定时/计数器的控制工作方式

&#x1f308;个人主页&#xff1a;会编程的果子君 &#x1f4ab;个人格言:“成为自己未来的主人~” 定时/计数器的控制 80C51单片机定时/计数器的工作由两个特殊功能寄存器控制&#xff0c;TMOD用于设置其工作方式&#xff1a; 1.工作方式寄存器TMOD 工作方式寄存器TMO…

IO流(主要是记住四大类InputStream,OutputStream、Reader和Writer,其他都是他们的子类)

IO流 1、文件 &#xff08;1&#xff09;文件概念 文件就是保存数据的地方。例如word文档&#xff0c;txt文件&#xff0c;execl文件等等。 &#xff08;2&#xff09;文件流 文件在程序中是以流的形式来操作的。 流&#xff1a;数据在数据源&#xff08;文件&#xff09;…

【MySQL基础】MySQL基础操作三

文章目录 &#x1f349;1.联合查询&#x1f95d;笛卡尔积 &#x1f349;2.内连接&#x1f95d;查询单个数据&#x1f95d;查询多个数据 &#x1f349;3.外连接&#x1f349;4.自连接&#x1f349;5.合并查询 &#x1f349;1.联合查询 &#x1f95d;笛卡尔积 实际开发中往往数…

docker 、postgres 数据库常用命令大全,持续更新

使用postgres 数据库&#xff0c;经常会忘记命令。今天抽时间整理一份常用命令列表&#xff0c;附带实践 使用docker 执行命令,导出数据库 docker exec -i postgres-container pd_dump -U postgres -d zxapp > /opt/zxapp.sql或者进入交互式界面&#xff1a; docker exec -i…

算力,承载AI无限可能

年后不久&#xff0c;美国人工智能研究公司OpenAI推出的Sora模型引起了广泛关注&#xff0c;对于人工智能发展之快的感慨还未过去&#xff0c;3月初&#xff0c;Anthropic丢出一颗“王炸”Claude3&#xff0c;其中Claude 3 Opus宣称在基准测试中优于OpenAI的GPT-4和Google的Gem…

vue3模块化引用组件和引用ts,调用ts中的接口

以简单的登录功能为例子 1.在util中创建loginValidators.ts import { ref, reactive } from vueinterface User{email: string;password: string; }export const loginUserreactive<User>({email: ,password: })interface Rules{email: {required: boolean;message: …

【MySQL】3. 库的操作

库的操作 1. 创建数据库 语法&#xff1a; CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [,create_specification] ...]create_specification:[DEFAULT] CHARACTER SET charset_name[DEFAULT] COLLATE collation_name说明&#xff1a; 大写的表示关键字 …

时间日期格式化

在创建的项目中&#xff0c;往往涉及到时间日期的参数&#xff0c;都不是显示正常&#xff08;中国&#xff09;时区&#xff0c;至于是那儿的时区小编也不知道&#xff01;但是&#xff0c;我们可以自定义返回的时间日期格式&#xff01; public class DataUtils {/*** 2021-0…

强化学习------DDPG算法(附pytorch代码)

目录 一、前言二、基本原理2.1、经验回放2.2、更新过程2.2.1、Critic网络更新过程2.2.2、Actor网络更新过程2.2.3、 目标网络的更新 2.3、噪音探索 三、算法代码实现四、训练示例4.1、实现效果 一、前言 Deep Deterministic Policy Gradient (DDPG)算法是DeepMind团队提出的一…

Guitar Pro2024中文免费版吉他爱好者必备工具,学习演奏、绘谱创作全覆盖

Guitar Pro8是一款功能强大的吉他工具&#xff0c;它支持多种乐器&#xff0c;包括但不限于吉他、贝斯、钢琴和鼓。这意味着&#xff0c;无论是吉他手、贝斯手、钢琴家还是鼓手&#xff0c;都可以利用这款软件进行音乐创作和演奏。 在Guitar Pro8中&#xff0c;用户可以轻松选…

【论文阅读】Improved Denoising Diffusion Probabilistic Models

Improved Denoising Diffusion Probabilistic Models 文章目录 Improved Denoising Diffusion Probabilistic Models概述Improving the Log-likelihoodLearning ∑ θ ( x t , t ) \sum_{\theta}(x_{t}, t) ∑θ​(xt​,t)Improving the Noise ScheduleReducing Gradient Nois…

数字多空策略(实盘+回测+数据)

数量技术宅团队在CSDN学院推出了量化投资系列课程 欢迎有兴趣系统学习量化投资的同学&#xff0c;点击下方链接报名&#xff1a; 量化投资速成营&#xff08;入门课程&#xff09; Python股票量化投资 Python期货量化投资 Python数字货币量化投资 C语言CTP期货交易系统开…

2023年总结:聊一聊我这10年做过的副业

以下是我的2023年终总结&#xff0c;我从公众号同步到CSDN&#xff0c;大家可以看看我这10年的副业经验&#xff0c;希望对大家有所帮助。 今天是2023年最后一天&#xff0c;今年是不平凡的一年&#xff0c;也是变动最大的一年&#xff0c;大家也知道&#xff0c;嘟嘟最近离职了…