Java集合04
9.Set接口方法
- Set接口基本介绍
- 无序(添加和取出的顺序不一致),没有索引
- 不允许重复元素,所以最多只有一个null
- JDK API中接口的实现类有:

- Set接口的常用方法:和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样。
- Set接口的遍历方式:同Collection的遍历方式一样,因为Set是Collection的子接口所以:
- 可以使用迭代器
- 增强for循环
- 不能使用索引的方式来获取
 例子1:以Set接口的实现类HashSet来讲解Set的方法
1.set接口的实现类的对象(set实现类对象),不能存放重复的数据,且最多只能添加一个null
2.set接口对象存放数据是无序的(即添加对象顺序和取出的对象顺序不一致)
3.注意:但是取出的顺序是固定的- package li.collections.set;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.Set;
- @SuppressWarnings("all")
- public class SetMethod {
- public static void main(String[] args) {
-
- Set set = new HashSet();
- set.add("john");
- set.add("lucy");
- set.add("john");
- set.add("jack");
- set.add("marry");
- set.add(null);
- set.add(null);
- System.out.println(set);//取出的顺序是一致的:[null, marry, john, lucy, jack]
- //遍历
- //1.使用迭代器
- Iterator iterator = set.iterator();
- System.out.println("======迭代器遍历======");
- while (iterator.hasNext()) {
- Object obj = iterator.next();
- System.out.print(obj+"\t");
- }
- //2.增强for循环
- System.out.println('\n'+"======增强for循环======");
- for (Object o:set) {
- System.out.print(o+"\t");
- }
- //set接口对象不能使用通过索引来获取,因此没有get方法
-
- }
- }
复制代码 10.HashSet
- HashSet实现了接口Set
- HashSet实际上是HashMap

- 可以存放null值,但只能有一个null
- HashSet 不保证元素是有序的,取决于hash后,再确定索引的结果
- 不能有重复元素/对象
例子1:不允许重复元素
- package li.collections.set;
- import java.util.HashSet;
- import java.util.Set;
- @SuppressWarnings("all")
- public class HasSet_ {
- public static void main(String[] args) {
- Set hashSet = new HashSet();
- //1.HashSet可以存放null,但是只能存放一个null,即不允许重复元素
- hashSet.add(null);
- hashSet.add(null);
- System.out.println(hashSet);//[null]
- }
- }
复制代码例子2:
- package li.collections.set;
- import java.util.HashSet;
- @SuppressWarnings("all")
- public class HashSet01 {
- public static void main(String[] args) {
- HashSet hashSet = new HashSet();
- //1.在执行add方法后会返回一个boolean值,添加成功返回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
- //2.可以通过remove()指定删除哪个对象
- hashSet.remove("john");
- System.out.println(hashSet);//[rose, lucy, jack]
- }
- }
复制代码 10.1怎么理解不能添加相同的元素/数据?
例子:
- package li.collections.set;
- import java.util.HashSet;
- @SuppressWarnings("all")
- public class HashSet01 {
- public static void main(String[] args) {
- HashSet hashSet = new HashSet();
- hashSet.add("lucy");//添加成功
- hashSet.add("lucy");//添加失败
- hashSet.add("lucy");//添加失败
- hashSet.add(new Dog("tom"));//添加成功
- hashSet.add(new Dog("tom"));//添加成功
- System.out.println(hashSet);//[Dog{name='tom'}, Dog{name='tom'}, lucy]
- //!!!经典面试题:
- hashSet.add(new String("jackcheng"));//添加成功
- hashSet.add(new String("jackcheng"));//添加失败,为什么呢?
- System.out.println(hashSet);//[jackcheng, Dog{name='tom'}, Dog{name='tom'}, lucy]
- }
- }
- class Dog{
- private String name;
- public Dog(String name) {
- this.name = name;
- }
- @Override
- public String toString() {
- return "Dog{" +
- "name='" + name + '\'' +
- '}';
- }
- }
复制代码 为什么相同的两个 hashSet.add(new Dog("tom")); hashSet.add(new Dog("tom"));语句可以添加成功,而却添加失败呢?原因在于HashSet的底层机制- hashSet.add(new String("jackcheng"));//添加成功
- hashSet.add(new String("jackcheng"));//添加失败,为什么呢?
复制代码 10.2HashSet的底层扩容机制
HashSet底层是HashMap,HashMap底层是数组+链表+红黑树
说明:
- HashSet底层是HashMap第一次添加时,table数组扩容到16,临界值(threshold)是16*加载因子(loadFactor,0.75)=12;如果table数组容量使用到了临界值12,就会扩容到16 * 2=32,新的临界值就是32 * 0 .75=24,依此类推(两倍扩容)
注意:这里的容量计算的不仅仅是table数组上的容量,链表上的容量也算。即只要增加了一个元素,使用的容量就+1
例如:当一个table表的数组某个索引位置上存储了一个值,而这个值后面的链表存储了7个值,加起来就是8,那么在数组长度没有超过64时,再加入一个值,数组就会进行两倍扩容
- 添加一个元素时,先得到一个hash值--会转成--索引值
- 找到存储数据表table,看这个索引位置是否已经存放有元素
3.1 如果没有则直接加入
3.2 如果有,则调用equals()比较,如果相同就放弃添加;如果不相同则添加到最后
- 在jdk8中,如果一条链表的元素个数 >= TREEIFY_THRESHOLD (默认为8),并且table数组的大小 >= MIN_TREEIFY_CAPACITY (默认为64)就会进行树化(红黑树),否则仍然采用数组扩容机制
 例子:模拟链表
- package li.collections.set;
- public class HashSetStructure {
- public static void main(String[] args) {
- //模拟一个HashSet的底层(即HashMap的底层结构)
- //1.创建一个数组,数组的类型是Node
- Node[] table = new Node[16];
- System.out.println(table);
- //2.创建结点
- Node john = new Node("john", null);
- table[2] = john;
- Node jack = new Node("jack", null);
- john.next = jack;//将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);
- }
- }
- class Node {//结点,用于存放数组,可以指向下一个节点从而形成链表
- Object item;//存放数据
- Node next;//指向下一个节点
- public Node(Object item, Node next) {
- this.item = item;
- this.next = next;
- }
- }
复制代码 例子2:HashSet的两倍扩容机制
- 如图,HashSet第一次添加数据,table数组的长度为16
 
- 在数组容量使用到12(临界值) 时,会进行一个两倍的扩容(12*0.75=32)

 例子3:HashSet的树化
- package li.collections.set.hashset;
- import java.util.HashSet;
- @SuppressWarnings("all")
- public class HashSetSource {
- public static void main(String[] args) {
- HashSet hashSet = new HashSet();
- for (int i = 1; i <=12 ; i++) {
- hashSet.add(new A(i));
- }
-
- System.out.println(hashSet);
- }
- }
- class A {
- private int n;
- public A(int n) {
- this.n = n;
- }
- @Override
- public int hashCode(){//重写hashCode方法
- return 100;
- }
- }
复制代码 </ol>
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |