Set
- set接口是Collection接口的子接口,跟list接口一样
- 可以使用迭代器遍历
- 可以使用增强for
- 不能使用索引
- 相同元素不能加入,存放对象数据是无序的
HashSet
1)HashSet实现set接口
2)其本质HashMap
3)相同元素不能加入,存放对象数据是无序的
package List.Set;
import java.util.HashSet;
@SuppressWarnings({"all"})
public class HashSet01 {
public static void main(String[] args) {
HashSet hs = new HashSet();
/* System.out.println(hs.add("hello"));
System.out.println(hs.add("hello"));
System.out.println(hs.add("java"));*/
hs = new HashSet();
hs.add(new Dog("dog"));
hs.add(new Dog("dog"));
hs.add(new String("hello"));
hs.add(new String("hello"));
hs.add(new Integer(10));
hs.add(new Integer(10));
hs.add(null);
System.out.println(hs);
}
}
class Dog {
private String name;
public Dog(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
'}';
}
}
HashSet底层说明
HashSet其底层是(数组+链表+红黑树)
- HashSet底层是HashMap。
- 添加一个元素时,先得到hash值-会转成->索引值
- 找到存储数据表table,看这个索引位置是否已经存放的有元素 如果没有,直接加入
- 如果有调用equals比较,如果相同,就放弃添加,如果不相同,则添加到最后
- 在Java8中,如果一条链表的元素个数到达TREEIFYTHRESHOLD(默认是8),并且table的大小>=MINTREEIFYCAPACITY(默认64)就会进行树化(红黑树)
HashSet扩容说明
-
1.HashSet底层是HashMap,第一次添加临界值时,table数组扩容到16,(threshold是16*加载因子(loadFactor)是0.75=12
-
2.如果table数组使用到了临界值12,就会扩容到162=32,新的临界值就是320.75=24,依次类推
-
3.在Java8中,如果一条链表的元素个数到达TREEIFYTHRESHOLD(默认是8)中Etable的大小>=MINTREEIFYCAPACITY(默认64),就进行树化(红黑树),否则仍然采用数组扩容机制
练习
条件:当name和birthday的值相同时,不能加入HashSet集合
package List.Set;
import java.util.Date;
import java.util.HashSet;
import java.util.Objects;
@SuppressWarnings({"all"})
public class HashExercise02 {
public static void main(String[] args) {
HashSet set = new HashSet();
set.add(new Employee("polo","男",new myDate(1997,12,05)));
set.add(new Employee("p","男",new myDate(1997,12,05)));
set.add(new Employee("polo","女",new myDate(1997,12,05)));
System.out.println(set);
}
}
class Employee {
private String name;
private String sal;
private myDate brithday;
public Employee(String name, String sal, myDate brithday) {
this.name = name;
this.sal = sal;
this.brithday = brithday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSal() {
return sal;
}
public void setSal(String sal) {
this.sal = sal;
}
public myDate getBrithday() {
return brithday;
}
public void setBrithday(myDate brithday) {
this.brithday = brithday;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", sal='" + sal + '\'' +
", brithday=" + brithday +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(name, employee.name) && Objects.equals(brithday, employee.brithday);
}
@Override
public int hashCode() {
return Objects.hash(name,brithday);
}
}
class myDate{
private Integer year;
private Integer month;
private Integer day;
public myDate() {
}
public myDate(Integer year, Integer month, Integer day) {
this.year = year;
this.month = month;
this.day = day;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
public Integer getMonth() {
return month;
}
public void setMonth(Integer month) {
this.month = month;
}
public Integer getDay() {
return day;
}
public void setDay(Integer 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 Objects.equals(year, myDate.year) && Objects.equals(month, myDate.month) && Objects.equals(day, myDate.day);
}
@Override
public int hashCode() {
return Objects.hash(year, month, day);
}
}
LinkedHashSet
- LinkedHashSet是HashSet的子类
- LinkedHashSet底层是LinkedHashMap,底层维护了一个数组和双向链表
- LinkedHashSet根据元素的hashcode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的
- LinkedHashSet不允许添加重复元素
LinkedHashSet底层机制
- 在LinkedHastSet中维护了一个hash表和双向链表(LinkedHashSet有head和tail)
- 每一个节点有before和after属性,这样可以形成双向链表
- 在添加元素时,先求hash值,在求索引,确定该元素在table的位置,然后将添加的元素加入到双向链表(如果已经存在,不添加原则和hashset一样)
tail.next=newElement//示意代码
newElement.pre=tail
tail = newEelment - 这样的话,我们遍历LinkedHashSet也能确保插入顺序和遍历顺序一致
练习
package List.Set;
import java.util.LinkedHashSet;
import java.util.Objects;
@SuppressWarnings({"all"})
public class LinkHashSetExercise {
public static void main(String[] args) {
LinkedHashSet set = new LinkedHashSet();
set.add(new Car("长城", 100));
set.add(new Car("长城", 30000));
set.add(new Car("奥迪", 100000000));
set.add(new Car("奥迪", 10000));
set.add(new Car("奔驰", 100000));
System.out.println(set);
}
}
class Car {
private String name;
private int price;
public Car() {
}
public Car(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return price == car.price && Objects.equals(name, car.name);
}
@Override
public int hashCode() {
return Objects.hash(name, price);
}
}