Java基础-集合_上

文章目录

    • 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【JAVA基础】算法与集合

1 查找 1.1 二分查找 public class Main {public static void main(String[] args) throws IOException, CloneNotSupportedException, ParseException { //数组必须有序int[] arr{1,2,4,5,6,24,123};System.out.println(binarySearch(arr,123));//6}public static int bina…

Docker Compose基本配置及使用笔记

Docker Compose基本配置及使用笔记 简介 Docker Compose 是一个用于定义和运行多个 Docker 容器应用程序的工具。它使用 YAML 文件来配置应用程序的服务&#xff0c;并通过简单的命令集管理这些服务的生命周期。 1.步骤1 代码如下&#xff1a;docker-compose.yml放在虚拟机roo…

vite打包时发布时,放在服务器的二级目录中

方式一 hash模式 如果我们的站点根目录为 public , 我们访问的时候使用的是 http://www.abc.com/ 访问到了站点的根目当&#xff0c;现在我们要访问 http://www.abc.com/mysite/#/ 配置如下 修改 vite.config.js base:“/mysite/” 修改 router中的配置 上面的步骤完成&…

【网站项目】320社区物业管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

C#创建第一个PIESDK模版的项目

目录 环境配置创建项目方式 环境配置 1软件安装 通过安装光盘或者U盘等介质读取PIE软件的安装程序和使用文档。程序安装过程比较简单&#xff0c;软件本身不借助与任何第三方程序&#xff0c;直接双击安装程序【PIESDK.Net_V6.3_Windows_X64.exe】安装文件&#xff0c;即可安装…

总要做一回书里的国风少女吧,女儿的新中式套装美出新高度了~

超有质感的新中式国风短袖 采用经典立领设计 活里内衬柔软舒适 搭配浅色系马面裙 如书中温婉气质的千金小姐~

HTML万字学习总结

html文本标签特殊符号图片音频与视频超链接表单列表表格语义标签(布局) html文本标签 标签简介<html></html>根目录<head></head>规定文档相关的配置信息&#xff08;元数据<body></body>元素表示文档的内容<meta></meta>表示…

Gatling压力测试Springboot项目

Gatling压力测试Springboot项目 一、指定Java Spring 项目作为测试项二、下载Gatling三、配置测试代码四、打开bin目录下的gatling.bat文件进行测试 一、指定Java Spring 项目作为测试项 这里给出一个简单的示例&#xff1a;代码链接 下载maven依赖以后在8080端口运行这个项目…

初识进程状态

&#x1f30e;进程状态【上】 文章目录&#xff1a; 进程状态 发现进程的状态 运行队列 进程排队 进程状态的表述       状态在代码中的表示       运行状态       阻塞状态       挂起状态 总结 前言&#xff1a; 为了搞明白正在运行的进程是什么意思…

计算机网络(7)----应用层

目录 一.应用层的基本概念 1.应用层的基本概述 2.网络应用模型 &#xff08;1&#xff09;客户/服务器模型 &#xff08;2&#xff09;P2P模型 二.应用程序相关 1.DNS系统 &#xff08;1&#xff09;域名与域名服务器 &#xff08;2&#xff09;域名解析过程&#xff…

Java Web项目—餐饮管理系统Day05-菜品管理

文章目录 1. 表结构与菜品展示页面2. 菜品的分类选择3. 图片的上传与下载4. 新增菜品5. 分页展示菜品6. 修改菜品6-1. 菜品信息回显6-1. 菜品信息更新 开始进行 Dish 菜品管理相关的开发. 该表包含一个图片字段, 需要上传图片以及图片回显的业务. 另外, 每个菜品可能包含多个口…

SpringBoot(异常处理)

SpringBoot&#xff08;异常处理&#xff09; 1.基本介绍 2.debug异常处理机制 1.找到 DefaultErrorViewResolver 2.下断点 3.debug启动&#xff0c;浏览器输出一个不存在的页面 4.第一次查找 error/404 1.查看目前要找的视图名 2.准备去查找资源 3.准备从四个默认存放静态资…

测试环境搭建整套大数据系统(十一:docker部署superset,无密码登录嵌入html,http改为https)

一&#xff1a;安装docker 参考文档 https://blog.csdn.net/weixin_43446246/article/details/136554243 二&#xff1a;安装superset 下载镜像。 拉取镜像&#xff08;docker pull amancevice/superset&#xff09; 查看镜像是否下载完成&#xff08;docker images&#xf…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:RichText)

富文本组件&#xff0c;解析并显示HTML格式文本。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。该组件无法根据内容自适应设置宽高属性&#xff0c;需要开发者设置显示布局。 子组件 不包含子组…

广度优先算法(一篇文章讲透)

目录 引言 一、算法概述 二、算法步骤 1 初始化 2 循环处理 三、算法应用 1 图的最短路径问题 2 网络爬虫 3 社交网络分析 4 游戏路径搜索 事例 四、算法特点与性能 五、性能优化 1 剪枝策略&#xff1a; 2 使用高效的数据结构&#xff1a; 3 并行化处理&#…

Machine Vision Technology:Lecture6 Blob detection斑点检测

Machine Vision Technology&#xff1a;Lecture6 Blob detection斑点检测 Blob detectionAchieving scale covarianceRecall&#xff1a;Edge detectionScale selectionBlob detection in 2DCharacteristic scale特征尺度Scale-space blob detectorEfficient implementation&am…

webconfig-boot项目说明

1、前言 最近利用空余时间写了一个项目webconfig-boot 。该项目主要配置了web项目常用的一些配置&#xff0c;如统一参数校验、统一异常捕获、统一日期的处理、常用过滤器、常用注解等。引入依赖接口完成常规的web配置。 这里也是总结了笔者在项目开发中遇到的一些常用的配置…

C语言葵花宝典之——文件操作

前言&#xff1a; 在之前的学习中&#xff0c;我们所写的C语言程序总是在运行结束之后&#xff0c;就会自动销毁&#xff0c;那如果我们想将一个结果进行长期存储应该如何操作呢&#xff1f;这时候就需要我们用文件来操作。 目录 1、什么是文件&#xff1f; 1.1 程序文件 1.2…

2024年AI辅助研发:科技创新的引擎

CSND - 个人主页&#xff1a;17_Kevin-CSDN博客 收录专栏&#xff1a;《人工智能》 技术进展 进入2024年&#xff0c;人工智能&#xff08;AI&#xff09;在科技界和工业界的焦点地位更加巩固&#xff0c;其在辅助研发领域的技术进步尤为显著。深度学习技术的突飞猛进使得数据分…

Window API 使用的一些注意事项

文章目录 1、LPCWSTR类型2、LPCTSTR类型3、LPCSTR类型4、LPCTSTR和LPCWSTR区别5、LPCTSTR和LPCSTR、LPCWSTR三者区别6、_T(" ")7、DWORD类型转换为std::wstring类型8、char类型转换为LPCSTR类型9、获取当前时间戳(毫秒)10、std::wstring和LPCSTR区别11、std::wstring…