1. 程式人生 > 實用技巧 >Linux - sudo和su的區別

Linux - sudo和su的區別

Java集合

目錄

day28課堂筆記

1、集合概述

1.1、什麼是集合?有什麼用?

	陣列其實就是一個集合。集合實際上就是一個容器。可以來容納其它型別的資料。

	集合為什麼說在開發中使用較多?
		集合是一個容器,是一個載體,可以一次容納多個物件。
		在實際開發中,假設連線資料庫,資料庫當中有10條記錄,
		那麼假設把這10條記錄查詢出來,在java程式中會將10條
		資料封裝成10個java物件,然後將10個java物件放到某一個
		集合當中,將集合傳到前端,然後遍歷集合,將一個數據一個
		資料展現出來。

1.2、集合不能直接儲存基本資料型別,另外集合也不能直接儲存java物件,
集合當中儲存的都是java物件的記憶體地址。(或者說集合中儲存的是引用。)
	list.add(100); //自動裝箱Integer
	注意:
		集合在java中本身是一個容器,是一個物件。
		集合中任何時候儲存的都是“引用”。

1.3、在java中每一個不同的集合,底層會對應不同的資料結構。往不同的集合中
儲存元素,等於將資料放到了不同的資料結構當中。什麼是資料結構?資料儲存的
結構就是資料結構。不同的資料結構,資料儲存方式不同。例如:
	陣列、二叉樹、連結串列、雜湊表...
	以上這些都是常見的資料結構。

	你往集合c1中放資料,可能是放到陣列上了。
	你往集合c2中放資料,可能是放到二叉樹上了。
	.....
	你使用不同的集合等同於使用了不同的資料結構。

	你在java集合這一章節,你需要掌握的不是精通資料結構。java中已經將資料結構
	實現了,已經寫好了這些常用的集合類,你只需要掌握怎麼用?在什麼情況下選擇
	哪一種合適的集合去使用即可。

	new ArrayList(); 建立一個集合,底層是陣列。
	new LinkedList(); 建立一個集合物件,底層是連結串列。
	new TreeSet(); 建立一個集合物件,底層是二叉樹。
	.....

1.4、集合在java JDK中哪個包下?
	java.util.*;
		所有的集合類和集合介面都在java.util包下。

1.5、為了讓大家掌握集合這塊的內容,最好能將集合的繼承結構圖背會!!!
	集合整個這個體系是怎樣的一個結構,你需要有印象。

1.6、在java中集合分為兩大類:
	一類是單個方式儲存元素:
		單個方式儲存元素,這一類集合中超級父介面:java.util.Collection;

	一類是以鍵值對兒的方式儲存元素
		以鍵值對的方式儲存元素,這一類集合中超級父介面:java.util.Map;

集合中儲存的是物件的記憶體地址:

  • 集合中存放的是物件的引用。
  • 集合中存放集合也是存放的是集合的引用。

2、總結重點:

第一個重點:把集合繼承結構圖背會。

第二個重點:把Collection介面中常用方法測試幾遍。

第三個重點:把迭代器弄明白。

第四個重點:Collection介面中的remove方法和contains方法底層都會呼叫equals,這個弄明白。

總結(所有的實現類):

ArrayList:底層是陣列。

LinkedList:底層是雙向連結串列。

Vector:底層是陣列,執行緒安全的,效率較低,使用較少。

HashSet:底層是HashMap,放到HashSet集合中的元素等同於放到HashMap集合key部分了。

TreeSet:底層是TreeMap,放到TreeSet集合中的元素等同於放到TreeMap集合key部分了。

n HashMap:底層是雜湊表。

Hashtable:底層也是雜湊表,只不過執行緒安全的,效率較低,使用較少。

Properties:是執行緒安全的,並且key和value只能儲存字串String。

TreeMap:底層是二叉樹。TreeMap集合的key可以自動按照大小順序排序。

List集合儲存元素的特點:

​ 有序可重複

​ 有序:存進去的順序和取出的順序相同,每一個元素都有下標。

​ 可重複:存進去1,可以再儲存一個1.

Set(Map)集合儲存元素的特點:

​ 無序不可重複

​ 無序:存進去的順序和取出的順序不一定相同。另外Set集合中元素沒有下標。

​ 不可重複:存進去1,不能再儲存1了。

SortedSet(SortedMap)集合儲存元素特點:

​ 首先是無序不可重複的,但是SortedSet集合中的元素是可排序的。

​ 無序:存進去的順序和取出的順序不一定相同。另外Set集合中元素沒有下標。

​ 不可重複:存進去1,不能再儲存1了。

​ 可排序:可以按照大小順序排列。

Map集合的key,就是一個Set集合。

往Set集合中放資料,實際上放到了Map集合的key部分。


程式碼

Collection部分

CollectionTest01——關於java.util.Collection介面中常用的方法
package com.bjpowernode.javase.collection;

import java.util.ArrayList;
import java.util.Collection;

/*
關於java.util.Collection介面中常用的方法。
    1、Collection中能存放什麼元素?
        沒有使用“泛型”之前,Collection中可以儲存Object的所有子型別。
        使用了“泛型”之後,Collection中只能儲存某個具體的型別。
        集合後期我們會學習“泛型”語法。目前先不用管。Collection中什麼都能存,
        只要是Object的子型別就行。(集合中不能直接儲存基本資料型別,也不能存
        java物件,只是儲存java物件的記憶體地址。)
    2、Collection中的常用方法
        boolean add(Object e) 向集合中新增元素
        int size()  獲取集合中元素的個數
        void clear() 清空集合
        boolean contains(Object o) 判斷當前集合中是否包含元素o,包含返回true,不包含返回false
        boolean remove(Object o) 刪除集合中的某個元素。
        boolean isEmpty()  判斷該集合中元素的個數是否為0
        Object[] toArray()  呼叫這個方法可以把集合轉換成陣列。【作為了解,使用不多。】
 */
public class CollectionTest01 {
    public static void main(String[] args) {
        // 建立一個集合物件
        //Collection c = new Collection(); // 介面是抽象的,無法例項化。
        // 多型
        Collection c = new ArrayList();
        // 測試Collection介面中的常用方法
        c.add(1200); // 自動裝箱(java5的新特性。),實際上是放進去了一個物件的記憶體地址。Integer x = new Integer(1200);
        c.add(3.14); // 自動裝箱
        c.add(new Object());
        c.add(new Student());
        c.add(true); // 自動裝箱

        // 獲取集合中元素的個數
        System.out.println("集合中元素個數是:" + c.size()); // 5

        // 清空集合
        c.clear();
        System.out.println("集合中元素個數是:" + c.size()); // 0

        // 再向集合中新增元素
        c.add("hello"); // "hello"物件的記憶體地址放到了集合當中。
        c.add("world");
        c.add("浩克");
        c.add("綠巨人");
        c.add(1);

        // 判斷集合中是否包含"綠巨人"
        boolean flag = c.contains("綠巨人");
        System.out.println(flag); // true
        boolean flag2 = c.contains("綠巨人2");
        System.out.println(flag2); // false
        System.out.println(c.contains(1)); // true

        System.out.println("集合中元素個數是:" + c.size()); // 5

        // 刪除集合中某個元素
        c.remove(1);
        System.out.println("集合中元素個數是:" + c.size()); // 4

        // 判斷集合是否為空(集合中是否存在元素)
        System.out.println(c.isEmpty()); // false
        // 清空
        c.clear();
        System.out.println(c.isEmpty()); // true(true表示集合中沒有元素了!)

        c.add("abc");
        c.add("def");
        c.add(100);
        c.add("helloworld!");
        c.add(new Student());

        // 轉換成陣列(瞭解,使用不多。)
        Object[] objs = c.toArray();
        for(int i = 0; i < objs.length; i++){
            // 遍歷陣列
            Object o = objs[i];
            System.out.println(o);
        }
    }
}

class Student{

}

CollectionTest02——關於集合遍歷/迭代專題(重點)

迭代集合的原理:

圖1:

圖2:

package com.bjpowernode.javase.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * 關於集合遍歷/迭代專題。(重點:五顆星*****)
 */
public class CollectionTest02 {
    public static void main(String[] args) {
        // 注意:以下講解的遍歷方式/迭代方式,是所有Collection通用的一種方式。
        // 在Map集合中不能用。在所有的Collection以及子類中使用。
        // 建立集合物件
        Collection c = new ArrayList(); // 後面的集合無所謂,主要是看前面的Collection介面,怎麼遍歷/迭代。
        // 新增元素
        c.add("abc");
        c.add("def");
        c.add(100);
        c.add(new Object());
        // 對集合Collection進行遍歷/迭代
        // 第一步:獲取集合物件的迭代器物件Iterator
        Iterator it = c.iterator();
        // 第二步:通過以上獲取的迭代器物件開始迭代/遍歷集合。
        /*
            以下兩個方法是迭代器物件Iterator中的方法:
                boolean hasNext()如果仍有元素可以迭代,則返回 true。
                Object next() 返回迭代的下一個元素。
         */
        while(it.hasNext()){
            Object obj = it.next();
            System.out.println(obj);
        }

        // 一直取,不判斷,會出現異常:java.util.NoSuchElementException
        /*while(true){
            Object obj = it.next();
            System.out.println(obj);
        }*/

        /*boolean hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            // 不管你當初存進去什麼,取出來統一都是Object。
            Object obj = it.next();
            System.out.println(obj);
        }

        hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            Object obj = it.next();
            System.out.println(obj);
        }

        hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            Object obj = it.next();
            System.out.println(obj);
        }

        hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            Object obj = it.next();
            System.out.println(obj);
        }

        hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            Object obj = it.next();
            System.out.println(obj);
        }*/
    }
}

CollectionTest03——關於集合的迭代/遍歷
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
/*
關於集合的迭代/遍歷
 */
public class CollectionTest03 {
    public static void main(String[] args) {
        // 建立集合物件
        Collection c1  = new ArrayList(); // ArrayList集合:有序可重複
        // 新增元素
        c1.add(1);
        c1.add(2);
        c1.add(3);
        c1.add(4);
        c1.add(1);

        // 迭代集合
        Iterator it = c1.iterator();
        while(it.hasNext()){
            // 存進去是什麼型別,取出來還是什麼型別。
            Object obj = it.next();
            /*if(obj instanceof Integer){
                System.out.println("Integer型別");
            }*/
            // 只不過在輸出的時候會轉換成字串。因為這裡println會呼叫toString()方法。
            System.out.println(obj);
        }

        // HashSet集合:無序不可重複
        Collection c2 = new HashSet();
        // 無序:存進去和取出的順序不一定相同。
        // 不可重複:儲存100,不能再儲存100.
        c2.add(100);
        c2.add(200);
        c2.add(300);
        c2.add(90);
        c2.add(400);
        c2.add(50);
        c2.add(60);
        c2.add(100);
        Iterator it2 = c2.iterator();
        while(it2.hasNext()){
            System.out.println(it2.next());
        }
    }
}
CollectionTest04——深入Collection集合的contains方法

參考:String類的equals方法

記憶體圖:

import java.util.ArrayList;
import java.util.Collection;

/*
深入Collection集合的contains方法:
    boolean contains(Object o)
        判斷集合中是否包含某個物件o
        如果包含返回true, 如果不包含返回false。

    contains方法是用來判斷集合中是否包含某個元素的方法,
    那麼它在底層是怎麼判斷集合中是否包含某個元素的呢?
        呼叫了equals方法進行比對。
        equals方法返回true,就表示包含這個元素。
 */
public class CollectionTest04 {
    public static void main(String[] args) {
        // 建立集合物件
        Collection c = new ArrayList();

        // 向集合中儲存元素
        String s1 = new String("abc"); // s1 = 0x1111
        c.add(s1); // 放進去了一個"abc"

        String s2 = new String("def"); // s2 = 0x2222
        c.add(s2);

        // 集合中元素的個數
        System.out.println("元素的個數是:" + c.size()); // 2

        // 新建的物件String
        String x = new String("abc"); // x = 0x5555
        // c集合中是否包含x?結果猜測一下是true還是false?
        System.out.println(c.contains(x)); //判斷集合中是否存在"abc" true
    }
}
CollectionTest05——測試contains方法、remove方法

重要結論:存放在一個集合中的型別,一定要重寫equals方法。

package com.bjpowernode.javase.collection;

import java.util.ArrayList;
import java.util.Collection;

/*
測試contains方法
測試remove方法。
結論:存放在一個集合中的型別,一定要重寫equals方法。
 */
public class CollectionTest05 {
    public static void main(String[] args) {
        // 建立集合物件
        Collection c = new ArrayList();
        // 建立使用者物件
        User u1 = new User("jack");
        // 加入集合
        c.add(u1);

        // 判斷集合中是否包含u2
        User u2 = new User("jack");

        // 沒有重寫equals之前:這個結果是false
        //System.out.println(c.contains(u2)); // false
        // 重寫equals方法之後,比較的時候會比較name。
        System.out.println(c.contains(u2)); // true

        c.remove(u2);
        System.out.println(c.size()); // 0

        /*Integer x = new Integer(10000);
        c.add(x);

        Integer y = new Integer(10000);
        System.out.println(c.contains(y)); // true*/

        // 建立集合物件
        Collection cc = new ArrayList();
        // 建立字串物件
        String s1 = new String("hello");
        // 加進去。
        cc.add(s1);

        // 建立了一個新的字串物件
        String s2 = new String("hello");
        // 刪除s2
        cc.remove(s2); // s1.equals(s2) java認為s1和s2是一樣的。刪除s2就是刪除s1。
        // 集合中元素個數是?
        System.out.println(cc.size()); // 0
    }
}

class User{
    private String name;
    public User(){}
    public User(String name){
        this.name = name;
    }

    // 重寫equals方法
    // 將來呼叫equals方法的時候,一定是呼叫這個重寫的equals方法。
    // 這個equals方法的比較原理是:只要姓名一樣就表示同一個使用者。
    public boolean equals(Object o) {
        if(o == null || !(o instanceof User)) return false;
        if(o == this) return true;
        User u = (User)o;
        // 如果名字一樣表示同一個人。(不再比較物件的記憶體地址了。比較內容。)
        return u.name.equals(this.name);
    }
}
CollectionTest06——關於集合元素的remove
package com.bjpowernode.javase.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/*
關於集合元素的remove
    重點:當集合的結構發生改變時,迭代器必須重新獲取,如果還是用以前老的迭代器,會出現
    異常:java.util.ConcurrentModificationException

    重點:在迭代集合元素的過程中,不能呼叫集合物件的remove方法,刪除元素:
        c.remove(o); 迭代過程中不能這樣。
        會出現:java.util.ConcurrentModificationException

    重點:在迭代元素的過程當中,一定要使用迭代器Iterator的remove方法,刪除元素,
    不要使用集合自帶的remove方法刪除元素。

 */
public class CollectionTest06 {
    public static void main(String[] args) {
        // 建立集合
        Collection c = new ArrayList();

        // 注意:此時獲取的迭代器,指向的是那是集合中沒有元素狀態下的迭代器。
        // 一定要注意:集合結構只要發生改變,迭代器必須重新獲取。
        // 當集合結構發生了改變,迭代器沒有重新獲取時,呼叫next()方法時:java.util.ConcurrentModificationException
        Iterator it = c.iterator();

        // 新增元素
        c.add(1); // Integer型別
        c.add(2);
        c.add(3);

        // 獲取迭代器
        //Iterator it = c.iterator();
        /*while(it.hasNext()){
            // 編寫程式碼時next()方法返回值型別必須是Object。
            // Integer i = it.next();
            Object obj = it.next();
            System.out.println(obj);
        }*/

        Collection c2 = new ArrayList();
        c2.add("abc");
        c2.add("def");
        c2.add("xyz");

        Iterator it2 = c2.iterator();
        while(it2.hasNext()){
            Object o = it2.next();
            // 刪除元素
            // 刪除元素之後,集合的結構發生了變化,應該重新去獲取迭代器
            // 但是,迴圈下一次的時候並沒有重新獲取迭代器,所以會出現異常:java.util.ConcurrentModificationException
            // 出異常根本原因是:集合中元素刪除了,但是沒有更新迭代器(迭代器不知道集合變化了)
            //c2.remove(o); // 直接通過集合去刪除元素,沒有通知迭代器。(導致迭代器的快照和原集合狀態不同。)
            // 使用迭代器來刪除可以嗎?
            // 迭代器去刪除時,會自動更新迭代器,並且更新集合(刪除集合中的元素)。
            it2.remove(); // 刪除的一定是迭代器指向的當前元素。
            System.out.println(o);
        }

        System.out.println(c2.size()); //0
    }
}

ListTest01——List介面中常用方法
package com.bjpowernode.javase.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/*
測試List介面中常用方法
    1、List集合儲存元素特點:有序可重複
        有序:List集合中的元素有下標。
        從0開始,以1遞增。
        可重複:儲存一個1,還可以再儲存1.
    2、List既然是Collection介面的子介面,那麼肯定List介面有自己“特色”的方法:
        以下只列出List介面特有的常用的方法:
            void add(int index, Object element)
            Object set(int index, Object element)
            Object get(int index)
            int indexOf(Object o)
            int lastIndexOf(Object o)
            Object remove(int index)

        以上幾個方法不需要死記硬背,可以自己編寫程式碼測試一下,理解一下,
        以後開發的時候,還是要翻閱幫助文件。
 */
public class ListTest01 {
    public static void main(String[] args) {
        // 建立List型別的集合。
        //List myList = new LinkedList();
        //List myList = new Vector();
        List myList = new ArrayList();

        // 新增元素
        myList.add("A"); // 預設都是向集合末尾新增元素。
        myList.add("B");
        myList.add("C");
        myList.add("C");
        myList.add("D");

        //在列表的指定位置插入指定元素(第一個引數是下標)
        // 這個方法使用不多,因為對於ArrayList集合來說效率比較低。
        myList.add(1, "KING");

        // 迭代
        Iterator it = myList.iterator();
        while(it.hasNext()){
            Object elt = it.next();
            System.out.println(elt);
        }

        // 根據下標獲取元素
        Object firstObj = myList.get(0);
        System.out.println(firstObj);

        // 因為有下標,所以List集合有自己比較特殊的遍歷方式
        // 通過下標遍歷。【List集合特有的方式,Set沒有。】
        for(int i = 0; i < myList.size(); i++){
            Object obj = myList.get(i);
            System.out.println(obj);
        }

        // 獲取指定物件第一次出現處的索引。
        System.out.println(myList.indexOf("C")); // 3

        // 獲取指定物件最後一次出現處的索引。
        System.out.println(myList.lastIndexOf("C")); // 4

        // 刪除指定下標位置的元素
        // 刪除下標為0的元素
        myList.remove(0);
        System.out.println(myList.size()); // 5

        System.out.println("====================================");

        // 修改指定位置的元素
        myList.set(2, "Soft");

        // 遍歷集合
        for(int i = 0; i < myList.size(); i++){
            Object obj = myList.get(i);
            System.out.println(obj);
        }
    }
}

/*
計算機英語:
    增刪改查這幾個單詞要知道:
        增:add、save、new
        刪:delete、drop、remove
        改:update、set、modify
        查:find、get、query、select
 */
ArrayListTest01——ArrayList集合
package com.bjpowernode.javase.collection;

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

/*
ArrayList集合:
    1、預設初始化容量10(底層先建立了一個長度為0的陣列,當新增第一個元素的時候,初始化容量10。)
    2、集合底層是一個Object[]陣列。
    3、構造方法:
        new ArrayList();
        new ArrayList(20);
    4、ArrayList集合的擴容:
        增長到原容量的1.5倍。
        ArrayList集合底層是陣列,怎麼優化?
            儘可能少的擴容。因為陣列擴容效率比較低,建議在使用ArrayList集合
            的時候預估計元素的個數,給定一個初始化容量。
    5、陣列優點:
        檢索效率比較高。(每個元素佔用空間大小相同,記憶體地址是連續的,知道首元素記憶體地址,
        然後知道下標,通過數學表示式計算出元素的記憶體地址,所以檢索效率最高。)
    6、陣列缺點:
        隨機增刪元素效率比較低。
        另外陣列無法儲存大資料量。(很難找到一塊非常巨大的連續的記憶體空間。)
    7、向陣列末尾新增元素,效率很高,不受影響。
    8、面試官經常問的一個問題?
        這麼多的集合中,你用哪個集合最多?
            答:ArrayList集合。
            因為往陣列末尾新增元素,效率不受影響。
            另外,我們檢索/查詢某個元素的操作比較多。

    7、ArrayList集合是非執行緒安全的。(不是執行緒安全的集合。)
 */
public class ArrayListTest01 {
    public static void main(String[] args) {

        // 預設初始化容量是10
        // 陣列的長度是10
        List list1 = new ArrayList();
        // 集合的size()方法是獲取當前集合中元素的個數。不是獲取集合的容量。
        System.out.println(list1.size()); // 0

        // 指定初始化容量
        // 陣列的長度是20
        List list2 = new ArrayList(20);
        // 集合的size()方法是獲取當前集合中元素的個數。不是獲取集合的容量。
        System.out.println(list2.size()); // 0

        list1.add(1);
        list1.add(2);
        list1.add(3);
        list1.add(4);
        list1.add(5);
        list1.add(6);
        list1.add(7);
        list1.add(8);
        list1.add(9);
        list1.add(10);

        System.out.println(list1.size());

        // 再加一個元素
        list1.add(11);
        System.out.println(list1.size()); // 11個元素。
        /*
        int newCapacity = ArraysSupport.newLength(oldCapacity,minCapacity - oldCapacity,oldCapacity >> 1);
         */
        // 100 二進位制轉換成10進位制: 00000100右移一位 00000010 (2)  【4 / 2】
        // 原先是4、現在增長:2,增長之後是6,增長之後的容量是之前容量的:1.5倍。
        // 6是4的1.5倍
    }
}

ArrayListTest02——集合ArrayList的構造方法
package com.bjpowernode.javase.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

/*
集合ArrayList的構造方法
 */
public class ArrayListTest02 {
    public static void main(String[] args) {

        // 預設初始化容量10
        List myList1 = new ArrayList();

        // 指定初始化容量100
        List myList2 = new ArrayList(100);

        // 建立一個HashSet集合
        Collection c = new HashSet();
        // 新增元素到Set集合
        c.add(100);
        c.add(200);
        c.add(900);
        c.add(50);

        // 通過這個構造方法就可以將HashSet集合轉換成List集合。
        List myList3 = new ArrayList(c);
        for(int i = 0; i < myList3.size(); i++){
            System.out.println(myList3.get(i));
        }
    }
}

LinkedListTest01——連結串列的原理以及優缺點
https://www.bilibili.com/video/BV1mE411x7Wt?p=211

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

/*
連結串列的優點:
    由於連結串列上的元素在空間儲存上記憶體地址不連續。
    所以隨機增刪元素的時候不會有大量元素位移,因此隨機增刪效率較高。
    在以後的開發中,如果遇到隨機增刪集合中元素的業務比較多時,建議
    使用LinkedList。

連結串列的缺點:
    不能通過數學表示式計算被查詢元素的記憶體地址,每一次查詢都是從頭
    節點開始遍歷,直到找到為止。所以LinkedList集合檢索/查詢的效率
    較低。

    ArrayList:把檢索發揮到極致。(末尾新增元素效率還是很高的。)
    LinkedList:把隨機增刪發揮到極致。
    加元素都是往末尾新增,所以ArrayList用的比LinkedList多。
 */
public class LinkedListTest01 {
    public static void main(String[] args) {
        // LinkedList集合底層也是有下標的。
        // 注意:ArrayList之所以檢索效率比較高,不是單純因為下標的原因。是因為底層陣列發揮的作用。
        // LinkedList集合照樣有下標,但是檢索/查詢某個元素的時候效率比較低,因為只能從頭節點開始一個一個遍歷。
        List list = new LinkedList();
        list.add("a");
        list.add("b");
        list.add("c");

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

        // LinkedList集合有初始化容量嗎?沒有。
        // 最初這個連結串列中沒有任何元素。first和last引用都是null。
        // 不管是LinkedList還是ArrayList,以後寫程式碼時不需要關心具體是哪個集合。
        // 因為我們要面向介面程式設計,呼叫的方法都是介面中的方法。
        //List list2 = new ArrayList(); // 這樣寫表示底層你用了陣列。
        List list2 = new LinkedList(); // 這樣寫表示底層你用了雙向連結串列。

        // 以下這些方法你面向的都是介面程式設計。
        list2.add("123");
        list2.add("456");
        list2.add("789");

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

單鏈表的實現

Node

package com.bjpowernode.javase.danlink;

/*
單鏈表中的節點。
節點是單向連結串列中基本的單元。
每一個節點Node都有兩個屬性:
    一個屬性:是儲存的資料。
    另一個屬性:是下一個節點的記憶體地址。
 */
public class Node {

    // 儲存的資料
    Object data;

    // 下一個節點的記憶體地址
    Node next;

    public Node(){

    }

    public Node(Object data, Node next){
        this.data = data;
        this.next = next;
    }
}

Link

package com.bjpowernode.javase.danlink;

/*
連結串列類。(單向連結串列)
 */
public class Link<E> {
    public static void main(String[] args) {
        Link<String> link = new Link<>();
        link.add("abc");

        // 型別不匹配。
        //link.add(123);
    }

    // 頭節點
    Node header;

    int size = 0;

    public int size(){
        return size;
    }

    // 向連結串列中新增元素的方法(向末尾新增)
    public void add(E data){
    //public void add(Object data){
        // 建立一個新的節點物件
        // 讓之前單鏈表的末尾節點next指向新節點物件。
        // 有可能這個元素是第一個,也可能是第二個,也可能是第三個。
        if(header == null){
            // 說明還沒有節點。
            // new一個新的節點物件,作為頭節點物件。
            // 這個時候的頭節點既是一個頭節點,又是一個末尾節點。
            header = new Node(data, null);
        }else {
            // 說明頭不是空!
            // 頭節點已經存在了!
            // 找出當前末尾節點,讓當前末尾節點的next是新節點。
            Node currentLastNode = findLast(header);
            currentLastNode.next = new Node(data, null);
        }
        size++;
    }

    /**
     * 專門查詢末尾節點的方法。
     */
    private Node findLast(Node node) {
        if(node.next == null) {
            // 如果一個節點的next是null
            // 說明這個節點就是末尾節點。
            return node;
        }
        // 程式能夠到這裡說明:node不是末尾節點。
        return findLast(node.next); // 遞迴演算法!
    }

    // 刪除連結串列中某個資料的方法
    public void remove(Object obj){

    }

    // 修改連結串列中某個資料的方法
    public void modify(Object newObj){

    }

    // 查詢連結串列中某個元素的方法。
    public int find(Object obj){
        return 1;
    }
}

Test

package com.bjpowernode.javase.danlink;

public class Test {
    public static void main(String[] args) {
        // 建立了一個集合物件
        Link link = new Link();

        // 往集合中新增元素
        link.add("abc");
        link.add("def");
        link.add("xyz");

        // 獲取元素個數
        System.out.println(link.size());
    }
}

VectorTest——Vector集合原始碼分析

https://www.bilibili.com/video/BV1mE411x7Wt?p=212

import java.util.*;

/*
Vector:
    1、底層也是一個數組。
    2、初始化容量:10
    3、怎麼擴容的?
        擴容之後是原容量的2倍。
        10--> 20 --> 40 --> 80

    4、ArrayList集合擴容特點:
        ArrayList集合擴容是原容量1.5倍。

    5、Vector中所有的方法都是執行緒同步的,都帶有synchronized關鍵字,
    是執行緒安全的。效率比較低,使用較少了。

    6、怎麼將一個執行緒不安全的ArrayList集合轉換成執行緒安全的呢?
        使用集合工具類:
            java.util.Collections;

            java.util.Collection 是集合介面。
            java.util.Collections 是集合工具類。
 */
public class VectorTest {
    public static void main(String[] args) {
        // 建立一個Vector集合
        List vector = new Vector();
        //Vector vector = new Vector();

        // 新增元素
        // 預設容量10個。
        vector.add(1);
        vector.add(2);
        vector.add(3);
        vector.add(4);
        vector.add(5);
        vector.add(6);
        vector.add(7);
        vector.add(8);
        vector.add(9);
        vector.add(10);

        // 滿了之後擴容(擴容之後的容量是20.)
        vector.add(11);

        Iterator it = vector.iterator();
        while(it.hasNext()){
            Object obj = it.next();
            System.out.println(obj);
        }

        // 這個可能以後要使用!!!!
        List myList = new ArrayList(); // 非執行緒安全的。

        // 變成執行緒安全的
        Collections.synchronizedList(myList); // 這裡沒有辦法看效果,因為多執行緒沒學,你記住先!

        // myList集合就是執行緒安全的了。
        myList.add("111");
        myList.add("222");
        myList.add("333");
    }
}
GenericTest01——泛型
package com.bjpowernode.javase.collection;

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

/*
1、JDK5.0之後推出的新特性:泛型
2、泛型這種語法機制,只在程式編譯階段起作用,只是給編譯器參考的。(執行階段泛型沒用!)
3、使用了泛型好處是什麼?
    第一:集合中儲存的元素型別統一了。
    第二:從集合中取出的元素型別是泛型指定的型別,不需要進行大量的“向下轉型”!

4、泛型的缺點是什麼?
    導致集合中儲存的元素缺乏多樣性!
    大多數業務中,集合中元素的型別還是統一的。所以這種泛型特性被大家所認可。
 */
public class GenericTest01 {
    public static void main(String[] args) {

        /*
        // 不使用泛型機制,分析程式存在缺點
        List myList = new ArrayList();

        // 準備物件
        Cat c = new Cat();
        Bird b = new Bird();

        // 將物件新增到集合當中
        myList.add(c);
        myList.add(b);

        // 遍歷集合,取出每個Animal,讓它move
        Iterator it = myList.iterator();
        while(it.hasNext()) {
            // 沒有這個語法,通過迭代器取出的就是Object
            //Animal a = it.next();

            Object obj = it.next();
            //obj中沒有move方法,無法呼叫,需要向下轉型!
            if(obj instanceof Animal){
                Animal a = (Animal)obj;
                a.move();
            }
        }
         */

        // 使用JDK5之後的泛型機制
        // 使用泛型List<Animal>之後,表示List集合中只允許儲存Animal型別的資料。
        // 用泛型來指定集合中儲存的資料型別。
        List<Animal> myList = new ArrayList<Animal>();

        // 指定List集合中只能儲存Animal,那麼儲存String就編譯報錯了。
        // 這樣用了泛型之後,集合中元素的資料型別更加統一了。
        //myList.add("abc");

        Cat c = new Cat();
        Bird b = new Bird();

        myList.add(c);
        myList.add(b);

        // 獲取迭代器
        // 這個表示迭代器迭代的是Animal型別。
        Iterator<Animal> it = myList.iterator();
        while(it.hasNext()){
            // 使用泛型之後,每一次迭代返回的資料都是Animal型別。
            //Animal a = it.next();
            // 這裡不需要進行強制型別轉換了。直接呼叫。
            //a.move();

            // 呼叫子型別特有的方法還是需要向下轉換的!
            Animal a = it.next();
            if(a instanceof Cat) {
                Cat x = (Cat)a;
                x.catchMouse();
            }
            if(a instanceof Bird) {
                Bird y = (Bird)a;
                y.fly();
            }
        }
    }
}

class Animal {
    // 父類自帶方法
    public void move(){
        System.out.println("動物在移動!");
    }
}

class Cat extends Animal {
    // 特有方法
    public void catchMouse(){
        System.out.println("貓抓老鼠!");
    }
}

class Bird extends Animal {
    // 特有方法
    public void fly(){
        System.out.println("鳥兒在飛翔!");
    }
}
GenericTest02——【泛型】自動型別推斷機制
package com.bjpowernode.javase.collection;

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

/*
JDK1.8之後引入了:自動型別推斷機制。(又稱為鑽石表示式)
 */
public class GenericTest02 {
    public static void main(String[] args) {

        // ArrayList<這裡的型別會自動推斷>(),前提是JDK8之後才允許。
        // 自動型別推斷,鑽石表示式!
        List<Animal> myList = new ArrayList<>();

        myList.add(new Animal());
        myList.add(new Cat());
        myList.add(new Bird());

        // 遍歷
        Iterator<Animal> it = myList.iterator();
        while(it.hasNext()){
            Animal a = it.next();
            a.move();
        }

        List<String> strList = new ArrayList<>();

        // 型別不匹配。
        //strList.add(new Cat());
        strList.add("http://www.126.com");
        strList.add("http://www.baidu.com");
        strList.add("http://www.bjpowernode.com");

        // 型別不匹配。
        //strList.add(123);

        //System.out.println(strList.size());

        // 遍歷
        Iterator<String> it2 = strList.iterator();
        while(it2.hasNext()){
            // 如果沒有使用泛型
            /*
            Object obj = it2.next();
            if(obj instanceof String){
                String ss = (String)obj;
                ss.substring(7);
            }
             */
            // 直接通過迭代器獲取了String型別的資料
            String s = it2.next();
            // 直接呼叫String類的substring方法擷取字串。
            String newString = s.substring(7);
            System.out.println(newString);
        }
    }
}
GenericTest03——自定義泛型
package com.bjpowernode.javase.collection;

/*
自定義泛型可以嗎?可以
    自定義泛型的時候,<> 尖括號中的是一個識別符號,隨便寫。
    java原始碼中經常出現的是:
        <E>和<T>
    E是Element單詞首字母。
    T是Type單詞首字母。
 */
public class GenericTest03<識別符號隨便寫> {

    public void doSome(識別符號隨便寫 o){
        System.out.println(o);
    }

    public static void main(String[] args) {

        // new物件的時候指定了泛型是:String型別
        GenericTest03<String> gt = new GenericTest03<>();

        // 型別不匹配
        //gt.doSome(100);

        gt.doSome("abc");

        // =============================================================
        GenericTest03<Integer> gt2 = new GenericTest03<>();
        gt2.doSome(100);

        // 型別不匹配
        //gt2.doSome("abc");

        MyIterator<String> mi = new MyIterator<>();
        String s1 = mi.get();

        MyIterator<Animal> mi2 = new MyIterator<>();
        Animal a = mi2.get();

        // 不用泛型就是Object型別。
        /*GenericTest03 gt3 = new GenericTest03();
        gt3.doSome(new Object());*/
    }
}

class MyIterator<T> {
    public T get(){
        return null;
    }
}
ForEachTest01——增強for迴圈

語法:

for(元素型別 變數名 : 陣列或集合){
System.out.println(變數名);
}

package com.bjpowernode.javase.collection;

/*
JDK5.0之後推出了一個新特性:叫做增強for迴圈,或者叫做foreach
 */
public class ForEachTest01 {
    public static void main(String[] args) {

        // int型別陣列
        int[] arr = {432,4,65,46,54,76,54};

        // 遍歷陣列(普通for迴圈)
        for(int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

        // 增強for(foreach)
        // 以下是語法
        /*for(元素型別 變數名 : 陣列或集合){
            System.out.println(變數名);
        }*/

        System.out.println("======================================");
        // foreach有一個缺點:沒有下標。在需要使用下標的迴圈中,不建議使用增強for迴圈。
        for(int data : arr) {
            // data就是陣列中的元素(陣列中的每一個元素。)
            System.out.println(data);
        }
    }
}
ForEachTest02——集合使用foreach

遍歷集合的三種方式:

  1. 使用迭代器方式
  2. 使用下標方式(只針對於有下標的集合)
  3. 使用foreach
package com.bjpowernode.javase.collection;

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

/*
集合使用foreach
 */
public class ForEachTest02 {
    public static void main(String[] args) {
        // 建立List集合
        List<String> strList = new ArrayList<>();

        // 新增元素
        strList.add("hello");
        strList.add("world!");
        strList.add("kitty!");

        // 遍歷,使用迭代器方式
        Iterator<String> it = strList.iterator();
        while(it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }

        // 使用下標方式(只針對於有下標的集合)
        for(int i = 0; i < strList.size(); i++){
            System.out.println(strList.get(i));
        }

        // 使用foreach
        for(String s : strList){ // 因為泛型使用的是String型別,所以是:String s
            System.out.println(s);
        }

        List<Integer> list = new ArrayList<>();
        list.add(100);
        list.add(200);
        list.add(300);
        for(Integer i : list){ // i代表集合中的元素
            System.out.println(i);
        }
    }
}
HashSetTest01——HashSet集合【無序不可重複】
package com.bjpowernode.javase.collection;

import java.util.HashSet;
import java.util.Set;

/*
HashSet集合:
    無序不可重複。
 */
public class HashSetTest01 {
    public static void main(String[] args) {
        // 演示一下HashSet集合特點
        Set<String> strs = new HashSet<>();

        // 新增元素
        strs.add("hello3");
        strs.add("hello4");
        strs.add("hello1");
        strs.add("hello2");
        strs.add("hello3");
        strs.add("hello3");
        strs.add("hello3");
        strs.add("hello3");

        // 遍歷
        /*
        hello1
        hello4
        hello2
        hello3
        1、儲存時順序和取出的順序不同。
        2、不可重複。
        3、放到HashSet集合中的元素實際上是放到HashMap集合的key部分了。
         */
        for(String s : strs){
            System.out.println(s);
        }
    }
}

TreeSetTest01——TreeSet集合【無序不可重複,可排序集合】

package com.bjpowernode.javase.collection;

import java.util.Set;
import java.util.TreeSet;

/*
TreeSet集合儲存元素特點:
    1、無序不可重複的,但是儲存的元素可以自動按照大小順序排序!
    稱為:可排序集合。

    2、無序:這裡的無序指的是存進去的順序和取出來的順序不同。並且沒有下標。
 */
public class TreeSetTest01 {
    public static void main(String[] args) {
        // 建立集合物件
        Set<String> strs = new TreeSet<>();
        // 新增元素
        strs.add("A");
        strs.add("B");
        strs.add("Z");
        strs.add("Y");
        strs.add("Z");
        strs.add("K");
        strs.add("M");
        // 遍歷
        /*
            A
            B
            K
            M
            Y
            Z
        從小到大自動排序!
         */
        for(String s : strs){
            System.out.println(s);
        }
    }
}

Map部分

MapTest01——java.util.Map介面中常用的方法
package com.bjpowernode.javase.collection;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/*
java.util.Map介面中常用的方法:
    1、Map和Collection沒有繼承關係。
    2、Map集合以key和value的方式儲存資料:鍵值對
        key和value都是引用資料型別。
        key和value都是儲存物件的記憶體地址。
        key起到主導的地位,value是key的一個附屬品。
    3、Map介面中常用方法:
        V put(K key, V value) 向Map集合中新增鍵值對
        V get(Object key) 通過key獲取value
        void clear()    清空Map集合
        boolean containsKey(Object key) 判斷Map中是否包含某個key
        boolean containsValue(Object value) 判斷Map中是否包含某個value
        boolean isEmpty()   判斷Map集合中元素個數是否為0
        V remove(Object key) 通過key刪除鍵值對
        int size() 獲取Map集合中鍵值對的個數。
        Collection<V> values() 獲取Map集合中所有的value,返回一個Collection

        Set<K> keySet() 獲取Map集合所有的key(所有的鍵是一個set集合)

        Set<Map.Entry<K,V>> entrySet()
            將Map集合轉換成Set集合
            假設現在有一個Map集合,如下所示:
                map1集合物件
                key             value
                ----------------------------
                1               zhangsan
                2               lisi
                3               wangwu
                4               zhaoliu

                Set set = map1.entrySet();
                set集合物件
                1=zhangsan 【注意:Map集合通過entrySet()方法轉換成的這個Set集合,Set集合中元素的型別是 Map.Entry<K,V>】
                2=lisi     【Map.Entry和String一樣,都是一種型別的名字,只不過:Map.Entry是靜態內部類,是Map中的靜態內部類】
                3=wangwu
                4=zhaoliu ---> 這個東西是個什麼?Map.Entry
 */
public class MapTest01 {
    public static void main(String[] args) {
        // 建立Map集合物件
        Map<Integer, String> map = new HashMap<>();
        // 向Map集合中新增鍵值對
        map.put(1, "zhangsan"); // 1在這裡進行了自動裝箱。
        map.put(2, "lisi");
        map.put(3, "wangwu");
        map.put(4, "zhaoliu");
        // 通過key獲取value
        String value = map.get(2);
        System.out.println(value);
        // 獲取鍵值對的數量
        System.out.println("鍵值對的數量:" + map.size());
        // 通過key刪除key-value
        map.remove(2);
        System.out.println("鍵值對的數量:" + map.size());
        // 判斷是否包含某個key
        // contains方法底層呼叫的都是equals進行比對的,所以自定義的型別需要重寫equals方法。
        System.out.println(map.containsKey(new Integer(4))); // true
        // 判斷是否包含某個value
        System.out.println(map.containsValue(new String("wangwu"))); // true

        // 獲取所有的value
        Collection<String> values = map.values();
        // foreach
        for(String s : values){
            System.out.println(s);
        }

        // 清空map集合
        map.clear();
        System.out.println("鍵值對的數量:" + map.size());
        // 判斷是否為空
        System.out.println(map.isEmpty()); // true
    }
}
MapTest02——Map集合的遍歷【非常重要】
package com.bjpowernode.javase.collection;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/*
Map集合的遍歷。【非常重要】
 */
public class MapTest02 {
    public static void main(String[] args) {

        // 第一種方式:獲取所有的key,通過遍歷key,來遍歷value
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "zhangsan");
        map.put(2, "lisi");
        map.put(3, "wangwu");
        map.put(4, "zhaoliu");
        // 遍歷Map集合
        // 獲取所有的key,所有的key是一個Set集合
        Set<Integer> keys = map.keySet();
        // 遍歷key,通過key獲取value
        // 迭代器可以
        /*Iterator<Integer> it = keys.iterator();
        while(it.hasNext()){
            // 取出其中一個key
            Integer key = it.next();
            // 通過key獲取value
            String value = map.get(key);
            System.out.println(key + "=" + value);
        }*/
        // foreach也可以
        for(Integer key : keys){
            System.out.println(key + "=" + map.get(key));
        }

        // 第二種方式:Set<Map.Entry<K,V>> entrySet()
        // 以上這個方法是把Map集合直接全部轉換成Set集合。
        // Set集合中元素的型別是:Map.Entry
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        // 遍歷Set集合,每一次取出一個Node
        // 迭代器
        /*Iterator<Map.Entry<Integer,String>> it2 = set.iterator();
        while(it2.hasNext()){
            Map.Entry<Integer,String> node = it2.next();
            Integer key = node.getKey();
            String value = node.getValue();
            System.out.println(key + "=" + value);
        }*/

        // foreach
        // 這種方式效率比較高,因為獲取key和value都是直接從node物件中獲取的屬性值。
        // 這種方式比較適合於大資料量。
        for(Map.Entry<Integer,String> node : set){
            System.out.println(node.getKey() + "--->" + node.getValue());
        }
    }
}

HashMapTest01——HashMap集合底層原理
https://www.bilibili.com/video/BV1mE411x7Wt?p=224
https://www.bilibili.com/video/BV1mE411x7Wt?p=225

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/*
HashMap集合:
    1、HashMap集合底層是雜湊表/散列表的資料結構。
    2、雜湊表是一個怎樣的資料結構呢?
        雜湊表是一個數組和單向連結串列的結合體。
        陣列:在查詢方面效率很高,隨機增刪方面效率很低。
        單向連結串列:在隨機增刪方面效率較高,在查詢方面效率很低。
        雜湊表將以上的兩種資料結構融合在一起,充分發揮它們各自的優點。
    3、HashMap集合底層的原始碼:
        public class HashMap{
            // HashMap底層實際上就是一個數組。(一維陣列)
            Node<K,V>[] table;
            // 靜態的內部類HashMap.Node
            static class Node<K,V> {
                final int hash; // 雜湊值(雜湊值是key的hashCode()方法的執行結果。hash值通過雜湊函式/演算法,可以轉換儲存成陣列的下標。)
                final K key; // 儲存到Map集合中的那個key
                V value; // 儲存到Map集合中的那個value
                Node<K,V> next; // 下一個節點的記憶體地址。
            }
        }
        雜湊表/散列表:一維陣列,這個陣列中每一個元素是一個單向連結串列。(陣列和連結串列的結合體。)
    4、最主要掌握的是:
        map.put(k,v)
        v = map.get(k)
        以上這兩個方法的實現原理,是必須掌握的。
    5、HashMap集合的key部分特點:
        無序,不可重複。
        為什麼無序? 因為不一定掛到哪個單向連結串列上。
        不可重複是怎麼保證的? equals方法來保證HashMap集合的key不可重複。
        如果key重複了,value會覆蓋。

        放在HashMap集合key部分的元素其實就是放到HashSet集合中了。
        所以HashSet集合中的元素也需要同時重寫hashCode()+equals()方法。

    6、雜湊表HashMap使用不當時無法發揮性能!
        假設將所有的hashCode()方法返回值固定為某個值,那麼會導致底層雜湊表變成了
        純單向連結串列。這種情況我們成為:雜湊分佈不均勻。
        什麼是雜湊分佈均勻?
            假設有100個元素,10個單向連結串列,那麼每個單向連結串列上有10個節點,這是最好的,
            是雜湊分佈均勻的。
        假設將所有的hashCode()方法返回值都設定為不一樣的值,可以嗎,有什麼問題?
            不行,因為這樣的話導致底層雜湊表就成為一維陣列了,沒有連結串列的概念了。
            也是雜湊分佈不均勻。
        雜湊分佈均勻需要你重寫hashCode()方法時有一定的技巧。
    7、重點:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同時重寫hashCode和equals方法。
    8、HashMap集合的預設初始化容量是16,預設載入因子是0.75
        這個預設載入因子是當HashMap集合底層陣列的容量達到75%的時候,陣列開始擴容。

        重點,記住:HashMap集合初始化容量必須是2的次冪(方便求雜湊值用的),這也是官方推薦的,
        這是因為達到雜湊均勻,為了提高HashMap集合的存取效率,所必須的。
 */
public class HashMapTest01 {
    public static void main(String[] args) {
        // 測試HashMap集合key部分的元素特點
        // Integer是key,它的hashCode和equals都重寫了。
        Map<Integer,String> map = new HashMap<>();
        map.put(1111, "zhangsan");
        map.put(6666, "lisi");
        map.put(7777, "wangwu");
        map.put(2222, "zhaoliu");
        map.put(2222, "king"); //key重複的時候value會自動覆蓋。

        System.out.println(map.size()); // 4

        // 遍歷Map集合
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        for(Map.Entry<Integer,String> entry : set){
            // 驗證結果:HashMap集合key部分元素:無序不可重複。
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
    }
}
HashMapTest02——重寫equals方法和hashCode方法
package com.bjpowernode.javase.bean;

import java.util.HashSet;
import java.util.Set;

/*
1、向Map集合中存,以及從Map集合中取,先封裝成Node,都是先呼叫key的hashCode方法,然後再呼叫equals方法!
equals方法有可能呼叫,也有可能不呼叫。
    拿put(k,v)舉例,什麼時候equals不會呼叫?
        k.hashCode()方法返回雜湊值,
        雜湊值經過雜湊演算法轉換成陣列下標。
        陣列下標位置上如果是null,equals不需要執行。
    拿get(k)舉例,什麼時候equals不會呼叫?
        k.hashCode()方法返回雜湊值,
        雜湊值經過雜湊演算法轉換成陣列下標。
        陣列下標位置上如果是null,equals不需要執行。

2、注意:如果一個類的equals方法重寫了,那麼hashCode()方法必須重寫。
並且equals方法返回如果是true,hashCode()方法返回的值必須一樣。
    equals方法返回true表示兩個物件相同,在同一個單向連結串列上比較。
    那麼對於同一個單向連結串列上的節點來說,他們的雜湊值都是相同的。
    所以hashCode()方法的返回值也應該相同。

3、hashCode()方法和equals()方法不用研究了,直接使用IDEA工具生成,但是這兩個方法需要同時生成。

4、終極結論:
    放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同時重寫hashCode方法和equals方法。

5、對於雜湊表資料結構來說:
    如果o1和o2的hash值相同,一定是放到同一個單向連結串列上。
    當然如果o1和o2的hash值不同,但由於雜湊演算法執行結束之後轉換的陣列下標可能相同,此時會發生“雜湊碰撞”。

 */
public class HashMapTest02 {
    public static void main(String[] args) {

        Student s1 = new Student("zhangsan");
        Student s2 = new Student("zhangsan");

        // 重寫equals方法之前是false
        //System.out.println(s1.equals(s2)); // false

        // 重寫equals方法之後是true
        System.out.println(s1.equals(s2)); //true (s1和s2表示相等)

		//返回的是物件的記憶體地址
        System.out.println("s1的hashCode=" + s1.hashCode()); //284720968 (重寫hashCode之後-1432604525)
        System.out.println("s2的hashCode=" + s2.hashCode()); //122883338 (重寫hashCode之後-1432604525)

        // s1.equals(s2)結果已經是true了,表示s1和s2是一樣的,相同的,那麼往HashSet集合中放的話,
        // 按說只能放進去1個。(HashSet集合特點:無序不可重複)
        Set<Student> students = new HashSet<>();
        students.add(s1);
        students.add(s2);
        System.out.println(students.size()); // 這個結果按說應該是1. 但是結果是2.顯然不符合HashSet集合儲存特點。怎麼辦?
    }
}

Student

package com.bjpowernode.javase.bean;

import java.util.Objects;

public class Student {
    private String name;

    public Student() {
    }

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    // hashCode

    // equals(如果學生名字一樣,表示同一個學生。)
    /*public boolean equals(Object obj){
        if(obj == null || !(obj instanceof Student)) return false;
        if(obj == this) return true;
        Student s = (Student)obj;
        return this.name.equals(s.name);
    }*/

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

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}
HashMapTest03——HashMap集合key部分允許null嗎?
package com.bjpowernode.javase.bean;

import java.util.HashMap;
import java.util.Map;

/*
HashMap集合key部分允許null嗎?
    允許
    但是要注意:HashMap集合的key null值只能有一個。
    有可能面試的時候遇到這樣的問題。
 */
public class HashMapTest03 {
    public static void main(String[] args) {

        Map map = new HashMap();

        // HashMap集合允許key為null
        map.put(null, null);
        System.out.println(map.size()); // 1

        // key重複的話value是覆蓋!
        map.put(null, 100);
        System.out.println(map.size()); //1

        // 通過key獲取value
        System.out.println(map.get(null)); // 100
    }
}
HashtableTest01——Hashtable的key可以為null嗎?
package com.example.javase.bean;

import java.util.Hashtable;
import java.util.Map;

/*
Hashtable的key可以為null嗎?
    Hashtable的key和value都是不能為null的。
    HashMap集合的key和value都是可以為null的。

Hashtable方法都帶有synchronized:執行緒安全的。
執行緒安全有其它的方案,這個Hashtable對執行緒的處理
導致效率較低,使用較少了。

Hashtable和HashMap一樣,底層都是雜湊表資料結構。
Hashtable的初始化容量是11,預設載入因子是:0.75f
Hashtable的擴容是:原容量 * 2 + 1
 */
public class HashtableTest01 {
    public static void main(String[] args) {
        Map map = new Hashtable();

        //map.put(null, "123");
        map.put(100, null);

    }
}
PropertiesTest01——Properties屬性類物件的相關方法
package com.example.javase.collection;

import java.util.Properties;

/*
目前只需要掌握Properties屬性類物件的相關方法即可。
Properties是一個Map集合,繼承Hashtable,Properties的key和value都是String型別。
Properties被稱為屬性類物件。
Properties是執行緒安全的。
 */
public class PropertiesTest01 {
    public static void main(String[] args) {

        // 建立一個Properties物件
        Properties pro = new Properties();

        // 需要掌握Properties的兩個方法,一個存,一個取。
        pro.setProperty("url", "jdbc:mysql://localhost:3306/example");
        pro.setProperty("driver","com.mysql.jdbc.Driver");
        pro.setProperty("username", "root");
        pro.setProperty("password", "123");

        // 通過key獲取value
        String url = pro.getProperty("url");
        String driver = pro.getProperty("driver");
        String username = pro.getProperty("username");
        String password = pro.getProperty("password");

        System.out.println(url);
        System.out.println(driver);
        System.out.println(username);
        System.out.println(password);

    }
}