Java中List、Set、數據結構、Collections、可變參數

知識點回顧

  • 什麼是迭代器:一個遍歷集合的對象。
  • 如何獲取迭代器:通過集合對象調取iterator方法獲得。
  • 迭代器常用方法:
  1. boolean hasNext():判斷當前指針指向位置是否有下一個元素,有返回true,否則false
  2. E next():先將指針下移指向下一個元素,並返回當前指針指向位置的元素。
  • 迭代器使用注意事項:
  1. hasNext和next方法必須成對出現
  2. 迭代器過程中不能增刪集合中的元素
  • 增強for的作用:JDK1.5新特性,專門用來遍歷集合和數組,本質:迭代器
  • 增強for的格式:for(數據類型  變量名:數組名或集合名){}
  • 什麼是泛型方法:在定義方法時定義了泛型變量的方法
  • 泛型方法的定義格式:修飾符<T> 返回值類型 方法名(參數列表){}
  • 泛型方法注意事項:泛型變量的具體數據類型是在調用方法時根據參數決定。
  • 什麼是泛型類:在定義類時定義了泛型變量的類
  • 泛型類的定義格式:class 類名<T>{}
  • 泛型類的注意事項:
  1. 泛型變量的具體數據類型在創建該類對象時由使用者指定,如果沒有指定泛型變量的具體數據類型,默認Object。
  2. 靜態方法不能使用類上定義的泛型變量,如果要使用泛型變量需要將該方法定義爲泛型方法。
  • 什麼是泛型接口:在定義接口時定義了泛型變量的接口
  • 泛型接口的定義格式:interface  接口名<T> {}
  • 泛型接口的實現方式:
  1. 方式1:實現接口的 同時指定泛型變量的具體數據類型。(不夠靈活)
  2. 方式2:實現接口時不指定泛型變量的具體數據類型,將實現類定義爲泛型類,由使用者創建泛型類對象時指定泛型變量的具體數據類型。
  • 泛型上限格式: ? extend Number;可以接收Number或Number的子類類型的數據
  • 泛型下限格式:? super Integer:可以接收Integer或Integer的父類類型數據
  • 小結:泛型的使用使我們的代碼更加具有通用性,不會導致定義了一種類型之後其他的類型都無法使用該代碼。
  • 知識補充:什麼是泛型:數據類型參數化。
  • 僞泛型:泛型只會存在編譯期,編譯完成之後就會被擦除。

1.1  數據結構有什麼用

  • 當你用着Java裏面的容器類很爽的時候,你有沒有想過,怎麼ArrayList就像一個無限擴充的數組,也好像鏈表之類的。好用嗎?好用,這就是數據結構的用處,只不過你在不知不覺之中使用了。
  • 現實世界的存儲,我們使用的工具和建模。每種數據結構 都有自己的優點和缺點,想想如果Google的數據用的是數組的存儲,我們還能方法地查詢到所需要的數據嗎?而算法,在這麼多的數據中是如何做到最快的插入,查找,刪除,也是在追求更快。
  • 我們的Java是面向對象的語言,就好似自動擋轎車,C語言好似手動擋吉普。數據結構呢?是變速箱的工作原理。你完全可以不知道變速箱怎樣工作,就把自動擋車子從A點開到B點,而且未必就比懂的人慢。寫程序這件事,和開車一樣,經驗可以起到很大的作用,但如果你不知道底層是怎麼工作的,就永遠只會開車,既不會修車,也不能造車。
  • 數據存儲的常用結構有:棧、隊列、數組、鏈表和紅黑樹


1.2 常見的數據結構

  • 棧:先進後出(First In Last FILO)
  • 隊列:先進先出(First In First Out FIFO)
  • 數組
  1. 增刪慢:每次增刪元素時需要創建新的數組,需要複製舊數組到新的數組中。
  2. 查詢快:可以根據索引查詢指定的元素。
  • 鏈表:
  1. 增刪快:每次增刪元素時不需要移動元素的位置,只需要修改上一個元素記住下一個元素的地址值。
  2. 查詢慢:每次根據索引查詢元素時都需要從鏈表頭或鏈表尾部開始遍歷查詢
  • 紅黑樹(瞭解)==>樹 ==>二叉樹

 棧

  • 棧:stack,又稱堆棧,它是運算受限的線性表,其限制是僅允許在標的一端進行插入和刪除操作,不允許在其他位置進行任何查找、刪除等操作。
  • 簡單地說:採用該結構的集合,對元素的存取有如下特點:
  • 先進後出:即存進去的元素,要在它後面的元素依次取出之後,才能取出該元素。例如,子彈壓進彈夾,先壓進去的子彈在下面,後壓進去的子彈在上面,當開槍時,先彈出上面的子彈,然後才能彈出下面的子彈。
  • 棧的入口和出口都是棧的頂端位置

  • 壓棧:就是存元素。即把元素存儲到棧的頂端位置。
  •  彈棧:就是取元素。即,把棧的頂端位置元素取出。

隊列

  •  queue,簡稱隊列,它同堆棧一樣,也是一種運算受限的線性表,其限制是僅允許在表的一端進行插入,而在表的另一端進行刪除。
  • 存取特點:
  1. 先進先出
  2. 隊列的入口和出口各佔一側。例如,下圖中的左側爲入口,右側爲出口。

 數組

  • 數組:Array,是有序的元素序列,數組是在內存中開闢一段連續的空間,並在此空間存放元素。
  • 查找元素快:通過索引,可以快速訪問指定位置的元素。
  • 增刪元素慢:指定索引位置增加元素:需要創建一個新的數組,將指定新元素存儲在指定索引位置,再把原數組元素根據索引,複製到新數組對應的索引位置。
  • 指定索引位置刪除元素:需要創建一個新數組,把原數組元素根據索引,複製到新數組對應的索引位置,原數組中指定索引位置元素不復制到新數組中。

 鏈表

  • 鏈表:linked list,由一系列結點node(鏈表中每一個元素稱爲結點)組成,結點可以在運行時動態形成。每個結點包括兩個部分:一個是存儲數據元素的數據域,另一個是存儲下一個結點的地址的指針域,我們常說的鏈表結構有單向鏈表與雙向鏈表,這裏介紹單向鏈表
  • 多個結點之間,通過地址進行連接。例如,多個人手拉手,每個人使用自己的右手拉住下一個人的左手,依次類推,這樣多個人就連在一起了。

  •  查找元素慢:想查找某個元素,需要通過連接的節點,依次向後查找指定元素
  • 增刪元素快:
  1. 增加元素:只需要修改連接下個元素的地址即可。
  2. 刪除元素:只需要修改連接下個元素的地址即可。

紅黑樹

  •  二叉樹:binary tree,是每個結點不超過2的有序 樹。
  • 簡單理解,就是一種類似於我們生活中的樹,只不過每個結點上都最多隻能有兩個子結點。
  • 二叉樹是每個節點最多有兩個子樹的結構。頂上的叫根結點,兩邊被稱作“左子樹”和“右子樹”。


List集合

  • 2.1 List集合特點:有序(存取順序一致),有索引,元素可重複。

2.1 List接口中特有方法

  • 1.public void add(int index,E element):將元素添加到指定的位置
  • 2.public E  get(int index):根據索引獲得指定位置元素
  • 3.public E remove(int index):刪除指定位置的元素,返回被刪除的元素。
  • 4.boolean  remove(Object O):刪除指定的元素,刪除成功返回true,否則false
  • 5.public E set(int index,E element):將指定位置的元素修改爲指定值。
  • 注意:3和4都是remove方法,在4方法中,刪除指定的元素,刪除成功返回true,否則false。而3方法是通過索引返回被刪除的元素,那麼在輸出的時候如何區分開來呢?例如,在一個例子中可以這樣輸出,System.out.println(list.remove(Integer.valueOf(1)));這樣就不會把1當成索引,而是看成元素

 

import java.util.ArrayList;
import java.util.List;

public class ListDemo01 {
    public static void main(String[] args) {
        //創建集合對象
        List<Integer>  list=new ArrayList<>();

        //添加元素
        list.add(1);
        list.add(2);
        list.add(3);
        System.out.println(list);//[1, 2, 3]

        //將元素4添加到索引爲2的位置
        list.add(2,4);
        System.out.println(list);//[1, 2, 4, 3]

        //根據索引獲得指定位置的元素
        System.out.println(list.get(2));//4

        //刪除指定位置的元素
        System.out.println(list.remove(0));//1

        //將指定位置的元素修改爲指定值
        list.set(1,10);
        System.out.println(list);//[2, 10, 3]

        //刪除指定的元素,刪除成功返回true,否則返回false
        System.out.println(list.remove(Integer.valueOf(10)));//true
    }
}

2.3 List接口常用子類

2.3.1 ArrayList集合

  • ArrayList集合特點:
  1. 有序(存取順序一致),有索引,元素可重複
  2. 底層結構是數組:增刪慢,查詢快( 有指定索引),線程不安全,效率高。
import java.util.ArrayList;

public class ArrayListDemo01 {
    public static void main(String[] args) {
        //創建集合對象
        ArrayList<String> list=new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        list.add("dd");
        System.out.println(list);//[aa, bb, cc, dd]
    }
}
  •  第一次添加元素的時候(如上面list.add("aa"))纔會創建長度爲10的數組,可以通過源碼分析出來,因爲如果有很多個集合,假如先不存儲元素,先創建長度會極大地浪費空間

 2.3.2 LinkedList集合

LinKedList集合特點

  • 有序(存取順序一致),有索引,元素可重複。
  • 底層結構是鏈表:增刪快,查詢慢,線程不安全,效率高。

LinkedList集合特有的方法

  • public void addFirst(E e):將元素添加鏈表頭
  • publiv void addLast(E e):將元素添加鏈表尾
  • public E getFirst():獲得鏈表頭元素
  • public E getLast():獲得鏈表尾元素
  • public E removeFirst():刪除鏈表頭元素,返回被刪除的元素
  • public E removelast():刪除鏈表尾元素,返回被刪除的元素
import java.util.LinkedList;

public class LinkedListDemo01 {
    public static void main(String[] args) {
        //創建LinkedList集合
        LinkedList<String> list=new LinkedList<>();

        //添加元素
        list.add("aa");
        list.add("bb");
        list.add("cc");

        //根據索引獲取元素
        System.out.println(list.get(0));//aa

        //將元素添加到鏈表頭
        list.addFirst("dd");
        System.out.println(list);//[dd, aa, bb, cc]

        //將元素添加到鏈表尾
        list.addLast("ee");
        System.out.println(list);//[dd, aa, bb, cc, ee]

        //獲得鏈表頭元素
        System.out.println(list.getFirst());//dd

        //獲得鏈表尾元素
        System.out.println(list.getLast());//ee

        //刪除鏈表頭元素
        list.removeFirst();
        System.out.println(list);//[aa, bb, cc, ee]

        //刪除鏈表尾元素
        list.removeLast();
        System.out.println(list);//[aa, bb, cc]
    }
}
  •  注意:鏈表根據索引獲得元素從鏈表頭還是鏈表尾部開始遍歷查詢
  1. 當索引小於元素個數的一半, 則從鏈表頭開始遍歷查詢,提高效率。
  2. 當索引大於等於集合元素個數的一半,則從鏈表尾部開始遍歷查詢。

ArrayList和LinkedList的選擇(依據底層結構特點去選擇)

  1. 如果不需要執行增刪操作,則選擇ArrayList
  2. 如果需要執行大量的增刪操作,則選擇LinkedList

3.1 set接口介紹

  •  set集合的特點:無序(存取順序不一致),無索引,元素不可重複。
  • set集合常用子類:
  1. HashSet
  2. LinkedHashSet
  • Set集合遍歷方式:(因爲沒有索引,不能通過普通for方法遍歷集合)
  1. 增強for
  2. 迭代器

3.2 對象的哈希值概述 

  • 什麼是對象哈希值?
  1. 哈希值就是一個十進制的整數,每一個對象都會有一個對應的哈希值。
  2. 哈希值默認是對象在內存中的地址值。
  • 如何獲得對象的哈希值?
  1. 通過對象調用hashCode方法獲得。(通過查API可得:hashCode方法在Object類中)
  • 哈希值的作用?
  1. 哈希值是對象存儲到哈希表的重要依據。
  • 在實際開發中,自定義對象一般都會重寫hashCode方法,目的是讓對象的哈希值和地址沒有關係,一般都是通風成員變量來計算哈希值。
  •  字符串哈希值小結:
  1. 字符串內容相同,哈希值一定相同。
  2. 字符串內容不同,哈希值也可能相同。
  • 示例代碼1: 
public class HashCodeDemo01 {
    public static void main(String[] args) {
        //創建字符串
        String s1="abc";
        String s2="abc";

        //獲取字符串的哈希值
        System.out.println(s1.hashCode());//96354
        System.out.println(s2.hashCode());//96354

        String s3="Aa";
        String s4="BB";

        //獲得字符串的哈希值
        System.out.println(s3.hashCode());//2112
        System.out.println(s4.hashCode());//2112
        
    }
}
  •  字符串哈希值小結
  1. 字符串內容相同,哈希值一定相同。(因爲底層算法一樣)
  2. 字符串內容不同,哈希值也可能相同。 如上面示例代碼中的"Aa"和“BB”(底層算法的原因)
  • 擴展
  • public native int hashCode(); 
  • native修飾的方法,依賴操作系統的底層代碼,不同的操作系統結果是不同的,而不是源碼的原因。
import java.util.Objects;

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

    public Student() {
    }

    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 String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {

        return Objects.hash(name, age);
    }
}

 

public class StudentDemo {
    public static void main(String[] args) {
        //創建學生對象
        Student stu1=new Student("Jack",23);
        Student stu2=new Student("Jack",23);

        //沒有重寫hashCode方法之前,獲得對象的哈希值並輸出
        System.out.println(stu1.hashCode());//1967205423
        System.out.println(stu2.hashCode());//42121758

        //重寫了hashCode方法之後,獲得對象的哈希值並輸出
        System.out.println(stu1.hashCode());//71329721
        System.out.println(stu2.hashCode());//71329721
    }
}
  •  從輸出結果可以看出,當對象沒有重寫hashCode方法時,不同對象的哈希值是不同的,當重寫了hashCode方法之後,只要兩個對象的成員變量值相同,它們的哈希值就相同了。

 3.2 HashSet集合

HashSet集合特點

  • HashCode集合的特點?
  1. 無序(存取順序不一致),無索引,元素不可重複。
  2. 底層結構是哈希表。
  • 什麼是哈希表?
  1. JDK1.8之前:哈希表是數組和鏈表的結合體
  2. JDK1.8之後,哈希表是數組+鏈表+紅黑樹的結合體
import java.util.HashSet;

public class hashSetDemo01 {
    public static void main(String[] args) {
        //創建Set集合對象
        HashSet<String> set=new HashSet<>();
        //添加元素
        set.add("abc");
        set.add("bcd");
        set.add("XXX");
        set.add(new String("abc"));

        System.out.println(set);//[bcd, abc, XXX]
    }
}

 


3.3 哈希表存儲元素過程分析

3.3.1 什麼是哈希表

  • 在JDK1.8 之前,哈希表是數組+鏈表
  • 在JDK1.8之後,哈希表是數組+鏈表+紅黑樹。當鏈表長度大於等於8時,將鏈表轉換爲紅黑樹,目的是爲了提高查詢效率。
  • 簡單的來說,哈希表是由數組+鏈表+紅黑樹(JDK1.8增加了紅黑樹部分)實現的,如下圖所示:

3.3.2 哈希表的存儲過程

  • 每存入一個新的元素都要走以下五步:
  1. 調用對象的hashCode()方法,獲得要存儲元素的哈希值。
  2. 將哈希值與表的長度(即數組的長度)進行求餘運算得到一個整數值,該值就是新元素要存放的位置(即使索引值)。
  • 如果索引值對應的位置上沒有存儲任何元素,則直接將元素存儲到該位置上。
  • 如果索引值對應的位置上已經存儲了元素,則執行第2步。

    3.遍歷該位置上的所有舊元素,依次比較每個舊元素的哈希值和新元素的哈希值是否相同。

  • 如果有哈希值相同的舊元素,則執行第4步。
  •  如果沒有哈希值相同的舊元素,則執行第5步。

   4.比較新元素和舊元素的地址是否相同

  • 如果地址值相同則用新的元素替換老的元素。停止比較。
  • 如果地址值不同,則新元素調用equals方法與舊元素比較內容是否相同。
  • 如果相同返回true,用新的元素替換老的元素,停止比較。
  • 如果返回false,則回到第3步繼續遍歷下一個舊元素。

  5.說明沒有重複,則將新元素存放到該位置上並讓新元素記住之前該位置的元素。

  • 總結哈希表的存儲過程:
  • 1.先比較哈希值,如果相同,再比較地址值,如果不同,則比較內容(內容相同則不存,內容不同意味着不同的元素,則需要存)
  • 2.先比較哈希值,如果相同,再比較地址值,如果相同,則不比較了,說明內容相同則不存。 
  •  HashSet存儲自定義對象
  • 需求如下:定義一個學生類,包含姓名和年齡兩個成員變量。
  • 在測試類中創建多個學生對象存儲到HashSet集合中
  • 要求相同姓名和年齡的學生對象只存儲一個。

代碼實現

import java.util.HashSet;

/*
    哈希表存儲自定義對象
        存儲學生對象,如果姓名和年齡相同只存儲一個
    哈希表存儲元素底層依賴元素的hashCode和equals方法
 */
public class hashSetDemo02 {
    public static void main(String[] args) {
        //創建set集合
        HashSet<Student> set=new HashSet<>();

        set.add(new Student("jack",20));
        set.add(new Student("rose",22));
        set.add(new Student("lily",24));
        set.add(new Student("jack",20));

        for (Student student : set) {
            System.out.println(student);
        }
        /*
            Student{name='rose', age=22}
            Student{name='jack', age=20}
            Student{name='lily', age=24}
         */
    }
}
  •  HashSet存儲元素的過程是根據對象的哈希值來確定元素在集合中的存儲位置,底層主要依賴於元素的:hasCode與equals方法。所有當存儲自定義對象時,如果希望兩個對象成員變量值都相同只存儲一個,則需要在自定義類中重寫hashCode和equals方法。(hashCode具有去重效果

 

import java.util.Objects;

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

    public Student() {
    }

    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 boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {

        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
import java.util.HashSet;

public class HashSetDemo02 {
    public static void main(String[] args) {
        //創建集合對象,該對象存儲Student類型
        HashSet<Student> set=new HashSet<Student>();

        set.add(new Student("張國榮",18));
        set.add(new Student("張國榮",18));
        set.add(new Student("王菲",20));
        set.add(new Student("成龍",32));

        for(Student stu:set){
            System.out.println(stu);
        }
    }
}
/*
    Student{name='張國榮', age=18}
    Student{name='成龍', age=32}
    Student{name='王菲', age=20}
 */

3.4 LinkedHashSet集合

LinkedHashSet集合的特點

  • LinkedHashSet集合特點:
  1. 繼承HashSet,能夠保證存取順序一致。
  2. 底層結構是:哈希表+鏈表。
  • HashSet和LinkedHashSet選擇:如果要求存取順序一致,則選LinkedHashSet,否則推薦HashSet。
import java.util.LinkedHashSet;

public class LinkedHashSetDemo01 {
    public static void main(String[] args) {
        LinkedHashSet<String> linkedHashSet=new LinkedHashSet<>();

        linkedHashSet.add("sdfsf");
        linkedHashSet.add("dfsfds");
        linkedHashSet.add("qweqwfsd");
        System.out.println(linkedHashSet);//有序[sdfsf, dfsfds, qweqwfsd]
    }
}

 3.5 單列集合小結

  • List:有序,有索引,元素可重複。
  1. ArrayList:底層結構:數組、增刪慢,查詢快,線程不安全,效率高。
  2. LinkedList:底層結構:鏈表,增刪快,查詢慢,線程不安全,效率高。
  • Set:無序,無索引,元素不可重複。
  1. HashSet:底層結構:哈希表:數組+鏈表+紅黑樹,線程不安全,效率高。
  2. LinkedHashSet:底層結構:哈希表+鏈表,能夠保證存取有序,線程不安全,效率高。
  • 單列集合的選擇
  • 如果要求元素可重複,則在List體系下選擇
  1. 如果只執行查詢操作,則選擇ArrayList
  2. 如果要執行增刪操作,則選擇LinkedList
  • 如果要求元素不可重複,則在Set體系下選擇
  1. 如果要求存取順序一致,則選擇LinkHashSet,否則選擇HashSet

4.可變參數概述

  • 概述:JDK 1.5新特性,參數個數可以是任意個(0到n個)
  1. 格式:數據類型...變量名
  2. 本質:數組 
  • 可變參數注意事項:方法中最多隻能有一個可變參數且必須出現在參數列表最後面。
public class Demo01 {
    public static void main(String[] args) {
        System.out.println(sum(1));
        System.out.println(sum(1,34,45,34,1,343,54,23));
        System.out.println(sum(23,34,23,4,5,23,23,23));

    }


    /*
        原始寫法:求兩個數之和、求三個數之和
     */
   /* public static int sum(int a,int b){
        int sum = a+b;
        return sum;
    }

    public static int sum(int a,int b,int c){
        int sum = a+b+c;
        return sum;
    }*/

    /*
        可變參數寫法
     */
    public static int sum(int a,int...arr){
        //定義求和變量
        int result=0;
        //遍歷數組
        for(int i:arr){
            result += i;
        }
        return result;
    }
}

 


5.1 Collections工具類

  • 常用方法
  1. public static <T> boolean addAll(Collection<T> c,T...elements):往集合中添加一些元素。
  2. public static void shuffle(List<?> list)打亂順序:打亂集合順序。
  3. public static <T> void sort(List<T>list):將集合中元素按照默認規則排序。
  4. public static <T> void sort(List<T> list,Comparator<? super T>c):使用自定義比較器對集合中的元素進行排序。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class CollectionsDemo {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        /*//原來寫法
        list.add(12);
        list.add(43);
        list.add(3245);*/
        //採用工具類 完成往集合中添加元素
        Collections.addAll(list, 12, 43, 3245);
        System.out.println(list);//[12, 43, 3245]

        //對集合元素亂序
        Collections.shuffle(list);
        System.out.println(list);//[43, 3245, 12]

        //排序方法:升序
        Collections.sort(list);
        System.out.println(list);//[12, 43, 3245]

        //使用自定義比較器排序:降序
        Collections.sort(list,new MyComparator());
        System.out.println(list);//[3245, 43, 12]
    }
    /*
        自定義比較器
     */
    static class MyComparator implements Comparator<Integer>{

        @Override
        public int compare(Integer o1, Integer o2) {
            return o2-o1;
        }
    }
}

 


5.2 Comparator比較器

  • public static <T> void sort(List<T> list):將集合中的元素按照默認規則排序。
  • 我們使用的是默認規則完成整型數據的排序,那麼默認規則是怎麼定義出來的呢?
  1. 說到排序,簡單來說就是兩個對象之間比較大小,那麼Java中提供了兩個比較實現的方式,一種是比較死板的採用java.lang.Comparable接口去實現,一種靈活的當我需要做排序的時候再去選擇的java.util.Comparator接口完成。
  2. 那麼我們採用的public static <T> void sort(List<T> list)這個方法完成的排序,實際上要求了被排序的類型需要實現Comparable接口完成比較的功能,在Integer類型上爲:
  • public final class Integer extends Number implements Comparable<Integer>{}
  • Integer類實現了這個接口,並完成了比較規則的定義,但是這樣就把規則寫死了,那比如我想要整型數據按照從大到小排序呢?那麼這樣就要修改String的源代碼,這是不可能的了,那麼我們這個時候我們可以使用:
  • public static <T> void sort(List<T> list,Comparator<? super T>)方法靈活的完成,這裏面就涉及到了Comparator這個接口,位於java.util包下,排序是Comparator能實現的功能之一,該接口代表一個比較器,比較器具有可比性,顧名思義就是做排序的,那麼比較的方法就是:
  • public int compare(String 01,String o2):比較兩個參數的順序。
  • 兩個對象比較的結果有三種:大於,等於,小於。
  • 如果要按照升序排序,則O1<O2,返回(負數),相等返回0,O1>O2返回正數。
  • 如果要 降序進行排序,O1<O2,返回正數,相等返回0,O1>O2返回負數。
import java.util.Comparator;
    /*
       自定義比較器
    */
class MyComparator implements Comparator<Integer> {

        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
       }
}



import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class CollectionsDemo01 {
    public static void main(String[] args) {
        ArrayList<Integer> list=new ArrayList<Integer>();
        //採用工具類 完成往集合中添加元素
        Collections.addAll(list,5,1000,43,23,2);
        //使用自定義比較器排序:降序
        Collections.sort(list,new MyComparator());
        System.out.println(list);//[1000, 43, 23, 5, 2]
    }
}

 示例代碼

/*
    創建一個學生類,存儲到ArrayList集合中完成指定排序操作。
 */
public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student() {
    }

    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 String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        return o.age-this.age;//降序
    }
}




import java.util.ArrayList;

public class Demo {
    public static void main(String[] args) {
        //創建4個學生對象,存儲到集合中
        ArrayList<Student> list=new ArrayList<Student>();

        list.add(new Student("rose",18));
        list.add(new Student("jack",16));
        list.add(new Student("abc",16));
        list.add(new Student("ace",17));
        list.add(new Student("mark",16));

        //讓學生按照年齡降序排列
        for(Student student:list){
            System.out.println(student);
        }

    }
}

自定義比較器(採取匿名內部類): 

import java.util.Collections;

/*
    創建一個學生類,存儲到ArrayList集合中完成指定排序操作。
 */
public class Student {
    private String name;
    private int age;

    public Student() {
    }

    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 String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    /*@Override
    public int compareTo(Student o) {
        return o.age-this.age;//降序
    }*/
}



import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Demo {
    public static void main(String[] args) {
        //創建4個學生對象,存儲到集合中
        ArrayList<Student> list=new ArrayList<Student>();

        list.add(new Student("rose",18));
        list.add(new Student("jack",16));
        list.add(new Student("abc",16));
        list.add(new Student("ace",17));
        list.add(new Student("mark",16));

       /*
        如果在使用的時候,想要獨立的定義規則,可以採用Collections.sort(List list,Comparetor c)方式
        自己定義規則(匿名內部類方式)
     */
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o2.getAge()-o1.getAge();//以學生的年齡降序
            }
        });
        System.out.println(list);

    }
}
  • Comparable與Comparator區別
  1. Comparable與Comparator都是Java的接口,用來定義排序規則
  2. Comparable是將排序規則定義在類內部,如果想換一種比較規則,那麼就必須修改內部的代碼。
  3. Comparator是將排序規則定義在類外部,對List排序時必須同時傳入數據和比較器,
  • 如Collections.sort(list,new MyComparator());
  • 如果想換一種比較規則,則需要修改比較器MyComparator,而類內部代碼不需要改變。
  •  Comparable與Comparator選擇:
  1. 如果排序規則有且只有一種且規定不變,則推薦使用Comparable接口。
  2. 如果排序規則不止一種,則推薦使用Comparator接口。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章