30天搞定Java--day23

每日一考和複習

每日一考

  1. 什麼是枚舉類?枚舉類的對象聲明的修飾符都有哪些?
1.類中的對象的個數是確定的,有限個
2.private static final
  1. 什麼是元註解?說說Retention和Target元註解的作用
1.對現有的註解進行解釋說明的註解
2.Retention:指明所修飾的註解的生命週期
  Target:的作用是限定註解可作用的元素
  1. 比較throw 和 throws 的異同
1.throw生成一個異常對象,並拋出。使用在方法內部,自動拋出異常對象
2.throws處理異常的方式,使用在方法聲明處的末尾
  1. 談談你對同步代碼塊中同步監視器和共享數據的理解及各自要求。
1.同步監視器:俗稱鎖,任何一個類的對象都可以充當鎖,所有線程必須共用同一個鎖
2.共享數據:需要使用同步機制將操作共享數據的代碼包起來。不能包多了,也不能包少了。

複習
day22的學習內容


集合

Java集合框架概述

  1. 集合、數組都是對多個數據進行存儲操作的結構,簡稱Java容器。
    說明:此時的存儲,主要指的是內存層面的存儲,不涉及到持久化的存儲(.txt,.jpg,.avi,數據庫等)

  2. 數組在存儲多個數據方面的特點:
    一旦初始化以後,其長度就確定了
    數組一旦定義好,其元素的類型也就確定了。我們也就只能操作指定類型的數據了

  3. 數組在存儲多個數據方面的缺點:
    ①一旦初始化以後,其長度就不可修改。
    ②數組中提供的方法非常有限,對於添加、刪除、插入數據等操作,非常不便,同時效率不高
    ③獲取數組中實際元素的個數的需求,數組沒有現成的屬性或方法可用
    ④數組存儲數據的特點:有序、可重複。對於無序、不可重複的需求,不能滿足


集合框架

//常用的接口和實現類
|----Collection接口:單列集合,用來存儲一個一個的對象
    |----List接口:存儲有序的、可重複的數據   
        |----ArrayList、LinkedList、Vector
    |----Set接口:存儲無序的、不可重複的數據
        |----HashSet、LinkedHashSet、TreeSet

|----Map接口:雙列集合,用來存儲一對(key - value)一對的數據
    |----HashMap、LinkedHashMap、TreeMap、Hashtable、Properties

Collection接口方法

  1. 添加
    add(Object obj)
    addAll(Collection coll)
Collection coll = new ArrayList();

//add(Object e):將元素e添加到集合coll中
coll.add("AA");
coll.add("BB");
coll.add(123);//自動裝箱
coll.add(new Date());

//addAll(Collection coll1):將coll1集合中的元素添加到當前的集合中
Collection coll1 = new ArrayList();
coll1.add(456);
coll1.add("CC");
coll.addAll(coll1);
  1. 獲取有效元素的個數
    size()
System.out.println(coll.size());
  1. 清空集合
    void clear()
coll.clear();
  1. 是否是空集合
    boolean isEmpty()
System.out.println(coll.isEmpty());
  1. 是否包含某個元素
    boolean contains(Object obj):是通過元素的equals方法來判斷是否是同一個對象
    boolean containsAll(Collection c):也是調用元素的equals方法來比較的。拿兩個集合的元素挨個比較
//contains(Object obj)
boolean contains = coll.contains(123);
System.out.println(contains);
System.out.println(coll.contains(new String("Tom")));

//containsAll(Collection coll1)
Collection coll1 = Arrays.asList(123, 4567);
System.out.println(coll.containsAll(coll1));
  1. 刪除
    boolean remove(Object obj) :通過元素的equals方法判斷是否是要刪除的那個元素。只會刪除找到的第一個元素
    boolean removeAll(Collection coll):取當前集合的差集
coll.remove(new Person("Jerry", 20));
System.out.println(coll);

Collection coll1 = Arrays.asList(123, 456);
coll.removeAll(coll1);
System.out.println(coll);
  1. 取兩個集合的交集
    boolean retainAll(Collection c):把交集的結果存在當前集合中,不影響c
Collection coll1 = Arrays.asList(123,456,789);
coll.retainAll(coll1);
System.out.println(coll);
  1. 集合是否相等(有序)
    boolean equals(Object obj)
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));
  1. 轉成對象數組
    Object[] toArray()
Object[] arr = coll.toArray();
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
  1. 獲取集合對象的哈希值
    hashCode()
System.out.println(coll.hashCode());
  1. 遍歷
    iterator():返回迭代器對象,用於集合遍歷
Iterator iterator = coll.iterator();

Iterator迭代器接口

  • Iterator對象稱爲迭代器(設計模式的一種),主要用於遍歷 Collection 集合中的元素

  • GOF給迭代器模式的定義爲:提供一種方法訪問一個容器(container)對象中各個元素,而又不需暴露該對象的內部細節。迭代器模式,就是爲容器而生

  • Collection接口繼承了java.lang.Iterable接口,該接口有一個iterator()方法,那麼所有實現了Collection接口的集合類都有一個iterator()方法,用以返回一個實現了Iterator接口的對象

  • Iterator 僅用於遍歷集合,Iterator 本身並不提供承裝對象的能力。如果需要創建Iterator 對象,則必須有一個被迭代的集合

  • 集合對象每次調用iterator()方法都得到一個全新的迭代器對象,默認遊標都在集合的第一個元素之前

  • 注意:
    Iterator可以刪除集合的元素,但是是遍歷過程中通過迭代器對象的remove方法,不是集合對象的remove方法
    如果還未調用next()或在上一次調用 next 方法之後已經調用了 remove 方法,再調用remove都會報IllegalStateException

  1. hasNext()判斷是否存在下一個元素
    Next()獲取下一個元素
//遍歷集合
iterator = coll.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
  1. remove()刪除當前元素
//刪除集合中"Tom"
Iterator iterator = coll.iterator();
while (iterator.hasNext()) {
    Object obj = iterator.next();
    if ("Tom".equals(obj)) {
        iterator.remove();
    }
}

Collection子接口之一:List接口

一、list接口框架

|----Collection接口:單列集合,用來存儲一個一個的對象
   |----List接口:存儲有序的、可重複的數據。  -->“動態”數組,替換原有的數組
       |----ArrayList:作爲List接口的主要實現類;線程不安全的,效率高;底層使用Object[] elementData存儲
       |----LinkedList:對於頻繁的插入、刪除操作,使用此類效率比ArrayList高;底層使用雙向鏈表存儲
       |----Vector:作爲List接口的古老實現類;線程安全的,效率低;底層使用Object[] elementData存儲

二、ArrayList的源碼分析
① jdk 7情況下

 ArrayList list = new ArrayList();//底層創建了長度是10的Object[]數組elementData
 list.add(123);//elementData[0] = new Integer(123);
 ...
 list.add(11);//如果此次的添加導致底層elementData數組容量不夠,則擴容

默認情況下,擴容爲原來的容量的1.5倍,同時需要將原有數組中的數據複製到新的數組中
結論:建議開發中使用帶參的構造器:ArrayList list = new ArrayList(int capacity)

② jdk 8中ArrayList的變化:

 ArrayList list = new ArrayList();//底層Object[] elementData初始化爲{}.並沒有創建長度爲10的數組
 list.add(123);//第一次調用add()時,底層才創建了長度10的數組,並將數據123添加到elementData[0]
 ...

後續的添加和擴容操作與jdk 7 無異

③ 小結:jdk7中的ArrayList的對象的創建類似於單例的餓漢式,而jdk8中的ArrayList的對象的創建類似於單例的懶漢式,延遲了數組的創建,節省內存


三、LinkedList的源碼分析

LinkedList list = new LinkedList(); 內部聲明瞭Node類型的first和last屬性,默認值爲null
list.add(123);//將123封裝到Node中,創建了Node對象。

其中,Node(LinkedList的內部類)定義爲:體現了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倍

面試題:ArrayList、LinkedList、Vector三者的異同?
同:三個類都是實現了List接口,存儲數據的特點相同:存儲有序的、可重複的數據
不同:參考list接口框架


五、List接口中的常用方法

  1. void add(int index, E element):在index位置插入element元素
//boolean add(E e):在末尾插入e
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, E element):在index位置插入element元素
list.add(1, "BB");
System.out.println(list);
  1. boolean addAll(Collection<? extends E> c):將c中的所有元素添加進來
//boolean addAll(Collection<? extends E> c):將c中的所有元素添加進來
List list1 = Arrays.asList(1, 2, 3);
list.addAll(list1);
System.out.println(list.size());
//boolean addAll(int index, Collection<? extends E> c):在index處加入c中的所有元素
  1. E get(int index):獲取指定index位置的元素
System.out.println(list.get(0));
  1. int indexOf(Object obj):返回obj在集合中首次出現的位置
int index = list.indexOf(4567);
System.out.println(index);
  1. int lastIndexOf(Object obj):返回obj在當前集合中末次出現的位置
System.out.println(list.lastIndexOf(456));
  1. E remove(int index):移除指定index位置的元素,並返回此元素
Object obj = list.remove(0);
System.out.println(obj);
System.out.println(list);
  1. E set(int index, E element):設置指定index位置的元素爲element
list.set(1, "CC");
System.out.println(list);
  1. List subList(int fromIndex, int toIndex):返回從fromIndex到toIndex位置的子集合,左閉右開
List subList = list.subList(2, 4);
System.out.println(subList);
System.out.println(list);

總結:常用方法
增: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循環 ③ 普通的循環


Collection子接口之二:Set接口

一、Set接口的框架

|----Collection接口:單列集合,用來存儲一個一個的對象
    |----Set接口:存儲無序的、不可重複的數據   -->高中講的“集合”
        |----HashSet:作爲Set接口的主要實現類;線程不安全的;可以存儲null值
            |----LinkedHashSet:作爲HashSet的子類;遍歷其內部數據時,可以按照添加的順序遍歷
               對於頻繁的遍歷操作,LinkedHashSet效率高於HashSet
        |----TreeSet:可以按照添加對象的指定屬性,進行排序

二、Set接口的概述

  1. Set接口中沒有額外定義新的方法,使用的都是Collection中聲明過的方法

  2. 向Set(主要指:HashSet、LinkedHashSet)中添加的數據,其所在的類一定要重寫hashCode()和equals()
    要求:重寫的hashCode()和equals()儘可能保持一致性:相等的對象必須具有相等的散列碼
    重寫兩個方法的小技巧:對象中用作 equals() 方法比較的 Field,都應該用來計算 hashCode 值


三、Set的特點
存儲無序的、不可重複的數據
以HashSet爲例說明:

  1. 無序性:不等於隨機性。存儲的數據在底層數組中並非按照數組索引的順序添加,而是根據數據的哈希值決定的
  2. 不可重複性:保證添加的元素按照equals()判斷時,不能返回true.即:相同的元素只能添加一個

四、添加元素的過程
以HashSet爲例:

我們向HashSet中添加元素a,首先調用元素a所在類的hashCode()方法,計算元素a的哈希值,此哈希值接着通過
某種算法計算出在HashSet底層數組中的存放位置(即爲:索引位置),判斷數組此位置上是否已經有元素:
    如果此位置上沒有其他元素,則元素a添加成功 --->情況1
    如果此位置上有其他元素b(或以鏈表形式存在的多個元素),則比較元素a與元素b的hash值:
        如果hash值不相同,則元素a添加成功 --->情況2
        如果hash值相同,進而需要調用元素a所在類的equals()方法:
               equals()返回true,元素a添加失敗
               equals()返回false,則元素a添加成功 --->情況3

對於添加成功的情況2和情況3而言:元素a 與已經存在指定索引位置上數據以鏈表的方式存儲

五、jdk版本的差異
jdk 7 :元素a放到數組中,指向原來的元素
jdk 8 :原來的元素在數組中,指向元素a

HashSet底層:數組+鏈表的結構


六、使用方法舉例

Set set = new HashSet();
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());
}

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

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)
  3. 自然排序中,比較兩個對象是否相同的標準爲:compareTo()返回0,不再是equals()
  4. 定製排序中,比較兩個對象是否相同的標準爲:compare()返回0,不再是equals()
//User類中實現Comparable接口(按名字排序),就會按序輸出
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());
//com是Comparator接口的實現類,可以匹配相應的構造器進行傳參
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());

知識補充

foreach

jdk 5.0 新增了foreach循環(增強for循環),用於遍歷集合、數組

  1. 遍歷集合
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);
}
  1. 遍歷數組
int[] arr = new int[]{1, 2, 3, 4, 5, 6};
//for(數組元素的類型 局部變量 : 數組對象)
for (int i : arr) {
    System.out.println(i);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章