一、基本介绍:
1、Set接口的特点:
1)无序(添加和取出的顺序不一致) ,没有索引
2)不允许重复元素,所以最多包含一个null
3) JDK API中Set接口的实现类有:
2、Set接口的常用方法:
和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样.
3、Set接口的遍历方式:
同Collection的遍历方式一样,因为Set接口是Collection接口的子接口。
(1)可以使用迭代器
(2)增强for
(3)不能使用索引的方式来获取.
package Collection_;import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;public class SetMethod {@SuppressWarnings({"all"})public static void main(String[] args) {Set set=new HashSet();set.add("john");set.add("lucky");set.add("john");set.add("jack");set.add(null);set.add(null);System.out.println("set="+set);//set=[lucky, null, john, jack]//取出的顺序是固定的,不会说同样的代码每次输出都不一样set.add("smith");System.out.println("set="+set);//set=[lucky, null, smith, john, jack]//遍历//1、使用迭代器System.out.println("=========使用迭代器=========");Iterator iterator=set.iterator();while (iterator.hasNext()) {Object obj = iterator.next();System.out.println("obj="+obj);}//2、增强forSystem.out.println("=========增强for=========");for (Object o :set) {System.out.println("o="+o);}}
}
//=========使用迭代器=========
//obj=lucky
//obj=null
//obj=smith
//obj=john
//obj=jack
//=========增强for=========
//o=lucky
//o=null
//o=smith
//o=john
//o=jack
4、Set接口常用的实现类:有HashSet、TreeSet.
二、HashSet:
1、基本介绍:
(1) HashSet实现了Set接口
(2)HashSet底层实际上是HashMap, 看下源码.
public HashSet(){map = new HashMap<>();
}
(3) 可以存放null值,但是只能有一个null.
(4)HashSet不保证元素是有序的,取决于hash后,再确定索引的结果.即,不保证存放元素的顺序和取出顺序一致
(5) 不能有重复元素/对象.
package Collection_;import java.util.HashSet;
import java.util.Set;
@SuppressWarnings({"all"})
public class HashSet_ {public static void main(String[] args) {Set hashSet = new HashSet();hashSet.add(null);hashSet.add(null);System.out.println("hashset="+hashSet);//hashset=[null]}
}
package Collection_;import java.util.HashSet;
import java.util.Set;
@SuppressWarnings({"all"})
public class HashSet_ {public static void main(String[] args) {Set set=new HashSet();set.add("john");set.add("tom");set =new HashSet();System.out.println("set="+set);//set=[]set.add("lucy");//okset.add("lucy");//noset.add(new Dog("tom"));//okset.add(new Dog("tom"));//ok 不同对象System.out.println("set="+set);//set=[Dog{name='tom'}, Dog{name='tom'}, lucy]//经典面试题set.add(new String("hsp"));//okset.add(new String("hsp"));//加入不了 看源码System.out.println("set="+set);//set=[Dog{name='tom'}, hsp, Dog{name='tom'}, lucy]}
}
class Dog{private String name;public Dog(String name) {this.name = name;}@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +'}';}
}
2、HashSet底层机制说明:(主要研究源码)
HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树)
package Collection_;
@SuppressWarnings({"all"})
public class HashSetStructure {public static void main(String[] args) {//1、创建一个Node类型的数组Node[] table=new Node[16];System.out.println("table="+table);//2、创建结点Node john=new Node("jonh",null);table[2]=john;Node jack=new Node("jack",null);john.next=jack;//将jack结点挂载到johnNode rose=new Node("Rose",null);jack.next=rose;//将rose结点挂载到jackSystem.out.println("table="+table);}
}
class Node{//结点,存储数据,可以指向下一个结点,从而形成链表Object item;//存放数据Node next;//指向下一个结点public Node(Object item, Node next) {this.item = item;this.next = next;}@Overridepublic String toString() {return "Node{" +"item=" + item +", next=" + next +'}';}
}
注意:equals的判断标准并不能简单地认为是两个字符串比较,因为String已经重写了equals方法,所以equals的判断标准和String没有关系,要视具体情况而定
//源码部分,非战斗人员,请做好撤退准备
package Collection_;import javax.swing.tree.TreeNode;
import java.util.HashMap;
import java.util.HashSet;
@SuppressWarnings({"all"})
public class HashSetSource {public static void main(String[] args) {HashSet hashSet=new HashSet();hashSet.add("java");hashSet.add("php");hashSet.add("java");System.out.println("set="+hashSet);/*追源码://1、执行HashSet()public HashSet() {map = new HashMap<>();}//2、执行add方法public boolean add(E e) {return map.put(e, PRESENT)==null;//PRESENT的源码: private static final Object PRESENT = new Object();主要起到占位的作用}//3、执行put方法,该方法会执行hash(key),得到key对应的hash值,//(hash(key)与hashcode不一样,hash(key)里面还包含了算法)相关算法: (h = key.hashCode()) ^ (h >>> 16)public V put(K key, V value) {//是PRESENT的值return putVal(hash(key), key, value, false, true);}//4、执行 putVal(核心代码)final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node[] tab; Node p; int n, i;//定义了辅助变量//table是HashMap的一个数组,类型是Node[]//if语句表示如果当前table是null,或者大小=0//就是第一次扩容到16个空间if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;//(1)根据key,得到hash去计算该key应该存放到table表的哪个索引位置//并把这个位置的对象,赋给p//(2)判断p是否为null//(2.1)如果p为null,表示还没有存放元素,就创建一个Node(key="java",value=PRESENT)//(2.2)否则,就放在该位置tab[i] = newNode(hash, key, value, null);if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {//一个开发技巧提示:在需要局部变量(辅助变量)时,再创建Node e; K k;//如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样//并且满足下面两个条件之一://(1)准备加入的key和p指向的Node的结点的key是同一个对象//(2)p指向的Node结点的key的equals()和准备加入的key比较后相同//就不能加入if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;//再判断p是不是一棵红黑树//如果是一棵红黑树,就调用putTreeVal来进行添加else if (p instanceof TreeNode)e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);else {//如果table对应索引位置,已经是一个链表,就使用for循环比较//(1)依次和该链表的每一元素比较后,都不相同,就加入到该链表的最后// 注意在把元素添加到链表后,立即判断该链表是否已经达到8个结点// 就调用treeifyBin()对当前这个链表进行树化(转成红黑树)// 注意,在转成红黑树时,要进行判断,判断条件// if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)// resize();// 如果上面条件成立,先table扩容// 只有上面条件不成立时,才进行转成红黑树//(2)依次和该链表的每一个元素比较过程中,如果有相同情况,就直接breakfor (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;//size就是我们每加入一个结点Node(k,v,h,next),size++if (++size > threshold)resize();afterNodeInsertion(evict);return null;}*/}
}
3、练习题:
(1)
//我的代码:
package Collection_;import java.util.HashSet;
import java.util.Iterator;@SuppressWarnings({"all"})
public class HashSetExercise {public static void main(String[] args) {HashSet hashSet = new HashSet();Employee[] employees = new Employee[4];employees[0]=new Employee("jack", 18);employees[1]=new Employee("tom", 19);employees[2]=new Employee("rose", 20);employees[3]=new Employee("rose", 20);hashSet.add(employees[0]);for (int i = 1; i < employees.length; i++) {int tmp=0;Iterator iterator=hashSet.iterator();while (iterator.hasNext()) {Object next = iterator.next();if (employees[i].getAge() == hashSet.hashCode()|| employees[i].getName().equals(hashSet.hashCode())) {break;}}hashSet.add(employees[i]);}for (Object o :hashSet) {System.out.println(o);}}
}
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;}@Overridepublic String toString() {return "Employee{" +"name='" + name + '\'' +", age=" + age +'}';}
}
//老师的代码:
package Collection_;import java.util.HashSet;
import java.util.Objects;
@SuppressWarnings({"all"})
public class HashSetExercise {public static void main(String[] args) {HashSet hashSet = new HashSet();hashSet.add(new Employee("milan",18));hashSet.add(new Employee("jack",28));hashSet.add(new Employee("milan",18));System.out.println("hashSet="+hashSet);}
}
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;}@Overridepublic String toString() {return "Employee{" +"name='" + name + '\'' +", age=" + age +'}';}//重写equals()方法和hashCode()方法:@Overridepublic 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);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}
//hashSet=[Employee{name='milan', age=18}, Employee{name='jack', age=28}]
·重写equals()方法和hashCode()方法:
alt+insert---->---->下一步---->
---->下一步---->
(2)
//我的代码
package Collection_;import java.util.HashSet;
import java.util.Objects;@SuppressWarnings({"all"})
public class HashSetExercise02 {public static void main(String[] args) {HashSet hashSet=new HashSet();MyDate jack=new MyDate(2000,1,1);MyDate tom=new MyDate(2001,2,2);hashSet.add(new Employee("jack",20000,jack));hashSet.add(new Employee("tom",30000,tom));hashSet.add(new Employee("jack",20000,jack));System.out.println(hashSet);}
}
class Employee{private String name;private double sal;private MyDate birthday;public Employee(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;}@Overridepublic String toString() {return "Employee{" +"name='" + name + '\'' +", sal=" + sal +", birthday=" + birthday +'}';}@Overridepublic 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(birthday, employee.birthday);}@Overridepublic int hashCode() {return Objects.hash(name, 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;}@Overridepublic String toString() {return "MyDate{" +"year=" + year +", month=" + month +", day=" + day +'}';}
}
//[Employee{name='jack', sal=20000.0, birthday=MyDate{year=2000, month=1, day=1}}, Employee{name='tom', sal=30000.0, birthday=MyDate{year=2001, month=2, day=2}}]
三、LinkedHashSet
1、基本介绍:
(1)LinkedHashSet 是 HashSet的子类
(2)LinkedHashSet 底层是一个 LinkedHashMap,底层维护了一个数组+双向链表
(3)LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,同时使 用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
(4)LinkedHashSet 不允许添重复元素
2、LinkedHashSet底层机制:
package Collection_;import java.util.LinkedHashSet;
import java.util.Set;
@SuppressWarnings({"all"})
public class LinkedHashSetSource {public static void main(String[] args) {Set set=new LinkedHashSet();set.add(new String("AA"));set.add(456);set.add(456);set.add(new Customer("刘",1001));set.add(123);set.add("HSP");}
}
class Customer{private String name;private int no;public Customer(String name, int no) {this.name = name;this.no = no;}
}
3、练习题:
//我的代码:
package Collection_;import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
@SuppressWarnings({"all"})
public class LinkedHashSet_ {public static void main(String[] args) {Set linkedHashSet=new LinkedHashSet();linkedHashSet.add(new Car("奥拓",1000));linkedHashSet.add(new Car("奥迪",300000));linkedHashSet.add(new Car("法拉利",10000000));linkedHashSet.add(new Car("保时捷",70000000));linkedHashSet.add(new Car("奥迪",300000));System.out.println("linkedHashSet="+linkedHashSet);}public static 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;}@Overridepublic String toString() {return "Car{" +"name='" + name + '\'' +", price=" + price +'}';}@Overridepublic 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);}@Overridepublic int hashCode() {return Objects.hash(name, price);}}
}
//linkedHashSet=[Car{name='奥拓', price=1000.0}, Car{name='奥迪', price=300000.0}, Car{name='法拉利', price=1.0E7}, Car{name='保时捷', price=7.0E7}]