Set、Map瑣碎

集合框架能適應數據變化的需求,長度可以改變,集合只能是引用類型,常用的集合類:

Collection
        |--List
            |--ArrayList
            |--LinkedList
        |--Set
            |--HashSet
            |--LinkedHashSet
            |--TreeSet

HashSet

在List的集合中可以存儲重復的值,Set集合正好相反,不能存儲相同的值。
HashSet的add()方法依賴HashMap的put()方法。
在put()方法中會依賴 hashCode()和equals()這兩個方法。
所以add()在添加元素時會走的邏輯是:

先看hashCode()值是否相同
    相同:繼續走equals()方法
         返回true:說明元素重復,就不添加
         返回false:說明元素不重復,就添加到集合
     不同:就直接把元素添加到集合

add()方法的底層操作第一步比較hash值的時候是相同的,那下面每新添加一個元素時,都要和目前集合中的元素進行equals()的比較,這樣效率低下。另外,返回hash值一樣,這些添加的元素會在哈希表的同一個位置,保存方式類似一個鏈表的結構。

如果類沒有重寫這兩個方法,默認使用的Object的。
String類重寫了hashCode()和equals()方法,Set集合在添加String類型的元素時,它就可以把內容相同的字符串去掉,只留下一個。

public class Student {
    private String name;
    private int age;

    public Student(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;
    }

   // 保證對象的hashCode()值不一樣
    @Override
    public int hashCode() {  //自動生成
        final int prime = 31;
        int result = 1;
        result = prime * result + age; //基本類型 +值
        // 引用類型, 加哈希值
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
            return true;
         }
         if (!(obj instanceof Student)) {
            return false;
         }
         Student s = (Student) obj;
         return this.name.equals(s.name) && this.age == s.age;
     }
    
     @Override
     public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
     }
    

}

上面的Student類重寫了equals()和hashCode()方法。
在使用Set時:

    HashSet<Student> hs = new HashSet<Student>();
    Student s1 = new Student("jiaobuchong", 22);
    hs.add(s1);

保證不會添加進去重復的Student類。

LinkedHashSet

底層數據結構由哈希表和鏈表組成。
哈希表保證元素的唯一, 鏈表保證元素有序, 存儲和取出一致.

TreeSet

元素會排好序,并唯一。
TreeSet的add()方法依賴于TreeMap()的put()方法。底層數據結構是紅黑樹(是一個自平衡的二叉樹)。根據比較的返回值是否是0來決定元素的唯一性。

  • 排序方法
  • 自然排序(按自然數從小到大的排序)
  • 自定義排序
自定義排序
  • 實現Comparable接口
public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student(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 int compareTo(Student s) {
        // return 0;   只加入了一個元素,程序認為加入的都是同一個元素,所以只add了一個元素
        // return 1;   表示后面添加的元素比前面的大
        // return -1;  表示后面添加的元素比前面的小

        // 按照年齡排序,主要條件
        int num = this.age - s.age;
        // 次要條件
        // 年齡相同的時候,還得去看姓名是否也相同
        // 如果年齡和姓名都相同,才是同一個元素
        int num1 = (num == 0) ? this.name.compareTo(s.name) : num;
        return num1;
    }
}

//--------------------------------------------------
   TreeSet<Student> ts = new TreeSet<>();
   Student s1 = new Student("jiaobuchong", 22);
   Student s2 = new Student("jack", 23);
   ts.add(s1);
   ts.add(s2);

ts中的Student元素按照年齡排序,通過compareTo()方法比較返回值是0的元素不會添加到TreeSet中。

  • 自定義比較器 Comparator

      // 匿名內部內靈活性較高
          TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {    //new 后面是類名或接口名,使用一次,這種實現較好
              @Override
              public int compare(Student s1, Student s2) {
                  // 姓名長度
                  int num = s1.getName().length() - s2.getName().length();
                  // 姓名內容
                  int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
                          : num;
                  // 年齡
                  int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
                  return num3;
              }
          });
          
          // 使用lambda表達式
            TreeSet<Student2> ts = new TreeSet<>((o1, o2) -> {
              int num = o1.getName().length() - o2.getName().length();
              // 姓名的長度相同,不代表姓名的內容相同
              int num2 = num == 0 ? o1.getName().compareTo(o2.getName()) : num;
              // 姓名的長度和內容相同,不代表年齡相同,所以還得繼續判斷年齡
              int num3 = num2 == 0 ? o1.getAge() - o2.getAge() : num2;
              return num3;
          });
          
    

使用操作集合的工具類Collections排序:

// list是一個保存Student對象的List,傳一個比較器Comparator
// 可以使用lambda表達式
Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int num = s2.getAge() - s1.getAge();
                int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
                        : num;
                return num2;
            }
        });

Map

Map常用的類:

    Map
     |--HashMap
     |--TreeMap
     |--LinkedHashMap

Map集合的數據結構僅僅針對鍵有效,與值無關。
存儲的是鍵值對形式的元素,鍵唯一,值可重復。

HashMap

底層數據結構是哈希表,線程不安全的,效率高
哈希表依賴兩個方法:HashCode()和equals()方法
執行順序和(前面敘述的Set集合是依賴于Map的):

首先判斷hashCode()值是否相同
    是:繼續執行equals(),看其返回值
        true:說明元素重復,不添加
        false:就直接添加到集合
    否:直接添加到集合中
    
    // 一般做法:自動生成這兩個方法即可
LinkedHashMap
  • 底層數據結構是鏈表和哈希表組成。
  • 鏈表保證存儲有序
  • 哈希表保證元素唯一性
TreeMap

底層數據結構是紅黑樹(是一種自平衡的二叉樹)

  • 如何保證元素唯一性?
  • 根據比較的返回值是否是0來決定
  • 如何保證元素排序?
    • 兩種方式
    • 自然排序(元素局部比較性)
      • 讓元素所屬的類實現Comparable接口
    • 比較器排序(集合具備比較性)
      * 讓集合接收一個Comparator比較器對象
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Java集合框架結構圖完整版 在完整版的結構圖中Collection集合和Map下有許多未實現的抽象類(Ab...
    LizGbus8閱讀 892評論 0 3
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,759評論 18 399
  • [TOC] 集合練習 集合對象的toString()是如何實現 關于System.out.println(c);為...
    lutianfei閱讀 606評論 0 1
  • 背景 一年多以前我在知乎上答了有關LeetCode的問題, 分享了一些自己做題目的經驗。 張土汪:刷leetcod...
    土汪閱讀 12,768評論 0 33
  • 已經很久沒有在夜里醒來過了,時間有多長也已經記不得了。困到極致,常常是一秒入睡,不留半分躊躇。很多時候夜晚如同...
    風起時想你你閱讀 158評論 0 0