Java學習筆記十五(集合)

內容的要求:(面試必考)

層次一:選擇合適的集合類去實現數據的保存,調用其內部的相關方法。
層次二:不同的集合類底層的數據結構爲何?如何實現數據的操作的:增刪改查等。

一、集合的概述:

1、集合和數組都是對數據進行存儲操作的的結構,簡稱Java容器;
(這裏的存儲主要指的是內存層面的存儲;不涉及持久化的存儲:.txt、數據庫中、.jpg等)
2、數組中存儲多個數據的特點:
①數組一旦確定,長度也就確定了;
②數組一旦定義好,其元素類型也就確定了,我們只能操作指定的類型的數據了;像:String[]、int[]等。
3、數組在存儲多個數據方面的缺點:
①數組一旦聲明完成,其長度就不可修改;
②數組中定義的方法非常有限,像增刪改查等操作非常不便;
③獲取數組中元素個數的需求,數組中沒有定義現成的屬性和方法;
④數組中存儲數據的特點是有序的、可重複的;對於無序的和不可重複的需求無法滿足。

二、集合框架:

|——Collection接口:單列集合,用來存儲一個一個的數據;

  • |——List接口:用來存儲有序的可重複的數據;(動態數組)
    • |——ArrayList、LinkedList、Vector
  • |——Set接口:存儲無序的不可重複的數據;(高中的集合)
    • |——HashSet、LinkedHashSet、TreeSet

|——Map接口:雙列集合,用於存儲一對一對(k-v)的數據;(高中的函數)

  • |——HashMap、LinkedHashMap、TreeMap、HashTable、Properties

在這裏插入圖片描述

三、Collection接口中常用方法的使用:

注意:
向Collection接口的實現類的對象中添加數據obj時,要求obj所在類要重寫equals().

add(Object obj),addAll(Collection coll),size(),isEmpty(),clear(),

 @Test
    public void test1(){
        Collection coll = new ArrayList();

        //add(Object e):將元素e添加到集合coll中,返回的是boolean類型
        coll.add("AA");
        coll.add("BB");
        coll.add(123);//自動裝箱
        coll.add(new Date());

        //size():獲取添加的元素的個數
        System.out.println(coll.size());//4

        //addAll(Collection coll1):將coll1集合中的元素添加到當前的集合中
        Collection coll1 = new ArrayList();
        coll1.add(456);
        coll1.add("CC");
        coll.addAll(coll1);

        System.out.println(coll.size());//6
        System.out.println(coll);

        //clear():清空集合元素
        coll.clear();

        //isEmpty():判斷當前集合是否爲空
        System.out.println(coll.isEmpty());

    }

contains(Object obj),containsAll(Collection coll)
(contains(Object obj):判斷當前集合中是否包含obj,我們在判斷時會調用obj對象所在類的equals(),比較的是對象中的實體內容。)

@Test
    public void test1(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
//        Person p = new Person("Jerry",20);
//        coll.add(p);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);
        //1.contains(Object obj):判斷當前集合中是否包含obj
        //我們在判斷時會調用obj對象所在類的equals()。
        boolean contains = coll.contains(123);
        System.out.println(contains);
        System.out.println(coll.contains(new String("Tom")));
//        System.out.println(coll.contains(p));//true
        System.out.println(coll.contains(new Person("Jerry",20)));//false -->true

        //2.containsAll(Collection coll1):判斷形參coll1中的所有元素是否都存在於當前集合中。
        Collection coll1 = Arrays.asList(123,4567);
        System.out.println(coll.containsAll(coll1));
    }

remove(Object obj),removeAll(Collection coll),retainsAll(Collectioncoll),equals(Object obj),hasCode(),toArray(),iterator();

public class CollectionTest {


    @Test
    public void test1() {
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
//        Person p = new Person("Jerry",20);
//        coll.add(p);
        coll.add(new Person("Jerry", 20));
        coll.add(new String("Tom"));
        coll.add(false);
        //1.contains(Object obj):判斷當前集合中是否包含obj
        //我們在判斷時會調用obj對象所在類的equals()。
        boolean contains = coll.contains(123);
        System.out.println(contains);
        System.out.println(coll.contains(new String("Tom")));
//        System.out.println(coll.contains(p));//true
        System.out.println(coll.contains(new Person("Jerry", 20)));//false -->true

        //2.containsAll(Collection coll1):判斷形參coll1中的所有元素是否都存在於當前集合中。
        Collection coll1 = Arrays.asList(123, 4567);
        System.out.println(coll.containsAll(coll1));
    }

    @Test
    public void test2() {
        //3.remove(Object obj):從當前集合中移除obj元素。
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry", 20));
        coll.add(new String("Tom"));
        coll.add(false);

        coll.remove(1234);
        System.out.println(coll);

        coll.remove(new Person("Jerry", 20));
        System.out.println(coll);

        //4. removeAll(Collection coll1):差集:從當前集合中移除coll1中所有的元素。
        Collection coll1 = Arrays.asList(123, 456);
        coll.removeAll(coll1);
        System.out.println(coll);


    }

    @Test
    public void test3() {
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry", 20));
        coll.add(new String("Tom"));
        coll.add(false);

        //5.retainAll(Collection coll1):交集:獲取當前集合和coll1集合的交集,並返回給當前集合
//        Collection coll1 = Arrays.asList(123,456,789);
//        coll.retainAll(coll1);
//        System.out.println(coll);

        //6.equals(Object obj):要想返回true,需要當前集合和形參集合的元素都相同。
        Collection coll1 = new ArrayList();
        coll1.add(456);
        coll1.add(123);
        coll1.add(new Person("Jerry", 20));
        coll1.add(new String("Tom"));
        coll1.add(false);

        System.out.println(coll.equals(coll1));


    }

    @Test
    public void test4() {
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry", 20));
        coll.add(new String("Tom"));
        coll.add(false);

        //7.hashCode():返回當前對象的哈希值
        System.out.println(coll.hashCode());

        //8.集合 --->數組:toArray()
        Object[] arr = coll.toArray();
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

        //拓展:數組 --->集合:調用Arrays類的靜態方法asList()
        List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
        System.out.println(list);

        List arr1 = Arrays.asList(new int[]{123, 456});//注意基本數據類型的數組,會被當成是一個元素
        System.out.println(arr1);//[[I@50134894]
        System.out.println(arr1.size());//1

        List arr2 = Arrays.asList(new Integer[]{123, 456});
        System.out.println(arr2.size());//2

        //9.iterator():返回Iterator接口的實例,用於遍歷集合元素。放在IteratorTest.java中測試

    }
}

Collection集合與數組間的轉換:

//集合 --->數組:toArray()
Object[] arr = coll.toArray();
for(int i = 0;i < arr.length;i++){
    System.out.println(arr[i]);
}

//拓展:數組 --->集合:調用Arrays類的靜態方法asList(T ... t)
List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(list);

//因爲不用自動裝箱也可以理解,就不會自動裝箱。
//可以理解爲看成了一個數組對象,
List arr1 = Arrays.asList(new int[]{123, 456});
//下面會自動裝箱
//List arr1 = Arrays.asList({123, 456});//2

System.out.println(arr1.size());//1

List arr2 = Arrays.asList(new Integer[]{123, 456});
System.out.println(arr2.size());//2

注意: 使用Collection集合存儲對象,要求對象所屬的類滿足:
向Collection接口的實現類的對象中添加數據obj時,要求obj所在類要重寫equals()。

遍歷Collection的兩種方式:

① 使用迭代器Iterator ② foreach循環(或增強for循環)
(java.utils包下定義的迭代器接口:Iterator)

Iterator說明
1、Iterator對象稱爲迭代器(設計模式的一種),主要用於遍歷 Collection 集合中的元素。
2、GOF給迭代器模式的定義爲:提供一種方法訪問一個容器(container)對象中各個元素,而又不需暴露該對象的內部細節。迭代器模式,就是爲容器而生。

如何獲取實例: coll.iterator()返回一個迭代器實例;

Iterator iterator = coll.iterator();
//hasNext():判斷是否還下一個元素
while(iterator.hasNext()){
    //next():①指針下移 ②將下移以後集合位置上的元素返回
    System.out.println(iterator.next());
}

說明迭代器的執行原理:
在這裏插入圖片描述
迭代器中remove()的使用
注意點
1、如果還未調用next()或在上一次調用 next 方法之後已經調用了 remove 方法,再調用remove都會報IllegalStateException。
2、內部定義了remove(),可以在遍歷的時候,刪除集合中的元素。此方法不同於集合直接調用remove()

 @Test
    public void test3(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);

        //刪除集合中"Tom"
        Iterator iterator = coll.iterator();
        while (iterator.hasNext()){
//            iterator.remove();
            Object obj = iterator.next();
            if("Tom".equals(obj)){
                iterator.remove();
//                iterator.remove();
            }

        }
        //遍歷集合
        iterator = coll.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

jdk5.0新增的foreach循環,用於便利集合、數組:
結構:for(集合元素的類型 局部變量 : 集合對象){};增強for循環中其實依然調用的iterator實現方式。

@Test
    public void test1(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);

        //for(集合元素的類型 局部變量 : 集合對象)
        //內部仍然調用了迭代器。
        for(Object obj : coll){
            System.out.println(obj);
        }
    }

一道關於增強for的練習題:

@Test
    public void test3(){

        String[] arr = new String[]{"MM","MM","MM"};

        //方式一:普通for賦值
        for(int i = 0;i < arr.length;i++){
            arr[i] = "GG";
        }//這種方式輸出是GG

        //方式二:增強for循環
        for(String s : arr){
            s = "GG";
        }//這種方式輸出是MM,新建了一個對象指向相應的內容,
        //並不改變數組中的元素;

        for(int i = 0;i < arr.length;i++){
            System.out.println(arr[i]);
        }


    }

Collection子接口之List:

1、List接口框架:
|——Collection接口:單列集合,用來存儲一個一個的數據;

  • |——List接口:用來存儲有序的可重複的數據;(動態數組)
    • |——ArrayList:作爲List的主要實現類;線程不安全,效率高;底層使用Object[] elementDate存儲(數組)
    • |—— LinkedList:對於頻繁的插入、刪除等操作,使用此類比ArrayList效率高;底層使用雙向鏈表存儲;
    • |——Vector:作爲List接口的古老實現類;線程安全的,效率低;底層使用Object[] elementData存儲;

ArrayList源碼分析:(JDK7和8有點變化)
JDK7下:
ArrayList list = new ArrayL();底層創建了一個容量爲10的Object[]數組elementDate;
list.add(123);底層相當於elementDate[0] = new Integer(123);
list.add(11);如果此次添加時底層的elementDate數組容量不足,則需要進行擴容;默認情況下,擴容爲原來的1.5倍,並且需要將原來數組中的元素複製到新的數組中。

結論: 建議開發中使用帶參的構造器ArrayList(int Capacity);

JDK8下:
ArrayList list = new ArrayList();底層創建的Object[] elementDate是一個{},並沒有創建長度爲10的數組;
list.add(123);第一次調用add()時底層才創建了長度爲10的數組,並將123添加到elementDate[0]中;後續的操作就和JDK7一樣了。

小結: JDK7創建ArrayList對象的底層操作類似單例模式的餓漢式;JDK8中相當於懶漢式;

LinkedList的源碼分析:
LinkedList list = new LinkedList();內部聲明瞭Node類型的first和last,默認值是null;
list.add();將123封裝到node中,創建node對象。
其中,Node定義爲:體現了LinkedList的雙向鏈表的說法

  •  private static class Node<E> {
          E item;
          Node<E> next;
          Node<E> prev;
    
          Node(Node<E> prev, E element, Node<E> next) {
          this.item = element;
          this.next = next;
          this.prev = prev;
          }
      }
    

Vector的源碼分析:
jdk7和jdk8中通過Vector()構造器創建對象時,底層都創建了長度爲10的數組。
在擴容方面,默認擴容爲原來的數組長度的2倍。
補充: 棧(一種數據結構,先進後出)是Vector的一個子類。

面試題: ArrayList、LinkedList、Vector三者的異同?

  • 同:三個類都是實現了List接口,存儲數據的特點相同:存儲有序的、可重複的數據
  • 不同:見上

List接口中的常用方法:
void add(int index, Object ele):在index位置插入ele元素
boolean addAll(int index, Collection eles):從index位置開始將eles中的所有元素添加進來
Object get(int index):獲取指定index位置的元素
int indexOf(Object obj):返回obj在集合中首次出現的位置
int lastIndexOf(Object obj):返回obj在當前集合中末次出現的位置
Object remove(int index):移除指定index位置的元素,並返回此元素
Object set(int index, Object ele):設置指定index位置的元素爲ele
List subList(int fromIndex, int toIndex):返回從fromIndex到toIndex位置的子集合

總結:常用方法
增:add(Object obj)
刪:remove(int index) / remove(Object obj)
改:set(int index, Object ele)
查:get(int index)
插:add(int index, Object ele)
長度:size()
遍歷:① Iterator迭代器方式
② 增強for循環
③ 普通的循環

@Test
    public void test3(){
        ArrayList list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add("AA");

        //方式一:Iterator迭代器方式
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }

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

        //方式二:增強for循環
        for(Object obj : list){
            System.out.println(obj);
        }

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

        //方式三:普通for循環
        for(int i = 0;i < list.size();i++){
            System.out.println(list.get(i));
        }



    }


    @Test
    public void test2(){
        ArrayList list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add("AA");
        list.add(new Person("Tom",12));
        list.add(456);
        //int indexOf(Object obj):返回obj在集合中首次出現的位置。如果不存在,返回-1.
        int index = list.indexOf(4567);
        System.out.println(index);

        //int lastIndexOf(Object obj):返回obj在當前集合中末次出現的位置。如果不存在,返回-1.
        System.out.println(list.lastIndexOf(456));

        //Object remove(int index):移除指定index位置的元素,並返回此元素
        Object obj = list.remove(0);
        System.out.println(obj);
        System.out.println(list);

        //Object set(int index, Object ele):設置指定index位置的元素爲ele
        list.set(1,"CC");
        System.out.println(list);

        //List subList(int fromIndex, int toIndex):返回從fromIndex到toIndex位置的左閉右開區間的子集合
        List subList = list.subList(2, 4);
        System.out.println(subList);
        System.out.println(list);


    }


    @Test
    public void test1(){
        ArrayList list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add("AA");
        list.add(new Person("Tom",12));
        list.add(456);

        System.out.println(list);

        //void add(int index, Object ele):在index位置插入ele元素
        list.add(1,"BB");
        System.out.println(list);

        //boolean addAll(int index, Collection eles):從index位置開始將eles中的所有元素添加進來
        List list1 = Arrays.asList(1, 2, 3);
        list.addAll(list1);
//        list.add(list1);
        System.out.println(list.size());//9

        //Object get(int index):獲取指定index位置的元素
        System.out.println(list.get(0));

    }

注意:注意區分List中remove(int index)和remove(Object obj);

public class ListExer {
    /*
    區分List中remove(int index)和remove(Object obj)
     */
    @Test
    public void testListRemove() {
        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        updateList(list);
        System.out.println(list);//
    }

    private void updateList(List list) {
//        list.remove(2);
        list.remove(new Integer(2));
    }

}

Set接口:

1、Set接口的框架:
|——Collection接口:單列集合,用來存儲一個一個的數據;

  • |——Set接口:存儲無序的不可重複的數據;(高中的集合)
    • |——HashSet:作爲Set的主要實現類,線程不安全,效率高,且可以存儲null值;
      • |——LinkedHashSet:作爲HashSet的子類,對其內部數據進行遍歷時,可以按照其添加順序遍歷,實現有序的訪問;對於頻繁遍歷的操作,LinkedHashSet效率高於HashSet。
    • |——TreeSet:可以按照添加對象的指定屬性進行排序。、

Set接口中沒有定義新的方法,使用的都是Collection聲明的方法。
存儲對象所在類的要求:
HashSet和LinkedHashSet:
向Set接口中添加的元素,其所在的類都要重寫equals()和hashCode()方法。重寫的equals()和hashCode()方法應儘可能保持一致性:equals()判斷相等的對象必須具備相同的散列碼(hash值)。
小技巧: 用於equals()比較的屬性也應該參與hashCode()的計算。
TreeSet:
1.自然排序中,比較兩個對象是否相同的標準爲:compareTo()返回0.不再是equals().
2.定製排序中,比較兩個對象是否相同的標準爲:compare()返回0.不再是equals().
Set的無序性和不可重複性理解:
1、無序性:不等同於隨機性;應解釋爲存儲的數據在底層數組中並非按照索引的順序進行添加,而是根據數據的hash值確定的;
2、不可重複性:保證添加的元素通過equals()判斷,不能反悔true;即是相同的元素只能添加一個。

添加元素的過程:(以HashSet爲例)
向HashSet中添加元素a時,首先根據元素a所在類的hashCode計算出a的hash值,然後在根據某種算法計算得出HashSet底層數組中的存放位置(即爲索引),然後在判斷其位置處是否存在元素:
1、若不存在則添加成功;
2、若存在元素b(或以鏈表形式存在的多個元素),則比較a和b的hash值:

  • ①若hash值不相同,則添加成功;——情況2
  • ②若hash值相同,則調用元素a所在類的equals()方法:
    • 1)若equals()返回true,添加失敗;
    • 2)若equals()返回false,添加成功——情況3

對於添加成功的情況2和情況3而言:元素a 與已經存在指定索引位置上數據以鏈表的方式存儲。
jdk 7 :元素a放到數組中,指向原來的元素。
jdk 8 :原來的元素在數組中,指向元素a
總結:七上八下
HashSet底層:數組+鏈表的結構(JDK7是,JDK8有變化)

LinkedHashSet的使用
LinkedHashSet作爲HashSet的子類,再添加元素的同時,還維護了兩個引用,用於記錄此數據前一個數據和後一個數據。
優點: 對於頻繁的遍歷的操作,LinkedHashSet效率高於HashSet。

@Test
    public void test2(){
        Set set = new LinkedHashSet();
        set.add(456);
        set.add(123);
        set.add(123);
        set.add("AA");
        set.add("CC");
        set.add(new User("Tom",12));
        set.add(new User("Tom",12));
        set.add(129);

        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

關於TreeSet: 可以根據添加元素的某種屬性進行排序。
1、向TreeSet中添加的數據要求是相同類的對象。
2、兩種排序方式:自然排序(實現Comparable接口)和定製排序(Comparator)。

 @Test
    public void test1(){
        TreeSet set = new TreeSet();

        //失敗:不能添加不同類的對象
//        set.add(123);
//        set.add(456);
//        set.add("AA");
//        set.add(new User("Tom",12));

            //舉例一:
//        set.add(34);
//        set.add(-34);
//        set.add(43);
//        set.add(11);
//        set.add(8);

        //舉例二:
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56));


        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }

    }

    @Test
    public void test2(){
        Comparator com = new Comparator() {
            //按照年齡從小到大排列
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof User && o2 instanceof User){
                    User u1 = (User)o1;
                    User u2 = (User)o2;
                    return Integer.compare(u1.getAge(),u2.getAge());
                }else{
                    throw new RuntimeException("輸入的數據類型不匹配");
                }
            }
        };

        TreeSet set = new TreeSet(com);
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Mary",33));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56));


        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

雙列集合框架Map:

常見面試題:
1、HashMap的底層實現原理?
2、HashMap 和 Hashtable的異同?
3、CurrentHashMap 與 Hashtable的異同?(暫時不講)

一、Map實現類的結構:
|——Map接口:雙列集合,用於存儲一對一對(k-v)的數據;(高中的函數)

  • |——HashMap:作爲Map的主要實現類;線程不安全,效率高,可以存儲null值得key和value。
    • |—— LinkedHashMap:可以保證在遍歷集合中的元素時,是按照添加的順序進行遍歷。(原因:在原有的HashMap底層結構的基礎上,添加了一對指針,指向前一個元素和後一個元素。對於頻繁的遍歷操作,此類的執行效率高於HashMap)
  • |——TreeMap:保證按照添加的key-value對進行排序,實現排序遍歷(考慮的是key的自然排序和定製排序)。底層使用紅黑樹。
  • |——HashTable:古老的Map實現類,線程安全,效率低,不能存儲null值的key和value。
    • |——Properties:常用來處理配置文件。key和value都是String類型。

補充:
HashMap底層使用:

  • 數組+鏈表(JDK7)
  • 數組+鏈表+紅黑樹(JDK8)

二、Map結構的理解:(以HashMap爲例)
Map中的key:無序的,不可重複的,使用Set存儲所有的key。——key所在的類要重寫equals()和hashCode()方法。
Map中的value:無序的,可重複的。使用Collection存儲所有的value——value所在的類要重寫equals()方法。
一個鍵值對:key-value構成一個Entry對象。
Map中的Entry:無序的,不可重複的;使用Set存儲所有的Entry。
在這裏插入圖片描述

三、HashMap的底層實現原理:(JDK7)
HashMap map = new HashMap();實例化之後底層創建一個長度爲16的一維數組Entry[] table。
>>>可能經過多次put之後>>>
map.put(key1,value1);首先,調用key1所在類的hashCode()方法計算出key1的哈希值,此值經過某種算法(其實是與length-1進行位與&)得到在Entry數組中存放的位置
如果此位置爲空,則鍵值對添加成功。----情況1
如果此位置上的數據不爲空,(意味着此位置上存在一個或多個數據(以鏈表形式存在)),比較key1和已經存在的一個或多個數據的哈希值:

  • 如果key1的哈希值與該位置上所有的元素都不相同,則添加key-value對成功。----情況2
  • 如果key1的哈希值和已經存在的某一個數據(key2-value2)的哈希值相同,繼續比較:調用key1所在類的equals(key2)方法,比較:
    • 如果equals()返回false:此時key1-value1添加成功。----情況3
    • 如果equals()返回true,則用value1替換value2

補充:
1、關於情況2和情況3:此時key1-value1和原來的數據以鏈表的方式存儲。

2、HashMap中的容量一定是2的冪次方,因爲它有一個檢驗的機制:
int capacity =1;
while(capacity < initalcapacity){
capacity <<= 1;}

3、Map不是到達容量極限在擴容:Map可能並不會到達容量極限,因爲它是根據hash值算出來的,同一個索引位置可以以鏈表或紅黑樹的方式存儲。(要儘可能減少,鏈表或紅黑樹的情況。)

在不斷的添加過程中會涉及到擴容的問題;當超出臨界值(且要存放的位置非空時),擴容。默認擴容爲原來的2倍。並將原來的數據複製過來。

JDK8相較於JDK7底層實現方面的不同:
1、new HashMap();底層沒有創建一個長度爲16的數組;
2、JDK8底層的數組是Node[]而非Entry[];
3、首次調用put()方法時,底層創建長度爲16的數組;
4、JDK7底層結構只有:數組+鏈表;JDK8底層是:數組+鏈表+紅黑樹。

  • 當形成鏈表時,七上八下(JDK7時新加的元素替換舊元素在數組中的位置並指向舊元素,JDK8時是舊元素指向新元素)。
  • 當數組某一索引上的元素以鏈表的形式存在的數據個數>8。並且數組的長度>64時,此時此索引位置上的所有元素改爲使用紅黑樹存儲。

關於Map的術語:
DEFAULT_INITIAL_CAPACITY : HashMap的默認容量,16
DEFAULT_LOAD_FACTOR:HashMap的默認加載因子:0.75
threshold:擴容的臨界值,=容量*填充因子:16 * 0.75 => 12
TREEIFY_THRESHOLD:Bucket中鏈表長度大於該默認值,轉化爲紅黑樹:8
MIN_TREEIFY_CAPACITY:桶中的Node被樹化時最小的hash表容量:64

四、LinkedHashMap的源碼分析:
就是在HashMap的基礎上加了兩個Entry類型的before和after用於指向前一個元素和後一個元素:

static class Entry<K,V> extends HashMap.Node<K,V> {
             Entry<K,V> before, after;//能夠記錄添加的元素的先後順序
             Entry(int hash, K key, V value, Node<K,V> next) {
                super(hash, key, value, next);
             }
         }

五、Map中定義的方法:
添加、刪除、修改操作:
Object put(Object key,Object value):將指定key-value添加到(或修改)當前map對象中
void putAll(Map m):將m中的所有key-value對存放到當前map中
Object remove(Object key):移除指定key的key-value對,並返回value
void clear():清空當前map中的所有數據
元素查詢的操作:
Object get(Object key):獲取指定key對應的value
boolean containsKey(Object key):是否包含指定的key
boolean containsValue(Object value):是否包含指定的value
int size():返回map中key-value對的個數
boolean isEmpty():判斷當前map是否爲空
boolean equals(Object obj):判斷當前map和參數對象obj是否相等
元視圖操作的方法:
Set keySet():返回所有key構成的Set集合
Collection values():返回所有value構成的Collection集合
Set entrySet():返回所有key-value對構成的Set集合

總結:常用方法:

  • 添加:put(Object key,Object value)
  • 刪除:remove(Object key)
  • 修改:put(Object key,Object value)
  • 查詢:get(Object key)
  • 長度:size()
  • 遍歷:keySet() / values() / entrySet()
@Test
    public void test5(){
        Map map = new HashMap();
        map.put("AA",123);
        map.put(45,1234);
        map.put("BB",56);

        //遍歷所有的key集:keySet()
        Set set = map.keySet();
            Iterator iterator = set.iterator();
            while(iterator.hasNext()){
                System.out.println(iterator.next());
        }
        System.out.println();
        //遍歷所有的value集:values()
        Collection values = map.values();
        for(Object obj : values){
            System.out.println(obj);
        }
        System.out.println();
        //遍歷所有的key-value
        //方式一:entrySet()
        Set entrySet = map.entrySet();
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()){
            Object obj = iterator1.next();
            //entrySet集合中的元素都是entry
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "---->" + entry.getValue());

        }
        System.out.println();
        //方式二:
        Set keySet = map.keySet();
        Iterator iterator2 = keySet.iterator();
        while(iterator2.hasNext()){
            Object key = iterator2.next();
            Object value = map.get(key);
            System.out.println(key + "=====" + value);

        }

    }


    /*
 元素查詢的操作:
 Object get(Object key):獲取指定key對應的value
 boolean containsKey(Object key):是否包含指定的key
 boolean containsValue(Object value):是否包含指定的value
 int size():返回map中key-value對的個數
 boolean isEmpty():判斷當前map是否爲空
 boolean equals(Object obj):判斷當前map和參數對象obj是否相等
     */
    @Test
    public void test4(){
        Map map = new HashMap();
        map.put("AA",123);
        map.put(45,123);
        map.put("BB",56);
        // Object get(Object key)
        System.out.println(map.get(45));
        //containsKey(Object key)
        boolean isExist = map.containsKey("BB");
        System.out.println(isExist);

        isExist = map.containsValue(123);
        System.out.println(isExist);

        map.clear();

        System.out.println(map.isEmpty());

    }

    /*
     添加、刪除、修改操作:
 Object put(Object key,Object value):將指定key-value添加到(或修改)當前map對象中
 void putAll(Map m):將m中的所有key-value對存放到當前map中
 Object remove(Object key):移除指定key的key-value對,並返回value
 void clear():清空當前map中的所有數據
     */
    @Test
    public void test3(){
        Map map = new HashMap();
        //添加
        map.put("AA",123);
        map.put(45,123);
        map.put("BB",56);
        //修改
        map.put("AA",87);

        System.out.println(map);

        Map map1 = new HashMap();
        map1.put("CC",123);
        map1.put("DD",123);

        map.putAll(map1);

        System.out.println(map);

        //remove(Object key)
        Object value = map.remove("CC");
        System.out.println(value);
        System.out.println(map);

        //clear()
        map.clear();//與map = null操作不同
        System.out.println(map.size());
        System.out.println(map);
    }

    @Test
    public void test2(){
        Map map = new HashMap();
        map = new LinkedHashMap();
        map.put(123,"AA");
        map.put(345,"BB");
        map.put(12,"CC");

        System.out.println(map);
    }


    @Test
    public void test1(){
        Map map = new HashMap();
//        map = new Hashtable();
        map.put(null,123);

    }
}

Collections:操作Collection、Map的工具類:

reverse(List):反轉 List 中元素的順序
shuffle(List):對 List 集合元素進行隨機排序
sort(List):根據元素的自然順序對指定 List 集合元素按升序排序
sort(List,Comparator):根據指定的 Comparator 產生的順序對 List 集合元素進行排序
swap(List,int, int):將指定 list 集合中的 i 處元素和 j 處元素進行交換

Object max(Collection):根據元素的自然順序,返回給定集合中的最大元素
Object max(Collection,Comparator):根據 Comparator 指定的順序,返回給定集合中的最大元素
Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object):返回指定集合中指定元素的出現次數
void copy(List dest,List src):將src中的內容複製到dest中
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替換 List 對象的所有舊值
說明:ArrayList和HashMap都是線程不安全的,如果程序要求線程安全,我們可以將ArrayList、HashMap轉換爲線程的。
使用synchronizedList(List list) 和 synchronizedMap(Map map)

 @Test
    public void test2(){
        List list = new ArrayList();
        list.add(123);
        list.add(43);
        list.add(765);
        list.add(-97);
        list.add(0);

        //報異常:IndexOutOfBoundsException("Source does not fit in dest")
//        List dest = new ArrayList();
//        Collections.copy(dest,list);
        //正確的:
        List dest = Arrays.asList(new Object[list.size()]);
        System.out.println(dest.size());//list.size();
        Collections.copy(dest,list);

        System.out.println(dest);


        /*
        Collections 類中提供了多個 synchronizedXxx() 方法,
        該方法可使將指定集合包裝成線程同步的集合,從而可以解決
        多線程併發訪問集合時的線程安全問題

         */
        //返回的list1即爲線程安全的List
        List list1 = Collections.synchronizedList(list);


    }

    @Test
    public void test1(){
        List list = new ArrayList();
        list.add(123);
        list.add(43);
        list.add(765);
        list.add(765);
        list.add(765);
        list.add(-97);
        list.add(0);

        System.out.println(list);

//        Collections.reverse(list);
//        Collections.shuffle(list);
//        Collections.sort(list);
//        Collections.swap(list,1,2);
        int frequency = Collections.frequency(list, 123);

        System.out.println(list);
        System.out.println(frequency);

    }

}

TreeMap的使用:

1、向TreeMap中添加key-value,要求key必須是由同一個類創建的對象因爲要按照key進行排序:自然排序 、定製排序。

 @Test
    public void test1(){
        TreeMap map = new TreeMap();
        User u1 = new User("Tom",23);
        User u2 = new User("Jerry",32);
        User u3 = new User("Jack",20);
        User u4 = new User("Rose",18);

        map.put(u1,98);
        map.put(u2,89);
        map.put(u3,76);
        map.put(u4,100);

        Set entrySet = map.entrySet();
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()){
            Object obj = iterator1.next();
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "---->" + entry.getValue());

        }
    }

    //定製排序
    @Test
    public void test2(){
        TreeMap map = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof User && o2 instanceof User){
                    User u1 = (User)o1;
                    User u2 = (User)o2;
                    return Integer.compare(u1.getAge(),u2.getAge());
                }
                throw new RuntimeException("輸入的類型不匹配!");
            }
        });
        User u1 = new User("Tom",23);
        User u2 = new User("Jerry",32);
        User u3 = new User("Jack",20);
        User u4 = new User("Rose",18);

        map.put(u1,98);
        map.put(u2,89);
        map.put(u3,76);
        map.put(u4,100);

        Set entrySet = map.entrySet();
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()){
            Object obj = iterator1.next();
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "---->" + entry.getValue());

        }
    }

Properties:常用來處理配置文件。key和value都是String類型:

public static void main(String[] args)  {
    FileInputStream fis = null;
    try {
        Properties pros = new Properties();

        fis = new FileInputStream("jdbc.properties");
        pros.load(fis);//加載流對應的文件

        String name = pros.getProperty("name");
        String password = pros.getProperty("password");

        System.out.println("name = " + name + ", password = " + password);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(fis != null){
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章