Java学习Day08HashSet

本文详细介绍了Java中的HashSet集合,包括其特点(无序、无重复、底层实现原理——基于HashMap),添加、删除元素的方法,以及迭代器的使用。还探讨了HashSet的扩容机制和equals与hashCode的重要性。
摘要由CSDN通过智能技术生成

Java学习Day08HashSet

Set接口的特点

public class SetMethod {
    //无序 没有索引
    //不允许重复元素 最多包含一个null
    //AbstractSet, ConcurrentHashMap.KeySetView, ConcurrentSkipListSet,
    //CopyOnWriteArraySet, EnumSet, HashSet, JobStateReasons, LinkedHashSet, TreeSet
    //常用HashSet TreeSet
    //Set接口和List接口一样都是Collection子接口 所有常用的方法和List一样
    //遍历有区别 可以用增强for可以迭代器 但不能用索引遍历
    public static void main(String[] args) {
        //Set接口的实现类的对象
        //Set接口对象存放数据是无序的 添加的顺序和取出的顺序不一样
        //只要确定了顺序 就不会一直改变 底层是数组和链表
        Set set = new HashSet();
        set.add("arthur");
        set.add("sadie");
        set.add("dutch");
        set.add("dutch");
        set.add("john");
        set.add(null);
        set.add(null);
        System.out.println(set);
        //迭代器
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object next = iterator.next();
            System.out.println(next);
        }
        System.out.println("============");
        for (Object o :set) {
            System.out.println(o);
        }
//        for (int i = 0; i < set.size(); i++) {
//            System.out.println(set.getClass());
//        }
        //不能获取索引
    }
}

HashSet

public class HashSetMethod {
    public static void main(String[] args) {

        HashSet hashSet = new HashSet();
//源码  public HashSet() {
//            map = new HashMap<>();
//        }
        hashSet.add(null);//可以存放空值只能有一个空元素不能重复,
        hashSet.add(null);
        System.out.println(hashSet);
        //不保证存放元素的顺序和取出顺序一致 取决于hash后
    }
}
public class HashSetStructure {
    //HashSet底层是HashMap,HashMap底层是链表数组红黑树
    public static void main(String[] args) {
    //创建一个Node数组也叫表
        Node[] table = new Node[16];
        System.out.println(table);
        Node john =new Node("john",null);
        //为了数据存储高效table大小超过64 变成红黑树
        table[2]=john;
        Node jack = new Node("jack", null);
        john.Next = jack;//将jack节点挂在掉john
        System.out.println(table);
        Node rose = new Node("rose", null);
        jack.Next = rose;
        System.out.println(table);
        Node lucy = new Node("lucy", null);
        table[3]=lucy;
        System.out.println(table);
    }
}
class Node{//节点 存放数据 可以指向下一个节点
    Object item;
    Node Next;

    public Node(Object item, Node next) {
        this.item = item;
        Next = next;
    }

}
public class HashSet01 {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        //执行add会返回boolean值 成功true 否则false
        System.out.println(set.add("arthur"));
        System.out.println(set.add("sadie"));
        System.out.println(set.add("dutch"));
        System.out.println(set.add("dutch"));
        System.out.println(set.add("john"));
        System.out.println(set.add("john"));
        set.remove("john");
        System.out.println(set);

        set = new HashSet();
        System.out.println(set);
        set.add("lucy");//添加成功
        set.add("lucy");//添加错误
        set.add(new Dog("kais"));//可以添加
        set.add(new Dog("kais"));//可以添加
        System.out.println(set);

        //很重要
        set.add(new String("yuh"));
        set.add(new String("yuh"));//加入不了
        //String重写了hashcode方法和equals方法
        System.out.println(set);

    }
}
class Dog{
    private String name;

    public Dog(String name) {
        this.name = name;
    }

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

HashSet底层源码

public class HashSetSource {
    //先获取HashCode 使用Hashcode方法
    //对hashcode进行运算 得出索引值 决定元素的位置
    //如果该位置上没有其他元素则直接存放 如果相等就不再添加 不相等就以链表的形式添加
    //java8中一个链表中元素个数8 就会树化且当前table大于等于64 红黑树
    public static void main(String[] args) {
        HashSet set = new HashSet();
        //先执行hashmap
        //public HashSet() {
        //        map = new HashMap<>();
        //    }
        set.add("arthur");
        /*执行add
        public boolean add(E e) {
                return map.put(e, PRESENT)==null; present 是一个静态对象只是站位
            }
        得到hashcode
        public V put(K key, V value) {
                return putVal(hash(key), key, value, false, true);
            }
        static final int hash(Object key) {
                int h;
                return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
                                            这里就是hashcode值      降低hash冲突值
            }
        final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                boolean evict) {
              Node<K,V>[] tab; Node<K,V> p; int n, i;//定义辅助变量
                 //table是hashmap的一个数组 类型是Node[]


                if ((tab = table) == null || (n = tab.length) == 0)
                    n = (tab = resize()).length;
         newCap = DEFAULT_INITIAL_CAPACITY;默认给数组大小是16
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
               //                 0.75*16     当数组大小达到12时就直接扩容了
              //当我们执行     tab = resize()   后tab就已经变成大小16的数组


                if ((p = tab[i = (n - 1) & hash]) == null)
                    tab[i] = newNode(hash, key, value, null);
           //1.根据这个key计算该key应该存放到table表的位置 且赋给p 判断是否为空
           //2.p为null 表示没有存放过元素 就创建一个Node 不是空就存在该位置
                else {
                //一个开发技巧 什么地方需要辅助变量 就在哪里定义
                    Node<K,V> e; K k;
                //如果当前索引位置链表对应的第一个元素hash值和准备添加的key hash值一样
                //并且满足1.准备加入的key 和p指向的Node节点的key是同一个对象
                       或2.不是同一个对象 但内容相同
                    if (p.hash == hash &&
                        ((k = p.key) == key || (key != null && key.equals(k))))
                        e = p;
                //判断p是不是一颗红黑树 用红黑树算法
                    else if (p instanceof TreeNode)
                        e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                    else {
                     //依次和该链表比较后都不相同 就会挂到链表末端
                        for (int binCount = 0; ; ++binCount) {
                            if ((e = p.next) == null) {
                                p.next = newNode(hash, key, value, null);
                                if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                                                 7 然而该判断是从第二个元素开始的所以总元素8就开始树化
                                //在把元素添加到链表后立即调用下面方法对当前链表树化
                                //在进行树化前该表的大小小于64 先扩容再树化
                                    treeifyBin(tab, hash);
                                break;
                                //挂到链表末端结束
                            }
                            if (e.hash == hash &&
                                ((k = e.key) == key || (key != null && key.equals(k))))
                                break;
                            p = e;//把p指向e让下一次比较从链表下一个元素开始
                        }
                        //jdk7是头插 jdk8是尾插
                    }
                    if (e != null) { // existing mapping for key
                        V oldValue = e.value;
                        if (!onlyIfAbsent || oldValue == null)
                            e.value = value;
                        afterNodeAccess(e);
                        return oldValue;
                    }
                }
                ++modCount;
                //每加入一个节点Node table上每一个位置都算

                if (++size > threshold)
                    resize();
                放完一个数据之后大于12就扩容
                afterNodeInsertion(evict);
           用于子方法例如LinkedHashmap实现这个方法 所以在hashMap来说可以忽略
                return null;
            }*/
        set.add("java");
        set.add("arthur");
        System.out.println(set);
    }
}

HashSet扩容底层源码

public class HashSetIncrement {
    /*HashSet底层是hashmap 第一次添加时table数组扩容16
    临界值值是16*0.75 =12
    threshold   loadfactor泊松分布
    超过12就会自动扩容到32 新的临界值是32*0.75=24
    超过24就会扩容到64 依次类推
    java8中一个链表达到8 且table已经达到64 就会树化
    否则则是数组的扩容
    */
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
          HashSet set = new HashSet();
//        for (int i = 0; i <= 100; i++) {
//            set.add(new A(i));
//
//        }

//        for (int i = 0; i <=100; i++) {
//        在一条链表里第9个就会扩容16到32第10个就会扩到64 第11就会树化
//            set.add(new A(i));
//        }
        for (int i = 0; i <=7; i++) {//在某一条链表上添加7个A对象
            set.add(new A(i));
        }
        for (int i = 0; i <=7; i++) {//在另外一条链表上添加7个B对象
            set.add(new B(i));
        }
        /*
         当我们向hashset增加一个元素 封装一个 Node 就算增加一个size 所以以上B添加到3个就会扩容
        * */

    }
}
class B{
    private int n;

    public B(int n) {
        this.n = n;
    }

    @Override
    public int hashCode() {
        return 200;
    }
}
class A{
    private int n;

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

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

Exercise01

/*
  定义一个员工类包含员工姓名和年龄
  创建3个对象放入
  要求当名字年龄相同认为同一个人 不能添加
 */
public class HashSetExercise {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new employee("arthur",33, new MyDate(1999, 8, 5)));
        hashSet.add(new employee("arthur",33, new MyDate(1999, 8, 5)));
        hashSet.add(new employee("dutch",55, new MyDate(1999, 8, 5)));

        System.out.println(hashSet);

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

    public employee(String name, int age, MyDate myDate) {
        this.name = name;
        this.age = age;
    }

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

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

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

Exercise02

public class HashSetExercise02 {
    @SuppressWarnings({"all"})
    //定义员工类 创建3个员工存放在Hashset里 当名字和生日相同时认为是同一个人
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new myemployee("Alice",32000,new MyDate(1999,8,5)));
        hashSet.add(new myemployee("Alice",32555.85,new MyDate(1999,8,5)));
        hashSet.add(new myemployee("john",625555,new MyDate(1999,8,5)));
        System.out.println(hashSet);
    }

}
//类名不能是Employee会重合
class myemployee{
    private String name;
    private double sal;
    private MyDate birthday;
    //Mydate类定义成类myemployee的一个属性birthday

    public myemployee(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;
    }
    //重写谁代表了要求谁相同
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        myemployee that = (myemployee) o;
        return Objects.equals(name, that.name) && Objects.equals(birthday, that.birthday);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, birthday);
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", sal=" + sal +
                ", birthday=" + birthday +
                '}';
    }
}
class MyDate{
    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 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 boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyDate myDate = (MyDate) o;
        return year == myDate.year && month == myDate.month && day == myDate.day;
    }

    @Override
    public int hashCode() {
        return Objects.hash(year, month, day);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值