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然后就会树化
// 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);
}
}