• 43、集合的第一大类:Set


    一、基本介绍: 

    1、Set接口的特点:

    1)无序(添加和取出的顺序不一致) ,没有索引

    2)不允许重复元素,所以最多包含一个null

    3) JDK API中Set接口的实现类有:

     2、Set接口的常用方法:

    和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样.

    3、Set接口的遍历方式:

    同Collection的遍历方式一样,因为Set接口是Collection接口的子接口。

    (1)可以使用迭代器

    (2)增强for

    (3)不能使用索引的方式来获取.

    1. package Collection_;
    2. import java.util.HashSet;
    3. import java.util.Iterator;
    4. import java.util.Set;
    5. public class SetMethod {
    6. @SuppressWarnings({"all"})
    7. public static void main(String[] args) {
    8. Set set=new HashSet();
    9. set.add("john");
    10. set.add("lucky");
    11. set.add("john");
    12. set.add("jack");
    13. set.add(null);
    14. set.add(null);
    15. System.out.println("set="+set);
    16. //set=[lucky, null, john, jack]
    17. //取出的顺序是固定的,不会说同样的代码每次输出都不一样
    18. set.add("smith");
    19. System.out.println("set="+set);
    20. //set=[lucky, null, smith, john, jack]
    21. //遍历
    22. //1、使用迭代器
    23. System.out.println("=========使用迭代器=========");
    24. Iterator iterator=set.iterator();
    25. while (iterator.hasNext()) {
    26. Object obj = iterator.next();
    27. System.out.println("obj="+obj);
    28. }
    29. //2、增强for
    30. System.out.println("=========增强for=========");
    31. for (Object o :set) {
    32. System.out.println("o="+o);
    33. }
    34. }
    35. }
    36. //=========使用迭代器=========
    37. //obj=lucky
    38. //obj=null
    39. //obj=smith
    40. //obj=john
    41. //obj=jack
    42. //=========增强for=========
    43. //o=lucky
    44. //o=null
    45. //o=smith
    46. //o=john
    47. //o=jack

    4、Set接口常用的实现类:有HashSet、TreeSet.

    二、HashSet:

    1、基本介绍: 

    (1) HashSet实现了Set接口

    (2)HashSet底层实际上是HashMap, 看下源码.

    1. public HashSet(){
    2.         map = new HashMap<>();
    3. }

    (3) 可以存放null值,但是只能有一个null.

    (4)HashSet不保证元素是有序的,取决于hash后,再确定索引的结果.即,不保证存放元素的顺序和取出顺序一致

    (5) 不能有重复元素/对象.

    1. package Collection_;
    2. import java.util.HashSet;
    3. import java.util.Set;
    4. @SuppressWarnings({"all"})
    5. public class HashSet_ {
    6. public static void main(String[] args) {
    7. Set hashSet = new HashSet();
    8. hashSet.add(null);
    9. hashSet.add(null);
    10. System.out.println("hashset="+hashSet);
    11. //hashset=[null]
    12. }
    13. }
    1. package Collection_;
    2. import java.util.HashSet;
    3. import java.util.Set;
    4. @SuppressWarnings({"all"})
    5. public class HashSet_ {
    6. public static void main(String[] args) {
    7. Set set=new HashSet();
    8. set.add("john");
    9. set.add("tom");
    10. set =new HashSet();
    11. System.out.println("set="+set);//set=[]
    12. set.add("lucy");//ok
    13. set.add("lucy");//no
    14. set.add(new Dog("tom"));//ok
    15. set.add(new Dog("tom"));//ok 不同对象
    16. System.out.println("set="+set);
    17. //set=[Dog{name='tom'}, Dog{name='tom'}, lucy]
    18. //经典面试题
    19. set.add(new String("hsp"));//ok
    20. set.add(new String("hsp"));//加入不了 看源码
    21. System.out.println("set="+set);
    22. //set=[Dog{name='tom'}, hsp, Dog{name='tom'}, lucy]
    23. }
    24. }
    25. class Dog{
    26. private String name;
    27. public Dog(String name) {
    28. this.name = name;
    29. }
    30. @Override
    31. public String toString() {
    32. return "Dog{" +
    33. "name='" + name + '\'' +
    34. '}';
    35. }
    36. }

     2、HashSet底层机制说明:(主要研究源码)

    HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树)

    1. package Collection_;
    2. @SuppressWarnings({"all"})
    3. public class HashSetStructure {
    4. public static void main(String[] args) {
    5. //1、创建一个Node类型的数组
    6. Node[] table=new Node[16];
    7. System.out.println("table="+table);
    8. //2、创建结点
    9. Node john=new Node("jonh",null);
    10. table[2]=john;
    11. Node jack=new Node("jack",null);
    12. john.next=jack;//将jack结点挂载到john
    13. Node rose=new Node("Rose",null);
    14. jack.next=rose;//将rose结点挂载到jack
    15. System.out.println("table="+table);
    16. }
    17. }
    18. class Node{//结点,存储数据,可以指向下一个结点,从而形成链表
    19. Object item;//存放数据
    20. Node next;//指向下一个结点
    21. public Node(Object item, Node next) {
    22. this.item = item;
    23. this.next = next;
    24. }
    25. @Override
    26. public String toString() {
    27. return "Node{" +
    28. "item=" + item +
    29. ", next=" + next +
    30. '}';
    31. }
    32. }

    注意:equals的判断标准并不能简单地认为是两个字符串比较,因为String已经重写了equals方法,所以equals的判断标准和String没有关系,要视具体情况而定

    1. //源码部分,非战斗人员,请做好撤退准备
    2. package Collection_;
    3. import javax.swing.tree.TreeNode;
    4. import java.util.HashMap;
    5. import java.util.HashSet;
    6. @SuppressWarnings({"all"})
    7. public class HashSetSource {
    8. public static void main(String[] args) {
    9. HashSet hashSet=new HashSet();
    10. hashSet.add("java");
    11. hashSet.add("php");
    12. hashSet.add("java");
    13. System.out.println("set="+hashSet);
    14. /*
    15. 追源码:
    16. //1、执行HashSet()
    17. public HashSet() {
    18. map = new HashMap<>();
    19. }
    20. //2、执行add方法
    21. public boolean add(E e) {
    22. return map.put(e, PRESENT)==null;
    23. //PRESENT的源码: private static final Object PRESENT = new Object();主要起到占位的作用
    24. }
    25. //3、执行put方法,该方法会执行hash(key),得到key对应的hash值,
    26. //(hash(key)与hashcode不一样,hash(key)里面还包含了算法)相关算法: (h = key.hashCode()) ^ (h >>> 16)
    27. public V put(K key, V value) {//是PRESENT的值
    28. return putVal(hash(key), key, value, false, true);
    29. }
    30. //4、执行 putVal(核心代码)
    31. final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
    32. boolean evict) {
    33. Node[] tab; Node p; int n, i;//定义了辅助变量
    34. //table是HashMap的一个数组,类型是Node[]
    35. //if语句表示如果当前table是null,或者大小=0
    36. //就是第一次扩容到16个空间
    37. if ((tab = table) == null || (n = tab.length) == 0)
    38. n = (tab = resize()).length;
    39. //(1)根据key,得到hash去计算该key应该存放到table表的哪个索引位置
    40. //并把这个位置的对象,赋给p
    41. //(2)判断p是否为null
    42. //(2.1)如果p为null,表示还没有存放元素,就创建一个Node(key="java",value=PRESENT)
    43. //(2.2)否则,就放在该位置tab[i] = newNode(hash, key, value, null);
    44. if ((p = tab[i = (n - 1) & hash]) == null)
    45. tab[i] = newNode(hash, key, value, null);
    46. else {
    47. //一个开发技巧提示:在需要局部变量(辅助变量)时,再创建
    48. Node e; K k;
    49. //如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样
    50. //并且满足下面两个条件之一:
    51. //(1)准备加入的key和p指向的Node的结点的key是同一个对象
    52. //(2)p指向的Node结点的key的equals()和准备加入的key比较后相同
    53. //就不能加入
    54. if (p.hash == hash &&
    55. ((k = p.key) == key || (key != null && key.equals(k))))
    56. e = p;
    57. //再判断p是不是一棵红黑树
    58. //如果是一棵红黑树,就调用putTreeVal来进行添加
    59. else if (p instanceof TreeNode)
    60. e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
    61. else {
    62. //如果table对应索引位置,已经是一个链表,就使用for循环比较
    63. //(1)依次和该链表的每一元素比较后,都不相同,就加入到该链表的最后
    64. // 注意在把元素添加到链表后,立即判断该链表是否已经达到8个结点
    65. // 就调用treeifyBin()对当前这个链表进行树化(转成红黑树)
    66. // 注意,在转成红黑树时,要进行判断,判断条件
    67. // if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
    68. // resize();
    69. // 如果上面条件成立,先table扩容
    70. // 只有上面条件不成立时,才进行转成红黑树
    71. //(2)依次和该链表的每一个元素比较过程中,如果有相同情况,就直接break
    72. for (int binCount = 0; ; ++binCount) {
    73. if ((e = p.next) == null) {
    74. p.next = newNode(hash, key, value, null);
    75. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
    76. treeifyBin(tab, hash);
    77. break;
    78. }
    79. if (e.hash == hash &&
    80. ((k = e.key) == key || (key != null && key.equals(k))))
    81. break;
    82. p = e;
    83. }
    84. }
    85. if (e != null) { // existing mapping for key
    86. V oldValue = e.value;
    87. if (!onlyIfAbsent || oldValue == null)
    88. e.value = value;
    89. afterNodeAccess(e);
    90. return oldValue;
    91. }
    92. }
    93. ++modCount;
    94. //size就是我们每加入一个结点Node(k,v,h,next),size++
    95. if (++size > threshold)
    96. resize();
    97. afterNodeInsertion(evict);
    98. return null;
    99. }
    100. */
    101. }
    102. }

    3、练习题:

    (1)

    //我的代码:

    1. package Collection_;
    2. import java.util.HashSet;
    3. import java.util.Iterator;
    4. @SuppressWarnings({"all"})
    5. public class HashSetExercise {
    6. public static void main(String[] args) {
    7. HashSet hashSet = new HashSet();
    8. Employee[] employees = new Employee[4];
    9. employees[0]=new Employee("jack", 18);
    10. employees[1]=new Employee("tom", 19);
    11. employees[2]=new Employee("rose", 20);
    12. employees[3]=new Employee("rose", 20);
    13. hashSet.add(employees[0]);
    14. for (int i = 1; i < employees.length; i++) {
    15. int tmp=0;
    16. Iterator iterator=hashSet.iterator();
    17. while (iterator.hasNext()) {
    18. Object next = iterator.next();
    19. if (employees[i].getAge() == hashSet.hashCode()|| employees[i].getName().equals(hashSet.hashCode())) {
    20. break;
    21. }
    22. }
    23. hashSet.add(employees[i]);
    24. }
    25. for (Object o :hashSet) {
    26. System.out.println(o);
    27. }
    28. }
    29. }
    30. class Employee{
    31. private String name;
    32. private int age;
    33. public Employee(String name, int age) {
    34. this.name = name;
    35. this.age = age;
    36. }
    37. public String getName() {
    38. return name;
    39. }
    40. public void setName(String name) {
    41. this.name = name;
    42. }
    43. public int getAge() {
    44. return age;
    45. }
    46. public void setAge(int age) {
    47. this.age = age;
    48. }
    49. @Override
    50. public String toString() {
    51. return "Employee{" +
    52. "name='" + name + '\'' +
    53. ", age=" + age +
    54. '}';
    55. }
    56. }

     //老师的代码:

    1. package Collection_;
    2. import java.util.HashSet;
    3. import java.util.Objects;
    4. @SuppressWarnings({"all"})
    5. public class HashSetExercise {
    6. public static void main(String[] args) {
    7. HashSet hashSet = new HashSet();
    8. hashSet.add(new Employee("milan",18));
    9. hashSet.add(new Employee("jack",28));
    10. hashSet.add(new Employee("milan",18));
    11. System.out.println("hashSet="+hashSet);
    12. }
    13. }
    14. class Employee{
    15. private String name;
    16. private int age;
    17. public Employee(String name, int age) {
    18. this.name = name;
    19. this.age = age;
    20. }
    21. public String getName() {
    22. return name;
    23. }
    24. public void setName(String name) {
    25. this.name = name;
    26. }
    27. public int getAge() {
    28. return age;
    29. }
    30. public void setAge(int age) {
    31. this.age = age;
    32. }
    33. @Override
    34. public String toString() {
    35. return "Employee{" +
    36. "name='" + name + '\'' +
    37. ", age=" + age +
    38. '}';
    39. }
    40. //重写equals()方法和hashCode()方法:
    41. @Override
    42. public boolean equals(Object o) {
    43. if (this == o) return true;
    44. if (o == null || getClass() != o.getClass()) return false;
    45. Employee employee = (Employee) o;
    46. return age == employee.age && Objects.equals(name, employee.name);
    47. }
    48. @Override
    49. public int hashCode() {
    50. return Objects.hash(name, age);
    51. }
    52. }
    53. //hashSet=[Employee{name='milan', age=18}, Employee{name='jack', age=28}]

    ·重写equals()方法和hashCode()方法:

    alt+insert---->---->下一步---->---->下一步---->

     (2)

     //我的代码

    1. package Collection_;
    2. import java.util.HashSet;
    3. import java.util.Objects;
    4. @SuppressWarnings({"all"})
    5. public class HashSetExercise02 {
    6. public static void main(String[] args) {
    7. HashSet hashSet=new HashSet();
    8. MyDate jack=new MyDate(2000,1,1);
    9. MyDate tom=new MyDate(2001,2,2);
    10. hashSet.add(new Employee("jack",20000,jack));
    11. hashSet.add(new Employee("tom",30000,tom));
    12. hashSet.add(new Employee("jack",20000,jack));
    13. System.out.println(hashSet);
    14. }
    15. }
    16. class Employee{
    17. private String name;
    18. private double sal;
    19. private MyDate birthday;
    20. public Employee(String name, double sal, MyDate birthday) {
    21. this.name = name;
    22. this.sal = sal;
    23. this.birthday = birthday;
    24. }
    25. public String getName() {
    26. return name;
    27. }
    28. public void setName(String name) {
    29. this.name = name;
    30. }
    31. public double getSal() {
    32. return sal;
    33. }
    34. public void setSal(double sal) {
    35. this.sal = sal;
    36. }
    37. public MyDate getBirthday() {
    38. return birthday;
    39. }
    40. public void setBirthday(MyDate birthday) {
    41. this.birthday = birthday;
    42. }
    43. @Override
    44. public String toString() {
    45. return "Employee{" +
    46. "name='" + name + '\'' +
    47. ", sal=" + sal +
    48. ", birthday=" + birthday +
    49. '}';
    50. }
    51. @Override
    52. public boolean equals(Object o) {
    53. if (this == o) return true;
    54. if (o == null || getClass() != o.getClass()) return false;
    55. Employee employee = (Employee) o;
    56. return Objects.equals(name, employee.name) && Objects.equals(birthday, employee.birthday);
    57. }
    58. @Override
    59. public int hashCode() {
    60. return Objects.hash(name, birthday);
    61. }
    62. }
    63. class MyDate{
    64. private int year;
    65. private int month;
    66. private int day;
    67. public MyDate(int year, int month, int day) {
    68. this.year = year;
    69. this.month = month;
    70. this.day = day;
    71. }
    72. public int getYear() {
    73. return year;
    74. }
    75. public void setYear(int year) {
    76. this.year = year;
    77. }
    78. public int getMonth() {
    79. return month;
    80. }
    81. public void setMonth(int month) {
    82. this.month = month;
    83. }
    84. public int getDay() {
    85. return day;
    86. }
    87. public void setDay(int day) {
    88. this.day = day;
    89. }
    90. @Override
    91. public String toString() {
    92. return "MyDate{" +
    93. "year=" + year +
    94. ", month=" + month +
    95. ", day=" + day +
    96. '}';
    97. }
    98. }
    99. //[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底层机制:

    1. package Collection_;
    2. import java.util.LinkedHashSet;
    3. import java.util.Set;
    4. @SuppressWarnings({"all"})
    5. public class LinkedHashSetSource {
    6. public static void main(String[] args) {
    7. Set set=new LinkedHashSet();
    8. set.add(new String("AA"));
    9. set.add(456);
    10. set.add(456);
    11. set.add(new Customer("刘",1001));
    12. set.add(123);
    13. set.add("HSP");
    14. }
    15. }
    16. class Customer{
    17. private String name;
    18. private int no;
    19. public Customer(String name, int no) {
    20. this.name = name;
    21. this.no = no;
    22. }
    23. }

     3、练习题:

     //我的代码:

    1. package Collection_;
    2. import java.util.LinkedHashSet;
    3. import java.util.Objects;
    4. import java.util.Set;
    5. @SuppressWarnings({"all"})
    6. public class LinkedHashSet_ {
    7. public static void main(String[] args) {
    8. Set linkedHashSet=new LinkedHashSet();
    9. linkedHashSet.add(new Car("奥拓",1000));
    10. linkedHashSet.add(new Car("奥迪",300000));
    11. linkedHashSet.add(new Car("法拉利",10000000));
    12. linkedHashSet.add(new Car("保时捷",70000000));
    13. linkedHashSet.add(new Car("奥迪",300000));
    14. System.out.println("linkedHashSet="+linkedHashSet);
    15. }
    16. public static class Car {
    17. private String name;
    18. private double price;
    19. public Car(String name, double price) {
    20. this.name = name;
    21. this.price = price;
    22. }
    23. public String getName() {
    24. return name;
    25. }
    26. public void setName(String name) {
    27. this.name = name;
    28. }
    29. public double getPrice() {
    30. return price;
    31. }
    32. public void setPrice(double price) {
    33. this.price = price;
    34. }
    35. @Override
    36. public String toString() {
    37. return "Car{" +
    38. "name='" + name + '\'' +
    39. ", price=" + price +
    40. '}';
    41. }
    42. @Override
    43. public boolean equals(Object o) {
    44. if (this == o) return true;
    45. if (o == null || getClass() != o.getClass()) return false;
    46. Car car = (Car) o;
    47. return Double.compare(car.price, price) == 0 && Objects.equals(name, car.name);
    48. }
    49. @Override
    50. public int hashCode() {
    51. return Objects.hash(name, price);
    52. }
    53. }
    54. }
    55. //linkedHashSet=[Car{name='奥拓', price=1000.0}, Car{name='奥迪', price=300000.0}, Car{name='法拉利', price=1.0E7}, Car{name='保时捷', price=7.0E7}]

  • 相关阅读:
    LeetCode50天刷题计划第二季(Day 32 — 阶乘后的零(13.20-14.00)
    @SpringBootApplication 注解报红
    软件设计师考试学习1
    如何解决跨区域文件传输存在的安全管控问题?
    Java高级技术之Gradle
    Spring Cloud和Dubbo有哪些区别?
    Nginx缓存
    6235. 逐层排序二叉树所需的最少操作数目
    (shorthand) pixelNeRF: Neural Radiance Fields from One or Few Images
    基于闪电搜索优化的BP神经网络(分类应用) - 附代码
  • 原文地址:https://blog.csdn.net/weixin_72052233/article/details/128031401