Java集合框架

集合框架

程序開發并不是解決了業務的基本功能就完成了,很多時候程序運行的環境是有限制的。比如內存小,CPU頻率低,或者是像手機這樣的設備,能源供應有限。在這種環境下,就需要程序能夠在有限的環境中提升效率。這就需要使用數據結構和算法。
但是數據結構與算法即便是學過,也未必在工作時能夠用好,而且通用性、性能等等也都是問題。加上學習程序開發的受眾群體越來越廣,讓程序員全部自己實現數據結構與算法不是一個好的主意。所以現在很多語言為了能夠提升學習效率,降低學習門檻,同時也為了讓程序有更好的執行效率和通用性,就自帶了各種實現了數據結構與算法的API集合。在Java中,這就是我們現在要學習的「集合框架」與現在常見到的數據結構類庫一樣,Java也是將集合類庫的接口(interface)與實現(implementation)分離。所以我們的學習方式一般都是先搞明白接口的分類和關系,然后根據不同的接口來學習對應的實現類。

使用集合做什么

  1. 搬運數據,集合可以存儲數據,然后通過API調用很方便就可以傳遞大量數據
  2. 數據處理,集合中可以直接對數據進行操作,比如統計、去重
  3. 排序,可以將數據按照需求進行各種排序,然后再傳遞給調用者


    image-20200212141927241.png

    Java的集合從 Collection 接口和 Map 接口入手

Map 接口和 Collection 沒有交集,它有自己的方式,只要標準庫后綴不是Map 結尾的,都是直接或者間接實現了Collection接口。

Collection 接口中常見的操作是數據的添加、刪除

  1. add / addAll
  2. remove / removeAll / removeIf

借助 Iterator 接口,Collection 還具備了數據的循環

public interface Collection<E> extends Iterable<E>{ 
    //... 
    //  對數據循環
    Iterator<E> iterator();
}

通過 Iterable 接口, 標準庫中的集合都可以使用 forEach 循環。

具體的實現類

集合類型 描述
ArrayList 一種可以動態增長和縮減的索引序列
LinkedList 一種可以在任何位置進行高效地插人和刪除操作的有序序列
ArrayDeque 一種用循環數組實現的雙端隊列
HashSet 一種沒有重復元素的無序集合
TreeSet 一種有序集
EnumSet 一種包含枚舉類型值的集
LinkedHashSet 一種可以記住元素插人次序的集
PriorityQueue 一種允許高效刪除最小元素的集合
HashMap 一種存儲鍵/ 值關聯的數據結構
TreeMap 一種鍵值有序排列的映射表
EnumMap 一種鍵值屬于枚舉類型的映射表
LinkedHashMap 一種可以記住腱/ 值項添加次序的映射表
WeakHashMap 一種其值無用武之地后可以被垃圾回收器回收的映射表
IdentityHashMap 一種用 == 而不是用equals 比較鍵值的映射表

Collection

image-20200212145950872.png

Map
image-20200212150240334.png

雖然類很多 ,但是我們在教授中只需要交幾個類就可以了,分別是下面:

  1. ArrayList
  2. LinkedList
  3. HashSet
  4. HashMap
  5. TreeMap

然后加上工具類2個:

  1. Collections
  2. Arrays

List

有序集合,可以精確控制列表中每個元素的插入位置。通過整數索引獲取列表中的元素。List允許出現重復的值 , 并可以精確控制列表中每個元素的插入位置,通過整數索引獲取列表中的元素。

方法名 說明
add(E e) 增加單個數據
addAll(Collection<? extends E> c) 將一個 Collection 集合的數據添加到現在的集合中
remove(Object o) 刪除指定的元素
contains(Object o) 判斷集合是否包含指定的元素
size() 得到集合的數據總數
isEmpty() 判斷集合是否有數據
get(int index) 通過索引獲取對應的數據元素
set(int index, E element) 通過索引和新的元素替換原有內容
clear() 清空數據
toArray() 將List轉為對象數組

ArrayList

ArrayList 是List 接口的大小可變數組的實現。實現了所有可選列表操作, 并允許包括null 在內的所有元素。除了實現List 接口外, 此類還提供一些方法來操作內部用來存儲列表的數組的大小( 此類大致上等同于 vector 類, 但 vector 是同步的) 。

ArrayList 的底層是使用數組實現的,看下面的圖
image-20200212171434134.png

可以看到,數組中的每一個元素,都存儲在內存單元中,并且元素之間緊密排列,既不能打亂元素的存儲順序,也不能跳過某個存儲單元進行存儲。

ArrayList 底層既然是使用數組實現,那么特點就和數組一致:查詢速度快,增刪速度慢

每個ArrayList 實例都有一個容量。該容量是指用來存儲列表元素的數組的大小, 它總是至少等于列表的大小。隨著向Array L ist 中小斷添加元素, 其容量也自動增長。并未指定增長策略的細節,因為這不只是添加元素會帶來分攤固定時間開銷那樣簡單。我們可以使用默認構造函數創建容量為 10 的列表, 也可以初始化指定容量大小。

ArrayList 指定初始容量大小的構造器方法

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    }
}

常用的操作

public static void main(String[] args) {

    ArrayList<Integer> list = new ArrayList<>();

    //  添加數據
    list.add(123);
    list.add(346);

    //  替換數據
    list.set(1, 777);

    //  將list中的所有數據放到 list2 中
    List<Integer> list2 = new ArrayList<>();
    list2.addAll( list );

    //  循環list2中所有的數據
    for (Integer integer : list2) {

        System.out.println( integer );

        //  刪除循環出的對象
        list2.remove(integer);
    }

    //  list 集合是否有數據
    if( !list.isEmpty() ) {

        System.out.println("list.size = "+ list.size());

        //  清空 list
        list.clear();
    }

    //  在清空list后,再看list現在有多少數據
    System.out.println("list.size = "+ list.size());
}

    DEFAULT_CAPACITY = 10   默認容量 = 10  初始化容量
    

LinkedList

LinkedList 是一個鏈表結構,可當作堆棧、隊列、雙端隊列 。鏈表是一種在物理上非連續、非順序的數據結構,由若干節點(node)所組成。

單鏈表

image-20200212173413313.png

單向鏈表的每一個節點包含兩部分,一部分是存放數據的變量data,另一部分是指向下一個節點的指針next。正如地下黨的聯絡方式,一級一級,單線傳遞:

private static class Node {
    int data;
    Node next;
}

雙鏈表

雙向鏈表比單向鏈表稍微復雜一些,它的每一個節點除了擁有data和next指針,還擁有指向前置節點的prev指針

image-20200212173517719.png

和數組不同,鏈表存儲數據的時候,則采用了見縫插針的方式,鏈表的每一個節點分布在內存的不同位置,依靠next指針關聯起來。這樣可以靈活有效地利用零散的碎片空間。
image-20200212173548104.png

LinkedList 做數據的添加和刪除操作時速度很快,但是做查詢的時候速度就很慢,這和 ArrayList 剛好相反。

適用 LinkedList 獨有的方法,在集合前后做數據插入

public static void main(String[] args) {

    LinkedList<Integer> list = new LinkedList<>();

    list.add(123);
    //  添加相同的數據
    list.add(123);
    list.add(456);
    list.add(789);
    list.add(101);

    //  將數據 111 添加到 list 的末尾
    list.addLast(111);
    //  將數據 999 添加到 list的最前面
    list.addFirst(999);

    for (int i = 0; i < list.size(); i++) {
        System.out.println( list.get(i) );
    }

}

注意代碼中聲明和實現都是 LinkedList , 如果聲明的是 List 接口 , addFirst 和 addLast 方法是不可見的

Set

Set 的特點是去重,如果相同的數據只會保留一個。集合內的數據是一個無序的狀態,。當然也有例外,TreeSet就是有序的。

方法名 說明
add(E e) 增加單個數據
addAll(Collection<? extends E> c) 將一個 Collection 集合的數據添加到現在的集合中
remove(Object o) 刪除指定的元素
contains(Object o) 判斷集合是否包含指定的元素
size() 得到集合的數據總數
isEmpty() 判斷集合是否有數據
clear() 清空數據
toArray() 將List轉為對象數組

可以看到,Set 和 List 中的方法都是相同的,作用也一致。但是 Set 是沒有直接獲取數據的方法。我們更多的時候使用的是 List 和 Map。

Has hSet 由哈希表( 實際上是一個HashM ap 實例) 支持, 為基木操作提供了穩定性能, 這些基本操作包括add() 、remove() 、contains() 和size() , 假定哈希函數將這些元素正確地分布在桶中。對此 set() 進行迭代所需的時間與HashSet 實例的大小( 元素的數量) 和底層HashMap 實例( 桶的數量)的“ 容量” 的和成比例。因此, 如果迭代性能很重要, 則不要將初始容量沒置得太高( 或將加載因了設置得太低) 。

public static void main(String[] args) {

    HashSet set = new HashSet<>();
        
    set.add( new Object() );
    set.add( new Object() );
    set.add( "hello" );
    set.add( "hello" );
    set.add( "你好" );
    set.add( 123 );
    set.add( 123 );
    set.add( 234 );
    set.remove("你好");
    set.remove( new Object() );
    for (Object object : set) {
        System.out.println( object );
    }
}

以上代碼經輸出可以看到:輸出的內容沒有按照輸入時的順序進行輸出。

李四
8890
張三
azxcv
ABC

TreeSet

Set 集合中的另類,存儲的數據時有序的

public static void main(String[] args) {

    TreeSet<String> set = new TreeSet<>();
    set.add("8890");
    set.add("123");
    set.add("張三");
    set.add("李四");
    set.add("ABC");
    set.add("azxcv");

    for (String string : set) {
        System.out.println( string );
    }
}

輸出的結果:

123
8890
ABC
azxcv
張三
李四

可以發現數據是按照數字、大寫字母、小寫字母、漢字進行排序的,但是漢字相關的數據本身沒有排序,可以嘗試將李四放在張三的數據上面看輸出的結果。

Map

Map 是一種把鍵對象和值對象進行關聯的容器, 而一個值對象又可以是一個Map, 依次類推,這樣就可形成一個多級映射。

想想學習英語使用的詞典軟件,輸入英文(key)后,軟件會顯示出對應的中文(value)。Map就是在內存中的這種結構。

Key(鍵):

  1. 和 set— 樣,鍵對象不允許重復,這是為了保持查找結果的一致性。 如果有兩個鍵對象一樣, 那你想得到那個鍵對象所對應的值對象時就有問題了。
  2. 在使用過程中某個鍵所對應的值對象可能會發生變化, 這時會按照最后一次修改的值對象與鍵對應(就是key同一個key有多次值綁定,最后一個就會覆蓋之前的)
  3. 可以使用 null 作為 Key

Value(值):

  1. 值對象沒有唯一性的要求, 你可以將任意多個鍵都映射到一個值對象上, 這不會發生任何問題( 不過對使用卻可能會造成不便, 你不知道你得到的到底是那一個鍵所對應的值對象,所以請不要這樣做)
  2. 可以使用 null 作為 Value

Map 有兩種比較常用的實現: HashMap 和 TreeMap

常用的方法

方法名 說明
put(key , value) 儲存數據
get(key) 通過key得到值
remove(key) 通過key刪除對應的值(key當然也會刪除)
entrySet() 獲取Map所有的Key,返回一個Set集合
values() 獲取Map所有的value,返回一個List 集合
containsKey(key) 判斷Map中是否有輸入的參數:key
containsValue(value) 判斷Map中是否有輸入的參數:value
size() 判斷Map中數據的總數
clear() 清空Map中所有的數據
isEmpty() 判斷Map中是否有數據

HashMap

HashMap 用到了哈希碼的算法, 以便快速查找一個鍵。

public static void main(String[] args) {

    HashMap<String, String> zsInfo = new HashMap<>();

    zsInfo.put("name", "張三");
    zsInfo.put("height", "173CM");
    zsInfo.put("sex", "男性");

    for (Map.Entry<String, String> info : zsInfo.entrySet()) {
        System.out.println( info );
    }
}
    DEFAULT_INITIAL_CAPACITY = 1 << 4   默認的初始化容量 = 2^4 =16
    aka: Also Known As  也被稱作
    DEFAULT_LOAD_FACTOR = 0.75f 默認加載因子 = 0.75 = 75%
    TREEIFY_THRESHOLD = 8       樹化_閾值 = 8 

TreeMap

TreeMap 是對鍵按序存放, 因此它便有一些擴展的方法, 比如 firstKey() 、lastKey() 等, 可以從TreeMap 中指定一個范圍以取得其子Map

public static void main(String[] args) {

    TreeMap<String, String> tree = new TreeMap<>();

    tree.put("name", "Jack");
    tree.put("age", "22");
    tree.put("身高", "173");
    tree.put("sex", "man");
    tree.put("體重", "70KG");

    System.out.println("-------------------");
    for (Map.Entry<String, String> entry : tree.entrySet()) {
        System.out.println( entry );
    }
    System.out.println("-------------------");

    System.out.println("firstKey = "+ tree.firstKey());
    System.out.println("firstEntry = "+ tree.firstEntry());
    System.out.println("lastKey = "+ tree.lastKey());
    System.out.println("lastEntry = "+ tree.lastEntry());

}   

數組、List、Set、Map的數據轉換

不同類型的集合相互轉換在程序開發中都是非常常見的 API 操作,這些基礎操作一定要熟悉。

數組轉 List

/*
 * 數組轉List,但是這里要注意,Arrays.asList方法轉出來的雖然也叫ArrayList,
 * 但不是java.util.ArrayList,而是Arrays中的一個內部類ArrayList。兩者之間并不相等。
 * 內部類ArrayList沒有實現remove、add等方法,使用的話會報錯。
 */
System.out.println("使用Arrays.asList將數組轉換為List--------------------------------");
String str[] = { "中文", "計算機", "ABC", "123", "qq@qq.com" };

List<String> strList = Arrays.asList(str);

for (String string : strList) {
    System.out.println(string);
}

System.out.println("使用ArrayList構造器方式再由Arrays.asList將數組轉換為List----------");
// 使用下面的代碼的寫法就不會有問題了。
ArrayList<String> stringsList = new ArrayList<String>(Arrays.asList(str));
// 使用remove方法
stringsList.remove(3);

for (String string : stringsList) {
    System.out.println(string);
}

System.out.println("使用List.toArray方法將一個List轉換為數組(默認為Object數組)----------");
Object[] strArr = stringsList.toArray();
for (Object object : strArr) {
    System.out.println(object.toString());
}

數組和Set

System.out.println("使用Arrays.asList將數組轉換為List--------------------------------");
String str[] = { "中文", "計算機", "ABC", "123", "qq@qq.com" };

System.out.println("下面是數組轉Set ----------------------------------");

Set<String> strSet = new HashSet<String>(Arrays.asList(str));
for (String string : strSet) {
    System.out.println(string);
}
System.out.println("下面是將Set集合轉換為數組-------------");
Object[] objArr = strSet.toArray();
for (Object string : objArr) {
    System.out.println(string);
}

從Map中得到Set和List

Map<String, String> map = new HashMap<String, String>();
map.put("1", "A");
map.put("2", "B");
map.put("3", "C");

// 輸出所有的key , keySet()方法其實返回的就是一個Set集合。
System.out.println("通過keySet輸出所有的key:" + map.keySet());

// 把map的值轉為一個List
List<String> stringList = new LinkedList<String>(map.values());
for (String string : stringList) {
    System.out.print(string + " ");
}

散列表(哈希表)的基本原理

上面 HashSet 和 HashMap 都使用了 散列表來做數據的檢索,那么什么是散列表 ,這個又有什么優點呢 ?

散列表也叫作哈希表(hash table),這種數據結構提供了鍵(Key)和值(Value)的映射關系。只要給出一個Key,就可以高效查找到它所匹配的Value,時間復雜度接近于O(1)。

是不是想到了 Map ?

散列表是如何根據Key來快速找到它所匹配的 Value 呢 ?

在目前所學的數據結構中,數組的查詢效率是最高的。散列表本質就是一個數組。不同的是數組的下標是數字,但是散列表的下標是字符串。這就需要設計一個中轉站,通過某種方式,把Key和數組下標進行轉換。這個中轉站就叫作哈希函數

image-20200213110338179.png

在不同的語言中,哈希函數的實現是不一樣的。我們以Java的HashMap為例,來看看哈希函數的實現。

在Java及大多數面向對象的語言中,每一個對象都有屬于自己的hashcode,這個hashcode是區分不同對象的重要標識。無論對象自身的類型是什么,它們的hashcode都是一個整型變量。

既然都是整型變量,想要轉化成數組的下標也就不難實現了。最簡單的轉化方式是什么呢?是按照數組長度進行取模運算

index = HashCode (Key) % Array.length

其實JDK中的哈希函數為了提升效率,使用的是位運算。我們就假設使用的是模運算

通過哈希函數,我們可以把字符串或其他類型的Key,轉化成數組的下標index。如給出一個長度為8的數組,則當
key=001121時

index = HashCode ("001121") % Array.length = 1420036703 % 8 = 7

而當key=this時,

index = HashCode ("this") % Array.length = 3559070 % 8 = 6

散列表的讀寫操作

有了哈希函數,就可以在散列表中進行讀寫操作了。

寫操作(put)

寫操作就是在散列表中插入新的鍵值對(在JDK中叫作Entry)。如調用hashMap.put("002931", "王五"),意思是插入一組Key為002931、Value為王五的鍵值對。具體該怎么做呢?

  1. 通過哈希函數,把Key轉化成數組下標5。

  2. 如果數組下標5對應的位置沒有元素,就把這個Entry填充到數組下標5的位置

    image-20200213111520051.png

    由于數組的長度是有限的,當插入的Entry越來越多時,不同的Key通過哈希函數獲得的下標有可能是相同的。例如002936這個Key對應的數組下標是2;002947這個Key對應的數組下標也是2。
    image-20200213111552721.png

    這種情況,就叫作哈希沖突

解決哈希沖突的方法主要有兩種,一種是開放尋址法,一種是鏈表法

  1. 開放尋址法的原理很簡單,當一個Key通過哈希函數獲得對應的數組下標已被占用時,我們可以“另謀高就”,尋找下一個空檔位置
  2. HashMap數組的每一個元素不僅是一個Entry對象,還是一個鏈表的頭節點。每一個Entry對象通過next指針指向它的下一個Entry節點。當新來的Entry映射到與之沖突的數組位置時,只需要插入到對應的鏈表中即可


    image-20200213111821959.png

讀操作(get)

讀操作就是通過給定的Key,在散列表中查找對應的Value。例如調用 hashMap.get("002936"),意思是查找Key為002936的Entry在散列表中所對應的值

  1. 通過哈希函數,把Key轉化成數組下標2。
  2. 找到數組下標2所對應的元素,如果這個元素的Key是002936,那么就找到了;如果這個Key不是002936也沒關系,由于數組的每個元素都與一個鏈表對應,我們可以順著鏈表慢慢往下找,看看能否找到與Key相匹配的節點。

Iterator

iterator是為了實現對Java容器(collection)進行遍歷功能的一個接口。
在iterator實現了Iterator接口后,相當于把一個Collection容器的所有對象,做成一個線性表(List),而iterator本身是一個指針,開始時位于第一個元素之前。

方法 說明
hasNext() 判斷 iterator 內是否存在下1個元素,如果存在,返回true,否則返回false。(注意,這時上面的那個指針位置不變)
next() 返回 iterator 內下1個元素,同時上面的指針向后移動一位。如果不斷地循環執行next()方法,就可以遍歷容器內所有的元素了
remove() 刪除 iterator 內指針的前1個元素,前提是至少執行過1次next()

遍歷1個ArrayList 和Linklist是十分容易的,遍歷1個Tree容器也不難,但是實現機制是完全不同,而遍歷1個Set容器就無從下手了。
Iterator 這個接口讓各種容器自己去重寫里面的hasNext()和next()方法。 不用關心各種容器的遍歷機制,只要使用Iterator,會讓人覺得各種容器的遍歷方法都是一樣的,這就是Java接口的重要意義。

HashMap zsInfo = new HashMap<>();

zsInfo.put("name", "張三");
zsInfo.put("height", "173CM");
zsInfo.put("sex", "男性");
zsInfo.put("skin-color", "白色");

zsInfo.put("height", "183CM");
zsInfo.remove("skin-color");

System.out.println("Iterator 循環------------------");
Iterator iterator = zsInfo.entrySet().iterator();

while( iterator.hasNext() ) {
    Map.Entry next = (Entry) iterator.next();
    System.out.println( next );
}

Collections

Collections 是 Java 提供對Set 、List 和Map 等集合操作的工具類。

該工具類里提供了大量方法,除了對集合元素進行排序、查詢和修改等操作;還提供了將集合對象設置為不可變、對集合對象實現同步控制等方法。

排序

ArrayList nums = new ArrayList<>();
nums.add(2);
nums.add(0);
nums.add(-5);
nums.add(0);
nums.add(3);
System.out.println( nums );
Collections.reverse( nums );
System.out.println( nums);

Collections.sort( nums );
System.out.println( nums );

Collections.shuffle( nums );
System.out.println( nums );

查找

ArrayList nums = new ArrayList<>();
nums.add(2);
nums.add(0);
nums.add(-5);
nums.add(0);
nums.add(3);
System.out.println( nums );

System.out.println( Collections.max( nums ));
System.out.println( Collections.min( nums ));
Collections.replaceAll(nums, 0 , 1);
System.out.println( nums );

System.out.println( Collections.frequency(nums, 1));

Collections.sort(nums);
System.out.println( nums );

System.out.println( Collections.binarySearch(nums, 3 ));

排序(書中沒有寫,但是課堂上要教授)

集合的排序有2種方式:

  1. 集合內部就擁有排序的能力。

    需要排序的能力的類實現 Comparable 接口的方法

  2. 集合本身沒有排序的能力,可以通過外部指定排序的方式。

    在排序方法中,指定一個實現了 Comparator 接口的類的對象

Comparable是需要比較的對象來實現接口。這樣對象調用實現的方法來比較。對對象的耦合度高(需要改變對象的內部結構,破壞性大)。

Comparator相當于一通用的比較工具類接口。需要定制一個比較類去實現它,重寫里面的compare方法,方法的參數即是需要比較的對象。對象不用做任何改變,解耦。

List 集合排序

List<Integer> list = new ArrayList<Integer>();

list.add(123);
list.add(999);
list.add(101);
list.add(33);
list.add(76);

Collections.sort(list);

for (Integer integer : list) {
    System.out.println( integer );
}

直接就可以得到自然升序,我們在上面的代碼中沒有看到任何接口,原因是List集合中的 Integer 類本身就實現了 Comparable 接口

public final class Integer extends Number implements Comparable<Integer> {
    //...
}

TreeMap 排序

treeMap 的示例中,我們使用了外部指定排序方式:Comparator

TreeMap<String, String> tree = new TreeMap<>(new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o2.compareTo(o1) ;   // 
    }
});

tree.put("name", "Jack");
tree.put("age", "22");
tree.put("身高", "173");
tree.put("sex", "man");
tree.put("體重", "70KG");

System.out.println("-------------------");
for (Map.Entry<String, String> entry : tree.entrySet()) {
    System.out.println( entry );
}

自定義類排序

當我們自定義了一個類,也希望能夠進行排序的話,就需要實現 Comparator接口或者是 Comparable 接口了。

public class User {
    private Integer uid;
    private String uname;
    //....
}

外部指定排序

User 類沒有實現 Comparable 接口,通過 Collections 工具類的 sort 方法進行排序,在 sort 方法的第二個參數上指定排序的規則

List<User> list = new ArrayList<User>();

list.add( new User(110 ,"Mark") );
list.add( new User(101 ,"李四") );
list.add( new User(100 ,"張三") );
list.add( new User(111 ,"Jack") );

//  Comparator 接口因為是外部排序,所以需要知道對比的2個對象
Collections.sort(list , new Comparator<User>() {
    @Override
    public int compare(User o1, User o2) {
        return o2.getUid() - o1.getUid() ;
    }
});

for (User user : list) {
    System.out.println( user );
}

本身擁有排序

因為是內部排序,所以只需要傳入被排序的對象即可,另一個排序對象當然是對象本身,使用 this 指向即可。

public class User implements Comparable<User> {

    private Integer uid;
    private String uname;

    public User(int i, String string) {
        this.uid = i;
        this.uname = string;
    }

    @Override
    public int compareTo(User o) {
        return this.getUid() - o.getUid() ;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    @Override
    public String toString() {
        return "User [uid=" + uid + ", uname=" + uname + "]";
    }
}

排序

List<User> list = new ArrayList<User>();

list.add( new User(110 ,"Mark") );
list.add( new User(101 ,"李四") );
list.add( new User(100 ,"張三") );
list.add( new User(111 ,"Jack") );

Collections.sort(list);

for (User user : list) {
    System.out.println( user );
}

輸出結果

User [uid=100, uname=張三]
User [uid=101, uname=李四]
User [uid=110, uname=Mark]
User [uid=111, uname=Jack]
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容