文章目錄
每日一考和複習
每日一考
- 什麼是枚舉類?枚舉類的對象聲明的修飾符都有哪些?
1.類中的對象的個數是確定的,有限個
2.private static final
- 什麼是元註解?說說Retention和Target元註解的作用
1.對現有的註解進行解釋說明的註解
2.Retention:指明所修飾的註解的生命週期
Target:的作用是限定註解可作用的元素
- 比較throw 和 throws 的異同
1.throw生成一個異常對象,並拋出。使用在方法內部,自動拋出異常對象
2.throws處理異常的方式,使用在方法聲明處的末尾
- 談談你對同步代碼塊中同步監視器和共享數據的理解及各自要求。
1.同步監視器:俗稱鎖,任何一個類的對象都可以充當鎖,所有線程必須共用同一個鎖
2.共享數據:需要使用同步機制將操作共享數據的代碼包起來。不能包多了,也不能包少了。
複習
day22的學習內容
集合
Java集合框架概述
-
集合、數組都是對多個數據進行存儲操作的結構,簡稱Java容器。
說明:此時的存儲,主要指的是內存層面的存儲,不涉及到持久化的存儲(.txt,.jpg,.avi,數據庫等) -
數組在存儲多個數據方面的特點:
一旦初始化以後,其長度就確定了
數組一旦定義好,其元素的類型也就確定了。我們也就只能操作指定類型的數據了 -
數組在存儲多個數據方面的缺點:
①一旦初始化以後,其長度就不可修改。
②數組中提供的方法非常有限,對於添加、刪除、插入數據等操作,非常不便,同時效率不高
③獲取數組中實際元素的個數的需求,數組沒有現成的屬性或方法可用
④數組存儲數據的特點:有序、可重複。對於無序、不可重複的需求,不能滿足
集合框架
//常用的接口和實現類
|----Collection接口:單列集合,用來存儲一個一個的對象
|----List接口:存儲有序的、可重複的數據
|----ArrayList、LinkedList、Vector
|----Set接口:存儲無序的、不可重複的數據
|----HashSet、LinkedHashSet、TreeSet
|----Map接口:雙列集合,用來存儲一對(key - value)一對的數據
|----HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
Collection接口方法
- 添加
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);
- 獲取有效元素的個數
size()
System.out.println(coll.size());
- 清空集合
void clear()
coll.clear();
- 是否是空集合
boolean isEmpty()
System.out.println(coll.isEmpty());
- 是否包含某個元素
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));
- 刪除
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);
- 取兩個集合的交集
boolean retainAll(Collection c):把交集的結果存在當前集合中,不影響c
Collection coll1 = Arrays.asList(123,456,789);
coll.retainAll(coll1);
System.out.println(coll);
- 集合是否相等(有序)
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));
- 轉成對象數組
Object[] toArray()
Object[] arr = coll.toArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
- 獲取集合對象的哈希值
hashCode()
System.out.println(coll.hashCode());
- 遍歷
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
- hasNext()判斷是否存在下一個元素
Next()獲取下一個元素
//遍歷集合
iterator = coll.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
- 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接口中的常用方法
- 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);
- 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中的所有元素
- E get(int index):獲取指定index位置的元素
System.out.println(list.get(0));
- int indexOf(Object obj):返回obj在集合中首次出現的位置
int index = list.indexOf(4567);
System.out.println(index);
- int lastIndexOf(Object obj):返回obj在當前集合中末次出現的位置
System.out.println(list.lastIndexOf(456));
- E remove(int index):移除指定index位置的元素,並返回此元素
Object obj = list.remove(0);
System.out.println(obj);
System.out.println(list);
- E set(int index, E element):設置指定index位置的元素爲element
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);
總結:常用方法
增: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接口的概述
-
Set接口中沒有額外定義新的方法,使用的都是Collection中聲明過的方法
-
向Set(主要指:HashSet、LinkedHashSet)中添加的數據,其所在的類一定要重寫hashCode()和equals()
要求:重寫的hashCode()和equals()儘可能保持一致性:相等的對象必須具有相等的散列碼
重寫兩個方法的小技巧:對象中用作 equals() 方法比較的 Field,都應該用來計算 hashCode 值
三、Set的特點
存儲無序的、不可重複的數據
以HashSet爲例說明:
- 無序性:不等於隨機性。存儲的數據在底層數組中並非按照數組索引的順序添加,而是根據數據的哈希值決定的
- 不可重複性:保證添加的元素按照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的使用
- 向TreeSet中添加的數據,要求是相同類的對象,且能夠比較大小
- 兩種排序方式:自然排序(實現Comparable接口) 和 定製排序(Comparator)
- 自然排序中,比較兩個對象是否相同的標準爲:compareTo()返回0,不再是equals()
- 定製排序中,比較兩個對象是否相同的標準爲: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循環),用於遍歷集合、數組
- 遍歷集合
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);
}
- 遍歷數組
int[] arr = new int[]{1, 2, 3, 4, 5, 6};
//for(數組元素的類型 局部變量 : 數組對象)
for (int i : arr) {
System.out.println(i);
}