Java学习之路(四十)| 集合(三)——Set接口、HashSet接口实现类(重要)

各自努力,最高处见!加油!

一、Set接口

1.set接口介绍

  1. 无序(添加和取出的顺序不一致),没有索引
  2. 不允许重复元素,最多包含一个null

2.遍历方式

  1. 迭代器
  2. 增强for循环(底层也是迭代器)
  3. 不能使用索引方式获取(即没有get方法)

3.常用方法使用

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

public class Set01 {
    public static void main(String[] args) {
        //用HashSet子类来演示Set类的使用方法
        Set set=new HashSet();
        set.add("john");
        set.add("lucy");
        set.add("john");
        set.add("jack");
        set.add(null);
        set.add(null);
        System.out.println(set);//[null, john, lucy, jack]
        // Set接口实现类的对象不能存放重复的元素
        // 存放的数据是无序的
        // 添加的元素与取出的元素顺序不一致(取出的顺序是一定的,底层有算法来进行排序)

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

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

        //删除元素
        set.remove(null);
        System.out.println(set);

    }
}

二、HashSet接口实现类

HashSet的底层代码比较重要,复习韩顺平老师的522~525课

https://www.bilibili.com/video/BV1fh411y7R8?p=521

hash()+equals()

  1. HashSet底层是HashMap
  2. 添加一个元素时,先得到一个hash值–>转变为索引值。HashSet首先比较的就是hash值,不同对象的hash值不同,即使里面存放的数据相同,也会添加到数组中。(默认条件下,重写了hashCode方法的情况除外)。
  3. 找到存储数据表table,看这个索引位置是否已经存放元素
  4. 如果没有,直接加入
  5. 如果有,调用equals比较,如果相同,就放弃添加,如果不相同,则添加到最后
  6. 在JDK8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认为8),并且table大小>=MIN_TREEIFY_CAPACITY(默认为64)就会转化为红黑树
  7. 哈希值不是HashCode
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
        HashMap.Node[] tab;//用的是HashMap
        int n;
        if ((tab = this.table) == null || (n = tab.length) == 0) {//table是HashMap的一个成员变量
            n = (tab = this.resize()).length;
        }

        Object p;
        int i;
        if ((p = tab[i = n - 1 & hash]) == null) {
            tab[i] = this.newNode(hash, key, value, (HashMap.Node)null);
        } else {
            Object e;
            Object k;
            if (((HashMap.Node)p).hash == hash && ((k = ((HashMap.Node)p).key) == key || key != null && key.equals(k))) {
                e = p;
            } else if (p instanceof HashMap.TreeNode) {
                e = ((HashMap.TreeNode)p).putTreeVal(this, tab, hash, key, value);
            } else {
                int binCount = 0;

                while(true) {
                    if ((e = ((HashMap.Node)p).next) == null) {
                        ((HashMap.Node)p).next = this.newNode(hash, key, value, (HashMap.Node)null);
                        if (binCount >= 7) {
                            this.treeifyBin(tab, hash);
                        }
                        break;
                    }

                    if (((HashMap.Node)e).hash == hash && ((k = ((HashMap.Node)e).key) == key || key != null && key.equals(k))) {
                        break;
                    }

                    p = e;
                    ++binCount;
                }
            }

            if (e != null) {
                V oldValue = ((HashMap.Node)e).value;
                if (!onlyIfAbsent || oldValue == null) {
                    ((HashMap.Node)e).value = value;
                }

                this.afterNodeAccess((HashMap.Node)e);
                return oldValue;
            }
        }

        ++this.modCount;
        if (++this.size > this.threshold) {
            this.resize();
        }

        this.afterNodeInsertion(evict);
        return null;
    }

HashSet的扩容机制

  1. HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值(threshold)是16,加载因子loaFactor是0.75.
  2. 如果table数组使用到了临界值12,就会扩容到162=32,新的临界值就是320.75=24,以此类推。
    举例说明:
    第一次添加table数组扩容到16,继续添加。
    当添加了12个元素后,数组再次扩容,扩容到16的两倍32 。
    当添加了32×0.75=24个元素后,数组再次扩容,扩容到32的两倍64 。
    当添加了64×0.75=48个元素后,数组再次扩容,扩容到64的两倍128 。
    如此类推
    注:只要是往数组中添加了元素,不管添加在那个链表,都是增加了元素。
  3. 在JDK8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认为8),并且table大小>=MIN_TREEIFY_CAPACITY(默认为64)就会转化为红黑树,否则仍然采用数组扩容机制(元素个数大于8,且table大小大于64==>红黑树)。

调试程序:

import java.util.HashSet;

public class HashSetlncrement {
    public static void main(String[] args) {
        HashSet hashSet=new HashSet();
        for (int i = 0; i < 100; i++) {
            hashSet.add(new A(i));
        }
    }
}

class A{
    private int n;
    public A(int n){
        this.n=n;
    }

    @Override
    public int hashCode() {
        return 100;
    }
}

三、HashSet练习

1.习题一

定义一个类,该类包括:private成员属性name,age。要求:

  1. 创建3个Employee对象放入HashSet中
  2. 当name和age的值相同时,认为是相同员工,不能添加到HashSet中。
import java.util.HashSet;
import java.util.Objects;

public class HashSetHomework {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("jack",18));
        hashSet.add(new Employee("tom",20));
        hashSet.add(new Employee("jack",18));

        System.out.println(hashSet);
    }
}
class Employee{
    String name;
    int age;

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Employee)) return false;
        Employee employee = (Employee) o;
        return age == employee.age &&
                Objects.equals(name, employee.name);
    }

    @Override
    public int hashCode() {//name和age都相同,在计算hashCode时返回相同的结果
        return Objects.hash(name, age);
    }

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

注意:要同时重写hashCode和equals方法才能实现不重复添加的功能。

2.习题二

定义一个Employee类,该类包含:privvate成员属性name,money,birthday(MyDate类型),其中MyDate的类型包括year,month,day。要求:

  1. 创建3个Employee放入HashSet中。
  2. 当name和birthday的值相同时,认为是相同员工,不能添加到HashSet集合中。
import java.util.HashSet;
import java.util.Objects;

public class HashSetHomework {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("jack",18));
        hashSet.add(new Employee("tom",20));
        hashSet.add(new Employee("jack",18));

        System.out.println(hashSet);
    }
}
class Employee{
    String name;
    int age;

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Employee)) return false;
        Employee employee = (Employee) o;
        return age == employee.age &&
                Objects.equals(name, employee.name);
    }

    @Override
    public int hashCode() {//name和age都相同,在计算hashCode时返回相同的结果
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值