Set 接口介绍:
Set 接口的常用方法
Set 接口的遍历方式
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetMethod {
public static void main(String[] args) {
//以Set接口的实现类 HashSet 来演示
//Set接口的实现类对象(Set接口对象),不能存放重复元素
//Set接口对象存放和读取数据无序
//取出的顺序虽然不是添加的顺序,但是,是固定有序的
Set set = new HashSet();
set.add("John");
set.add("Lucy");
set.add("Jack");
set.add(null);
set.add(null);
System.out.println(set);//[null, John, Lucy, Jack] 执行多遍都是这个结果
set.remove(null);//等常用方法可以依照Colleciotn常用方法,是一致的
//遍历:迭代器
Iterator iterator = set.iterator();
while(iterator.hasNext()){
Object o = iterator.next();
System.out.println(o);
}
//遍历:增强for (底层还是迭代器)
for(Object o:set){
System.out.println(o);
}
//不能索引遍历,且set接口对象没有get()方法
}
}
import java.util.HashSet;
public class HashSet01 {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
//1.在执行add方法后,会返回一个boolean值
//2.如果添加成功,返回true,否则返回false
System.out.println(hashSet.add("john"));//true
System.out.println(hashSet.add("lucy"));//true
System.out.println(hashSet.add("john"));//false
System.out.println(hashSet.add("jack"));//true
System.out.println(hashSet.add("rose"));//true
hashSet.remove("john");//指定删除某对象
System.out.println("hashset = "+hashSet);//hashset = [rose, lucy, jack]
hashSet = new HashSet();
//HashSet不能添加相同的元素、数据
hashSet.add("lucy");//添加成功
hashSet.add("lucy");//加入不了
hashSet.add(new Dog("tom"));//OK
hashSet.add(new Dog("tom"));//也能加入
System.out.println("hashset = "+hashSet);//hashset = [Dog{name='tom'}, lucy, Dog{name='tom'}]
//经典面试题
hashSet.add(new String("ok"));//可以加入
hashSet.add(new String("ok"));//无法加入
System.out.println("hashset = "+hashSet);//hashset = [Dog{name='tom'}, ok, lucy, Dog{name='tom'}]
//看源码 add到底发生了什么 --》底层机制
}
}
class Dog{
private String name;
public Dog(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
'}';
}
}
HashSet 底层其实是HashMap,HashMap底层是(数组+链表+红黑树)
模拟数组+链表结构:
public class HashSetStructure {
public static void main(String[] args) {
//模拟一个HashSet的底层(HashMap)
//1.创建一个数组,数组的类型是Node[]
//2.Node[] 也称为一个表
Node[] table = new Node[16];
//3.创建一个节点
Node john = new Node("john", null);
table[2] = john;
Node jack = new Node("jack", null);
john.next = jack;//将节点挂载到john
Node rose = new Node("rose",null);
jack.next = rose;//将rose节点挂载到jack
Node lucy = new Node( "lucy",null);
table[3] = lucy;//把lucy放到table表的索引为3的位置
System.out.println("table = "+table);
}
}
class Node{//节点,存储数据,可以指向下一个节点,从而形成链表
Object item;//存放数据
Node next;//指向下一个节点
public Node(Object item, Node next) {
this.item = item;
this.next = next;
}
}
debug后解读:
HashSet底层机制:
用例:
定义一个Employee类,该类包含:private成员属性name,age
要求:
1.创建3个Employee对象放入HashSet中;
2.当name和age的值相同时,认为是相同员工,不能添加到HashSet集合中;
import java.util.HashSet;
import java.util.Objects;
public class HashSet_Exercise {
/**
* 定义一个Employee类,该类包含:private成员属性name,age
* 1.创建3个Employee对象放入HashSet中;
* 2.当name和age的值相同时,认为是相同员工,不能添加到HashSet集合中;
*/
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add(new Employee("jack",18));
hashSet.add(new Employee("tom",28));
hashSet.add(new Employee("rose",18));
//加入了三个成员
System.out.println(hashSet);//[Employee{name='jack', age=18}, Employee{name='rose', age=18}, Employee{name='tom', age=28}]
}
}
//创建Employee
class Employee{
private String name;
private int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//如果name和age相同,则返回相同的hash值
@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);
}
//name和age相同,hashcode相同
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
import java.util.LinkedHashSet;
import java.util.Set;
public class LinkedHashSetSource {
public static void main(String[] args) {
//LinkedHashSet底层机制
Set set = new LinkedHashSet();
set.add(new String("OK"));
set.add(128);
set.add(128);
set.add(new Customer("靳",1201));
set.add("JinYu");
System.out.println(set);
//[OK, 128, com.study.set_.Customer@677327b6, JinYu]
/*
1.添加元素和取出顺序一致
2.LinkedHashSet底层维护的是一个LinkedHashMap(是HashMap的子类)
3.LinkedHashSet底层结构:数组table+双向链表
4.添加第一次时,直接将数组table扩容到16,存放的结点类型是LinkedHashSetMap$Entry
5.数组是HashMap$Node[] 存放的元素/数据是LinkedHashSetMap$Entry类型
*/
}
}
class Customer{
private String name;
private int id;
public Customer(String name, int id) {
this.name = name;
this.id = id;
}
}
示例:Car类(属性name,price),如果name和price一样,则认为是相同元素,就不能添加
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
public class LinkedHashSetExercise {
public static void main(String[] args) {
Set set = new LinkedHashSet();
set.add(new Car("奥拓",1000));
set.add(new Car("奥迪",300000));
set.add(new Car("法拉利",9000000));
set.add(new Car("奥迪",300000));
set.add(new Car("保时捷",1000));
set.add(new Car("奥迪",300000));
System.out.println(set);
/* 未重写equals和hashCode方法:
[Car{name='奥拓', price=1000.0}
, Car{name='奥迪', price=300000.0}
, Car{name='法拉利', price=9000000.0}
, Car{name='奥迪', price=300000.0}
, Car{name='保时捷', price=1000.0}
, Car{name='奥迪', price=300000.0}
]*/
/* 重写equals和hashCode方法后:
[Car{name='奥拓', price=1000.0}
, Car{name='奥迪', price=300000.0}
, Car{name='法拉利', price=9000000.0}
, Car{name='保时捷', price=1000.0}
]
*/
}
}
class Car{
private String name;
private double price;
public Car(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", price=" + price +
'}'+"\n";
}
//重写equals方法和hashCode方法
//当name和price相同时,返回相同的hashCode值
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return Double.compare(car.price, price) == 0 && Objects.equals(name, car.name);
}
@Override
public int hashCode() {
return Objects.hash(name, price);
}
}
TreeSet的独特之处在于它的构造器可以传入比较器,所以TreeSet常用来排序,
TreeSet 底层是 TreeMap
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSet_ {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();//无参构造,默认排序
//添加数据
treeSet.add("Jack");
treeSet.add("Tom");
treeSet.add("Ayo");
treeSet.add("Luck");
System.out.println(treeSet);//默认排序:首字母ASCII由小到大
//[Ayo, Jack, Luck, Tom]
//如果我们想按字符串大小排序
//使用TreeSet提供的一个构造器,传入一个比较器(匿名内部类)指定排序规则
treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String)o2).compareTo((String)o1);//利用String类的compareTo方法,由大到小
//如果是按照长度由大到小:return ((String)o1).length()-((String)o2).length();
}//构造器把传入的比较器对象,赋给了TreeSet的底层的TreeMap的属性this.comparator
});
treeSet.add("Jack");
treeSet.add("Tom");
treeSet.add("Ayo");
treeSet.add("Luck");
System.out.println(treeSet);//[Tom, Luck, Jack, Ayo]
}
}