HashSet类

  • 底层:数组+链表[+红黑树(JDK1.8+)]

  • HashSet底层是HashMap,用了HashMap的key(key不可重复)。

    public HashSet() {
      map = new HashMap<>();
    }
    
    public boolean add(E e) {
      return map.put(e, PRESENT)==null;
    }
    
    private static final Object PRESENT = new Object();
    // PRESENT是一个不变的值。
    
  • 基于HashCode计算元素存放位置。

  • 当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入。

  • 如果要改写HashSet判断关于某两个对象是否相等,需要同时改写hashCode()equals()方法。

1. HashSet

Set<String> set = new HashSet<>();
// 1.添加数据
set.add("tang");
set.add("he");
set.add("yu");
System.out.println("数据个数:" + set.size());
System.out.println(set.toString());//无序输出
// 2.删除数据
/*
         * set.remove("tang"); System.out.println(set.toString());
         */
// 3.遍历【重点】
// 3.1 使用增强for
for (String string : set) {
  System.out.println(string);
}
// 3.2 使用迭代器
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
  System.out.println(iterator.next());
}
// 4.判断
System.out.println(set.contains("tang"));
System.out.println(set.isEmpty());
HashSet<Person> hashSet = new HashSet<>();
Person p1 = new Person("tang", 21);
Person p2 = new Person("he", 22);
Person p3 = new Person("yu", 21);
// 1.添加元素
hashSet.add(p1);
hashSet.add(p2);
hashSet.add(p3);
// 重复,添加失败
hashSet.add(p3);
// 直接new一个相同属性的对象,依然会被添加,不难理解。
// 假如相同属性便认为是同一个对象,怎么修改?
hashSet.add(new Person("yu", 21));
System.out.println(hashSet.toString());
// 2.删除元素
hashSet.remove(p2);
// 3.遍历
// 3.1 增强for
for (Person person : hashSet) {
  System.out.println(person);
}
// 3.2 迭代器
Iterator<Person> iterator = hashSet.iterator();
while (iterator.hasNext()) {
  System.out.println(iterator.next());
}
// 4.判断
System.out.println(hashSet.isEmpty());
// 直接new一个相同属性的对象结果输出是false,不难理解。
// 注:假如相同属性便认为是同一个对象,该怎么做?
System.out.println(hashSet.contains(new Person("tang", 21)));

:hashSet存储过程:

  1. 根据hashCode计算保存的位置,如果位置为空,则直接保存,否则执行第二步。
  2. 执行equals方法,如果方法返回true,则认为是重复,拒绝存储,否则形成链表。

存储过程实际上就是重复依据,要解决上述的问题,可以重写hashCode和equals代码:

@Override
public int hashCode () {
  final int prime = 31;
  int result = 1;
  result = prime * result + age;
  result = prime * result + ((name == null) ? 0 : name.hashCode());
  return result;
}
@Override
public boolean equals (Object obj){
  if (this == obj)
    return true;
  if (obj == null)
    return false;
  if (getClass() != obj.getClass())
    return false;
  Person other = (Person) obj;
  if (age != other.age)
    return false;
  if (name == null) {
    if (other.name != null)
      return false;
  } else if (!name.equals(other.name))
    return false;
  return true;
}

hashCode方法里为什么要使用31这个数字大概有两个原因:

  1. 31是一个质数,这样的数字在计算时可以尽量减少散列冲突。
  2. 可以提高执行效率,因为31*i=(i<<5)-i,31乘以一个数可以转换成移位操作,这样能快一点;但是也有网上一些人对这两点提出质疑。

2. HashSet源码

HashSet的存储结构就是HashMap

public boolean add(E e) {
  return map.put(e, PRESENT)==null;
}

很明了地发现它的add方法调用的就是map的put方法,把元素作为map的key传进去的。。

Copyright © rootwhois.cn 2021-2022 all right reserved,powered by GitbookFile Modify: 2023-03-05 10:55:52

results matching ""

    No results matching ""