Java的Set接口及其实现类(面试题)

news2024/11/24 19:33:44

Set接口中没有额外定义新的方法,使用的都是Collection接口中声明过的方法。


 * 1. Set接口的框架:
 *
 * |----Collection接口:单列集合,用来存储一个一个的对象
 *          |----Set接口:存储无序的、不可重复的数据   -->高中讲的“集合”
 *              |----HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null*                  |----LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
 *                 对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
 *              |----TreeSet:可以按照添加对象的指定属性,进行排序。
 *
 *
 *  1. Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。
 *
 *  2. 要求:向Set(主要指:HashSetLinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()equals()
 *     要求:重写的hashCode()equals()尽可能保持一致性:相等的对象必须具有相等的散列码。
 *      重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。


public class SetTest {
    /*
    一、Set:存储无序的、不可重复的数据
    以HashSet为例说明:
    1. 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。

    2. 不可重复性:保证添加的元素按照equals()判断时,不能返回true.返回true说明数据相等。即:相同的元素只能添加一个。
    
    */
    @Test
    public void test1(){
        Set set = new HashSet();
        set.add(456);
        set.add(123);
        set.add(123);
        set.add("AA");
        set.add("CC");
        set.add(new User("Tom",12));
        set.add(new User("Tom",12));//若User类中重写了hashCode()和equals()则插入失败。(不能有重复数据)
        set.add(129);

        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

HashSet

Set实现类之一: HashSet
@HashSet是Set接口的典型实现,大多数时候使用Set集合时都使用这个实现类。
●HashSet按Hash算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。
●HashSet具有以下特点:
➢不能保证元素的排列顺序
➢HashSet不是线程安全的
➢集合元素可以是null
●HashSet集合判断两个元素相等的标准:两个对象通过hashCode()方法比较相
等,并且两个对象的equals()方法返回值也相等。
●对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Objectobj)方法,以实现对象相等规则。即:‘
“相等的对象必须具有相等的散列码”。

添加元素的过程:以HashSet为例:

  • 我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
  • 此哈希值接着通过某种散列函数计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:
  • 如果此位置上没有其他元素,则元素a添加成功。 —>情况1
  • 如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
  • 如果hash值不相同,则元素a添加成功。—>情况2
  • 如果hash值相同,进而需要调用元素a所在类的equals()方法:
  • equals()返回true,元素a添加失败
  • equals()返回false,则元素a添加成功。—>情况3
对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
jdk 7 :元素a(新元素)放到数组中,指向原来的元素。
jdk 8 :原来的元素在数组中,指向元素a
总结:七上八下

HashSet底层:数组+链表的结构。
HashSet put操作存放数据的位置就是HashMapkey(key不重复),所有的key都指向一个value(static并且final)。

在这里插入图片描述
哈希值相同,值不同就会插进链表(红色)中。

LinkedHashSet

Set实现类之二: LinkedHashSet
●LinkedHashSet是HashSet的子类
●LinkedHashSet根据元素的hashCode值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,
这使得元素看起来是以插入顺序保存的。
●LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能。
●LinkedHashSet不允许集合元素重复。

   //LinkedHashSet的使用
    //LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。
    //优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
    @Test
    public void test2(){
        Set set = new LinkedHashSet();
        set.add(456);
        set.add(123);
        set.add(123);
        set.add("AA");
        set.add("CC");
        set.add(new User("Tom",12));
        set.add(new User("Tom",12));
        set.add(129);
   Iterator iterator = set.iterator();
    while(iterator.hasNext()){
        System.out.println(iterator.next());//输出的顺序和插入的顺序一样
	//        因为有两个指针记录了前后数据的地址
    }
}

在这里插入图片描述

TreeSet

Integer中方法compare和compareTo的区别

从名字上我们就可以大概才出来他们的区别,compareTo的参数是一个整数,是对象的方法.相对之下compare的参数应该就是两个,是类的方法.

再来看看源码

//compareTo
public int compareTo(Integer anotherInteger) {
    return compare(this.value, anotherInteger.value);
}

//compare
public static int compare(int x, int y) {
    return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
这就很清楚了,compare是静态的,可以通过类名直接调用.Integer.compare(a,b)
compareTo是非静态的,只能通过对象名.compareTo()来调用

Set实现类之三: TreeSet
●TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。
●TreeSet底层使用红黑树结构存储数据
●新增的方法如下: (了 解)
➢Comparator comparator()
➢Object first()
➢Object last()
➢Object lower(Object e)
➢Object higher(Object e)
➢SortedSet subSet(fromElement, toElement)
➢SortedSet headSet(toElement)
➢SortedSet tailSet(fromElement)
●TreeSet两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序。

public class TreeSetTest {

    /*
    1.向TreeSet中添加的数据,要求是相同类的对象。
    2.两种排序方式:自然排序(实现Comparable接口) 和 定制排序(Comparator)

    3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是equals().
    4.定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals().
     */
    @Test
    public void test1(){
        TreeSet set = new TreeSet();

        //失败:不能添加不同类的对象
//        set.add(123);
//        set.add(456);
//        set.add("AA");
//        set.add(new User("Tom",12));

            //举例一:
//        set.add(34);
//        set.add(-34);
//        set.add(43);
//        set.add(11);
//        set.add(8);
//		遍历输出顺序是从小到大
        //举例二:
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56)); //jack重名,插入失败 只有正确重写compareTo方法(排序年龄)才能插入

        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }

    }
    //按照姓名从大到小排列,年龄从小到大排列
    @Override
    public int compareTo(Object o) {
        if(o instanceof User){
            User user = (User)o;
//    return -this.name.compareTo(user.name); 加个负号就是从大到小 不加-就是从小到大
            int compare = -this.name.compareTo(user.name);
            if(compare != 0){
                return compare;
            }else{
                return Integer.compare(this.age,user.age);
            }//else compare为0说明姓名相同 再比较年龄
        }else{
            throw new RuntimeException("输入的类型不匹配");
        }

    }

在这里插入图片描述

    @Test
    public void test2(){
        Comparator com = new Comparator() {//Comparator是接口 这里用到了匿名内部类实现接口
            //按照年龄从小到大排列
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof User && o2 instanceof User){
                    User u1 = (User)o1;
                    User u2 = (User)o2;
                    return Integer.compare(u1.getAge(),u2.getAge());
                }else{
                    throw new RuntimeException("输入的数据类型不匹配");
                }
            }
        };

        TreeSet set = new TreeSet(com);
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Mary",33));
        set.add(new User("Jack",33));//重复年龄 插入失败
        set.add(new User("Jack",56));

每日一考(复习)

1.集合Collection中存储的如果是自定义类的对象,需要自定义类重写哪个方法?为什么?

equals()方法。  因为涉及这些操作:contains() /remove()/retainsAll().
Listequals()方法
Set(HashSetLinkedHashSet为例)equals()hashCode()
     (TreeSet为例):需要重写排序:ComparablecompareTo(Object obj)
						 Comparatorcompare(Object o1,Object o2)


2.List 接口的常用方法有哪些?(增、删、改、查、插、长度、遍历)
add(Object obj)
remove(Object obj)/remove(int index)
set(int index,Object obj)
get(int index)
add(int index,Object obj)
size() 
使用Iterator;foreach;普通的for		

3.Collection接口常用方法:
add(Object obj),
addAll(Collection coll),
size(),			isEmpty(),			clear();
contains(Object obj),
containsAll(Collection coll),
remove(Object obj),
removeAll(Collection coll),
retainsAll(Collection coll),
equals(Object obj);
hashCode(),  		toArray(),			iterator();
3.Collection集合与数组间的转换
//集合 --->数组:toArray()
Object[] arr = coll.toArray();
for(int i = 0;i < arr.length;i++){
    System.out.println(arr[i]);
}

//拓展:数组 --->集合:调用Arrays类的静态方法asList(T ... t)
List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(list);

List arr1 = Arrays.asList(new int[]{123, 456});
System.out.println(arr1.size());//1 整个数组被看成一个对象

List arr2 = Arrays.asList(new Integer[]{123, 456});
System.out.println(arr2.size());//2

4.使用Collection集合存储对象,要求对象所属的类满足:
向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().

list接口

1. 存储的数据特点:存储有序的、可重复的数据。

2. 常用方法:(记住)
增:add(Object obj)
删:remove(int index) / remove(Object obj)
改:set(int index, Object ele)
查:get(int index)
插:add(int index, Object ele)
长度:size()
遍历:① Iterator迭代器方式
     ② 增强for循环
     ③ 普通的循环


3. 常用实现类:
|----Collection接口:单列集合,用来存储一个一个的对象
*  |----List接口:存储序的、可重复的数据。  -->“动态”数组,替换原的数组
*      |----ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
*      |----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
*      |----Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储


4. 源码分析(难点)
4.1 ArrayList的源码分析:
*   2.1 jdk 7情况下
*      ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData
*      list.add(123);//elementData[0] = new Integer(123);
*      ...
*      list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。
*      默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
*
*      结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)
*
*   2.2 jdk 8ArrayList的变化:
*      ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没创建长度为10的数组
*
*      list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]
*      ...
*      后续的添加和扩容操作与jdk 7 无异。
*   2.3 小结:jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象
*            的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。
*

4.2 LinkedList的源码分析:
*      LinkedList list = new LinkedList(); 内部声明了Node类型的first和last属性,默认值为null
*      list.add(123);//将123封装到Node中,创建了Node对象。
*
*      其中,Node定义为:体现了LinkedList的双向链表的说法
*      private static class Node<E> {
            E item;
            Node<E> next;
            Node<E> prev;

            Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
            }
        }
4.3 Vector的源码分析:
jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
在扩容方面,默认扩容为原来的数组长度的2倍。

5. 存储的元素的要求:
添加的对象,所在的类要重写equals()方法
[面试题]
*  面试题:ArrayListLinkedListVector者的异同?
*  同:三个类都是实现了List接口,存储数据的特点相同:存储序的、可重复的数据
*  不同:见上(第3部分+4部分)

set接口:

1. 存储的数据特点:无序的、不可重复的元素
具体的:
以HashSet为例说明:
1. 无序性:不等于随机性。存储的数据在底层数组中并非照数组索引的顺序添加,而是根据数据的哈希值决定的。
2. 不可重复性:保证添加的元素照equals()判断时,不能返回true.即:相同的元素只能添加一个。


2. 元素添加过程:(HashSet为例)
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置,判断
数组此位置上是否已经元素:
    如果此位置上没其他元素,则元素a添加成功。 --->情况1
    如果此位置上其他元素b(或以链表形式存在的多个元素,则比较元素a与元素b的hash值:
        如果hash值不相同,则元素a添加成功。--->情况2
        如果hash值相同,进而需要调用元素a所在类的equals()方法:
               equals()返回true,元素a添加失败
               equals()返回false,则元素a添加成功。--->情况2

对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
jdk 7 :元素a放到数组中,指向原来的元素。
jdk 8 :原来的元素在数组中,指向元素a
总结:七上八下

HashSet底层:数组+链表的结构。(前提:jdk7)


3. 常用方法
Set接口中没额外定义新的方法,使用的都是Collection中声明过的方法。

4. 常用实现类:
|----Collection接口:单列集合,用来存储一个一个的对象
*          |----Set接口:存储无序的、不可重复的数据   -->高中讲的“集合”
*              |----HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null*                  |----LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
*                 在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。   对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
*              |----TreeSet:可以照添加对象的指定属性,进行排序。

5. 存储对象所在类的要求:
HashSet/LinkedHashSet:

要求:向Set(主要指:HashSetLinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()equals()
要求:重写的hashCode()equals()尽可能保持一致性:相等的对象必须具有相等的散列码
*    重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
*

TreeSet:
1.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是equals().
2.定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals().

6. TreeSet的使用
6.1 使用说明:
1.TreeSet中添加的数据,要求是相同类的对象。
2.两种排序方式:自然排序(实现Comparable接口 和 定制排序(Comparator6.2 常用的排序方式:
//方式一:自然排序
@Test
    public void test1(){
        TreeSet set = new TreeSet();

        //失败:不能添加不同类的对象
//        set.add(123);
//        set.add(456);
//        set.add("AA");
//        set.add(new User("Tom",12));

            //举例一:
//        set.add(34);
//        set.add(-34);
//        set.add(43);
//        set.add(11);
//        set.add(8);

        //举例二:
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56));


        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }

    }

//方式二:定制排序
    @Test
    public void test2(){
        Comparator com = new Comparator() {
            //照年龄从小到大排列
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof User && o2 instanceof User){
                    User u1 = (User)o1;
                    User u2 = (User)o2;
                    return Integer.compare(u1.getAge(),u2.getAge());
                }else{
                    throw new RuntimeException("输入的数据类型不匹配");
                }
            }
        };

        TreeSet set = new TreeSet(com);
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Mary",33));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56));


        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

TreeSet习题:


/**
 * 定义一个Employee类。
 该类包含:private成员变量name,age,birthday,其中 birthday 为 MyDate 类的对象;
 并为每一个属性定义 getter, setter 方法;
 并重写 toString 方法输出 name, age, birthday

 * @author shkstart
 * @create 2019 上午 10:22
 */
public class Employee implements Comparable{
    private String name;
    private int age;
    private MyDate birthday;

    public Employee(String name, int age, MyDate birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public Employee() {

    }

    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 MyDate getBirthday() {
        return birthday;
    }

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

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

    //按 name 排序
    @Override
    public int compareTo(Object o) {
        if(o instanceof Employee){
            Employee e = (Employee)o;
            return this.name.compareTo(e.name);
        }
//        return 0;
        throw new RuntimeException("传入的数据类型不一致!");
    }
}

/**
 * MyDate类包含:
 private成员变量year,month,day;并为每一个属性定义 getter, setter 方法;

 * @author shkstart
 * @create 2019 上午 10:21
 */
public class MyDate implements Comparable{
    private int year;
    private int month;
    private int day;

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

    public MyDate() {

    }

    public int getYear() {
        return year;
    }

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

    public int getMonth() {
        return month;
    }

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

    public int getDay() {
        return day;
    }

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

    @Override
    public String toString() {
        return "MyDate{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }


    @Override
    public int compareTo(Object o) {
        if(o instanceof MyDate){
            MyDate m = (MyDate)o;
            //比较年
            int minusYear = this.getYear() - m.getYear();
            if(minusYear != 0){
                return minusYear;
            }
            //比较月
            int minusMonth = this.getMonth() - m.getMonth();
            if(minusMonth != 0){
                return minusMonth;
            }
            //比较日
            return this.getDay() - m.getDay();
        }

        throw new RuntimeException("传入的数据类型不一致!");

    }
}

/**
 * 创建该类的 5 个对象,并把这些对象放入 TreeSet 集合中(下一章:TreeSet 需使用泛型来定义)
 分别按以下两种方式对集合中的元素进行排序,并遍历输出:

 1). 使Employee 实现 Comparable 接口,并按 name 排序
 2). 创建 TreeSet 时传入 Comparator对象,按生日日期的先后排序。

 *
 * @author shkstart
 * @create 2019 上午 10:23
 */
public class EmployeeTest {

    //问题二:按生日日期的先后排序。
    @Test
    public void test2(){


        TreeSet set = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof Employee && o2 instanceof Employee){
                    Employee e1 = (Employee)o1;
                    Employee e2 = (Employee)o2;

                    MyDate b1 = e1.getBirthday();
                    MyDate b2 = e2.getBirthday();
                    //方式一:
//                    //比较年
//                    int minusYear = b1.getYear() - b2.getYear();
//                    if(minusYear != 0){
//                        return minusYear;
//                    }
//                    //比较月
//                    int minusMonth = b1.getMonth() - b2.getMonth();
//                    if(minusMonth != 0){
//                        return minusMonth;
//                    }
//                    //比较日
//                    return b1.getDay() - b2.getDay();

                    //方式二:在MyDate类中重写compareTo()方法 然后调用
                    return b1.compareTo(b2);

                }
//                return 0;
                throw new RuntimeException("传入的数据类型不一致!");
            }
        });

        Employee e1 = new Employee("liudehua",55,new MyDate(1965,5,4));
        Employee e2 = new Employee("zhangxueyou",43,new MyDate(1987,5,4));
        Employee e3 = new Employee("guofucheng",44,new MyDate(1987,5,9));
        Employee e4 = new Employee("liming",51,new MyDate(1954,8,12));
        Employee e5 = new Employee("liangzhaowei",21,new MyDate(1978,12,4));

        set.add(e1);
        set.add(e2);
        set.add(e3);
        set.add(e4);
        set.add(e5);

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }


    //问题一:使用自然排序
    @Test
    public void test1(){
        TreeSet set = new TreeSet();

        Employee e1 = new Employee("liudehua",55,new MyDate(1965,5,4));
        Employee e2 = new Employee("zhangxueyou",43,new MyDate(1987,5,4));
        Employee e3 = new Employee("guofucheng",44,new MyDate(1987,5,9));
        Employee e4 = new Employee("liming",51,new MyDate(1954,8,12));
        Employee e5 = new Employee("liangzhaowei",21,new MyDate(1978,12,4));

        set.add(e1);
        set.add(e2);
        set.add(e3);
        set.add(e4);
        set.add(e5);

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

Set接口习题(面试题):

publicstaticList duplicateList(List list) {
    HashSet set= newHashSet();
    set.addAll(list);//利用set重复的数据插不进去这个特点
    return   newArrayList(set); 
}
public static void main(String[] args) {
    List list=newArrayList();
  list.add(newInteger(1));
    list.add(newInteger(2));
    list.add(newInteger(2));
    list.add(newInteger(4));
    list.add(newInteger(4));
    List list2= duplicateList(list);
    for(Object integer: list2) {
        System.out.println(integer); // 1,2,4
    }
//其中Person类中重写了hashCode()和equal()方法

HashSetset = new HashSet();
Person p1 = new Person(1001,"AA");
Person p2 = new Person(1002,"BB");

set.add(p1);
set.add(p2);
p1.name = "CC";//CC插入的位置在哈希值为p1的位置上,见下图
set.remove(p1);//这里删除时 找的是(1001,"CC")的哈希值 因为之前没插入过(1001,"CC"),所以很大概率删除的是个空值。见下图
System.out.println(set);
//因此这里输出的set是(1001,"CC"),(1002,BB)
set.add(new Person(1001,"CC"));
//这里实际上还是能插入成功的,set不是不允许重复数据吗?
//但是上一条(1001,"CC")插入的哈希位置是(1001,"AA")的,set的add操作时先进行hashcode判断,hashcode一样再进行equals判断;
//插入(1001,"CC")时首先进行hashcode判断,(1001,"CC")的哈希值大概率和(1001,"AA")不一样,所以都到不了equals()这一步,自然能插入成功。
System.out.println(set);
//so 输出三条数据
set.add(new Person(1001,"AA"));
//插入成功,首先进行hashcode判断,虽然和之前的(1001,"AA")哈希值冲突了,但是再进行equals判断时,之前(1001,"AA")的位置已经是(1001,"CC")了,所以equals判断通过不相等。插入成功。
System.out.println(set);
//输出四条数据
  

在这里插入图片描述

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

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

相关文章

【Spring】— 应用案例——基于注解的Spring MVC应用

目录 应用案例——基于注解的Spring MVC应用1.搭建项目环境2.修改配置文件3.修改Controller类4.启动项目&#xff0c;测试应用 应用案例——基于注解的Spring MVC应用 为了帮助读者熟悉掌握Spring MVC的核心类和注解的使用&#xff0c;接下来将以注解的方式对入门案例进行改写…

CPU多核一定比单核性能强吗

在一般人的眼中&#xff0c;多核性能肯定比单核强&#xff0c;处理器核心越多越好&#xff0c;但果真如此吗&#xff1f; 许多人认为CPU 内核越多越好。然而&#xff0c;情况并非总是如此。这就是为什么更多内核并非在所有情况下都是好的选择&#xff0c;这不仅仅是因为在芯片…

数据结构——单向链表(万字讲解)

单向链表&#xff08;又名单链表、线性链表&#xff09;是链表的一种&#xff0c;其特点是链表的链接方向是单向的&#xff0c;对链表的访问要通过从头部开始&#xff0c;依序往下读取。 //单链表的打印 void SLTPrint(SLTNode* phead) {SLTNode* cur phead;while (cur){pri…

数据孤岛如何形成,企业如何破解?

国内企业数据化现状 在数字化时代&#xff0c;数据已经成为了企业不可或缺的重要资产。企业内部部门或系统之间存在着数据割裂现象&#xff0c;导致数据无法流通和共享&#xff0c;从而形成数据孤岛的现象。对于企业来说&#xff0c;数据孤岛问题是十分普遍的&#xff0c;它阻…

如何在项目管理中获得更多权威和影响力?这篇文章告诉你!

1 透明力 信息力和感知力是对环境的观察、观察、再观察&#xff0c;这些 观察的结果只有透明出来&#xff0c;才能发挥效用。你要想法把你看到的问题可视化&#xff0c;让决策者和团队都能看到问题。即透明的力量。 1.1 怎么运用透明的力量呢&#xff1f; 我曾在某团队经常听…

win11 系统安装 wsl

1. 打开虚拟设置 在【开始】搜索【启用或关闭 Windows 功能】&#xff0c;打开Windows 功能对话框&#xff0c;勾选“适用于Linux的Windows子系统”和“虚拟机平台”两个选项。 2. wsl 安装 管理员模式启动powershell 设置wsl版本&#xff1a;wsl --set-default-version 1 更新…

业务 IT 一体化:华为云 SaaS Package 做到了

SaaS 这个概念在近两年可谓十分火热&#xff0c;特别是进入到了后疫情时代&#xff0c;似乎离不开 SasS。然而很多人并没有真正理解 SaaS 的概念&#xff0c;对 SaaS 的认知还是比较片面。 你要弄清 SaaS&#xff0c;首先就绕不开 laaS 和 PaaS。下面这张图可以很直观地让你明…

leetcode40. 组合总和 II (java-回溯算法)

组合总和 II leetcode40. 组合总和 II题目描述解题思路代码演示 回溯算法专题 leetcode40. 组合总和 II 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/combination-sum-ii 题目描述 给定一个候选人编号的集合 candida…

【滤波】扩展卡尔曼滤波器

本文主要翻译自rlabbe/Kalman-and-Bayesian-Filters-in-Python的第11章节11-Extended-Kalman-Filters&#xff08;扩展卡尔曼滤波&#xff09;。 %matplotlib inline#format the book import book_format book_format.set_style()我们发展了线性卡尔曼滤波器的理论。然后在上两…

Fiddler之日常使用简介

目录 前言&#xff1a; Fiddler界面简介 一、Fiddler抓取http、https请求配置 二、抓取指定域名会话 三、http请求统计试图 前言&#xff1a; Fiddler是一款功能强大的Web调试工具&#xff0c;它可以帮助开发人员在开发和测试过程中捕获、修改和检查HTTP请求和响应。 使用Fiddl…

Python基础(11)——Python循环(while、for)

Python基础&#xff08;11&#xff09;——Python循环&#xff08;while、for&#xff09; 文章目录 Python基础&#xff08;11&#xff09;——Python循环&#xff08;while、for&#xff09;目标一. 循环简介1.1 循环的作用1.2 循环的分类 二. while的语法2.1 快速体验 三. w…

全球开源 AI 游戏开发挑战赛,只等你来!

我们在之前的文章中 预告过 (*划重点&#xff0c;IP 属地法国)&#xff1a;7 月初&#xff0c;我们将举办一次与 AI 游戏相关的黑客松活动&#xff0c;这是有史以来的首次开源游戏开发挑战赛&#xff0c;借助人工智能工具释放你的创造力&#xff0c;一起打破游戏开发的边界。 比…

Python自动化测试 软件测试最全教程(附笔记),看完就可就业

最近看到很多粉丝在后台私信我&#xff0c;叫我做一期Python自动化测试的教程&#xff0c;其实关于这个问题&#xff0c;我也早就在着手准备了&#xff0c;我录制了一整套完整的Python自动化测试的教程&#xff0c;都上传在B站上面&#xff0c;大家有兴趣的可以去看一下&#x…

Redis 2023面试5题(二)

一、Redis key没设置过期时间却被redis主动删除了 如果一个 Redis 键没有设置过期时间&#xff0c;那么 Redis 无法判断该键是否应该自动删除。因此&#xff0c;如果一个没有设置过期时间的键被 Redis 主动删除了&#xff0c;可能是以下原因之一&#xff1a; 内存不足&#x…

工程机械比例阀电流采集方案

工程机械的需求量变得越来越大&#xff0c;而关于工程机械&#xff0c;其比例阀的控制问题不容忽视。比例阀是一种新型的液压控制装置。在普通压力阀、流量阀和方向阀上&#xff0c;用比例电磁铁替代原有的控制部分&#xff0c;并按输入的电气信号连续、按比例地对油流的压力、…

VUE 2X 条件列表渲染 ⑧

目录 文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持✨ V u e j s Vuejs Vuejs条件渲染列表渲染 基本列表 Key的原理 列表过滤 列表排序 列表更新小问题 监测数据改变的原理~对象 V u e S e t VueSet VueSet的…

常见web中间件漏洞复现总结

文章目录 IISPUT漏洞iis6.0 解析漏洞IIS短文件漏洞远程代码执行(RCE-CVE-2017-7269)iis7.x文件解析漏洞HTTP.SYS远程代码执行 (MS15-034) apache未知扩展名解析漏洞AddHandler解析漏洞目录遍历漏洞Apache HTTPD 换行解析漏洞&#xff08;CVE-2017-15715&#xff09; Nginx文件解…

RabbitMQ入门学习笔记

文章目录 RabbitMQ学习笔记前言1、RabbitMQ概述1.1 MQ概述1.2 RabbitMQ概述 2、RabbitMQ的安装3、RabbitMQ初体验4、工作队列4.1 消息应答4.1.1 消息应答简介4.1.2 消息手动应答 4.2 队列和消息的持久化4.2.1 队列持久化4.2.2 消息持久化 4.3 消息分发4.3.1 不公平分发4.3.2 预…

[进阶]Java:线程池、处理Runnable、Callable任务、使用Executors得到线程池

什么是线程池&#xff1f; 线程池就是一个可以复用线程的技术。 不实用线程池的问题&#xff1f; 用户每发起一个请求&#xff0c;后台就需要创建一个新线程来处理&#xff0c;下次新任务来了肯定又要创建新线程处理的&#xff0c; 而创建新线程的开销是很大的&#xff0c;并…

MIT 6.S081 (BOOK-RISCV-REV1)教材第四章内容 --中

MIT 6.S081 教材第四章内容 -- 中 引言Debug Trap代码执行流程进入Trap前发生Trap时uservec函数Issue usertrap函数usertrapret函数userret函数 小结 引言 MIT 6.S081 2020 操作系统 本文为MIT 6.S081课程第四章教材内容翻译加整理。 本课程前置知识主要涉及: C语言(建议阅读…