Java 知識點整理-9.Java集合框架 集合概述+Collection介面+Iterator介面+迭代器+List介面+併發修改異常+ListIterator介面+Vector類+List子類
本章會用大量程式碼做具體演示。統一說明:返回型別E在JDK1.5之前是Object,1.5之後等泛型再講。
先介紹一個偶然發現的快捷鍵和一個很實用的快捷鍵:
Alt + Shift + N 快速呼叫建立選單。直接按開頭的首字母進行建立即可,某些時候感覺比Ctrl + N更快捷。
Alt+Shift+R 選中變數後,可集體改名。
目錄
7、案例演示:Collection儲存自定義物件並用迭代器遍歷(while實現+for實現)
3、案例演示:List集合儲存學生物件並遍歷(通過size()和get()方法結合使用遍歷)。
1、案例演示:需求:我有一個集合,請問,我想判斷裡面有沒有"world"這個元素。如果有,我就新增一個"javaee"元素,請寫程式碼實現。
4、Vector和ArrayList、LinkedList三者之間的區別,共同點。
集合概述
1、陣列中儲存的不是物件,而是儲存記錄物件的地址值。
2、陣列和集合儲存引用資料型別,存的都是地址值。
3、集合的由來:
陣列長度是固定的,當新增的元素超過了陣列的長度時,需要對陣列重新定義,太麻煩。java內部給我們提供了集合類,能儲存任意物件,長度是可以改變的,隨著元素的增加而增加,隨著元素的減少而減少。
4、陣列和集合的區別:
區別1(儲存型別) :
陣列既可以儲存基本資料型別,又可以儲存引用資料型別,基本資料型別儲存的是值,引用資料型別儲存的是地址值。
集合只能儲存引用資料型別(物件),集合中也可以儲存基本資料型別,但是在儲存的時候會自動裝箱變成物件。
100→new Integer(100)
區別2(儲存長度):
陣列長度是固定的,不能自動增長。
集合的長度的是可變的,可以根據元素的增加而增長。
5、陣列和集合什麼時候用:
如果元素個數是固定的推薦用陣列。效率比較高,比較節省記憶體。集合的長度是一點點增長上去的,會把原來的物件變成垃圾。
如果元素個數不是固定的推薦用集合。
6、集合體系圖:
Collection介面
1、介面 Collection<E>的概述:
public interface Collection<E> extends Iterable<E>,Collection層次結構中的根介面。Collection表示一組物件,這些物件也稱為collection的元素。一些collection允許有重複的元素,而另一些則不允許。一些collection是有序的,而另一些則是無序的。
JDK不提供此介面的任何直接實現:它提供更多具體的子介面(如Set和List)實現。
java.util包下,使用需要導包。介面不能直接例項化。
<>是泛型,先不講,暫時不加。
2、Collection<E>介面方法:
boolean add(E e) 確保此collection包含指定的元素(可選操作)。E代表Object,所以集合中可以新增任何物件。 返回全是true,因為子類Set集合中無序不能儲存相同的元素,那時會返回false,而List集合是有序可以儲存兩個相同的元素,則返回true。
ArrayList的爺爺類AbstractCollection重寫了toString方法:(原始碼)
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext()) //如果集合中沒有元素,就返回空的中括號。
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('['); //新增]
for (;;) { //無限迴圈
E e = it.next(); //拿到每一個元素
sb.append(e == this ? "(this Collection)" : e); //新增元素
if (! it.hasNext())
return sb.append(']').toString(); //最後新增],再轉換成字串返回
sb.append(',').append(' '); //中間用,和空格隔開
}
}
注:如果列印物件的引用出現的結果和Object類裡的toString()結果不一樣,就說明重寫了toString()方法,如果該類中找不到toString()方法,就去他父類中找或去他爺爺類中找。
boolean remove(Object o) 從此collection中刪除指定元素的單個例項,如果存在的話(可選操作)。 已知返回值都是true,所以直接Collection變數呼叫方法即可。
void clear() 移除此collection中的所有元素(可選操作)。
boolean contains(Object o) 如果此collection包含指定的元素,則返回 true 。boolean型別直接列印即可。
boolean isEmpty() 如果此collection不包含元素,則返回 true 。
int size() 返回此collection中的元素個數。
3、消除非提示未使用變數的黃色波浪線。
新出現的黃色波浪線是因為沒加泛型,有一定的安全隱患。
如果不希望看到,游標放上去Ctrl+1,選中@ Add @SuppressWarming 'rawtypes' to 'main()'這個註解。選中後會在main方法上方出現@SuppressWarnings({ "rawtypes", "unchecked"})我們將它們兩個挪到整個類上去,這樣所有方法多都會有這養的控制,“原始型別”,“不檢查”。在之後的演示程式碼中會看到。
Collection生成的註解是原始型別,Collection型別變數.add(引數);生成的註解是不檢查。
4、集合的遍歷之集合轉陣列遍歷:
集合的遍歷:其實就是依次獲取集合中的每一個元素。
把集合轉換成陣列,可以實現集合的遍歷 Object[] arr = Collection變數名.toArray();
Collection<E>介面方法:
Object[] toArray() 返回包含此collection中所有元素的陣列。
演示:
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(new Student("張三",23)); //Object obj = new Student("張三",23);
coll.add(new Student("李四",24));
coll.add(new Student("王五",25));
coll.add(new Student("趙六",26));
Object[] arr = coll.toArray(); //將集合轉換成陣列。
for (int i = 0; i < arr.length; i++) {
Student s = (Student)arr[i]; //向下轉型成Student。
System.out.println(s.getName() + "," + s.getAge());
}
}
解析Collection coll = new ArrayList();
父類引用指向子類物件,所以Collection變數名.add(new 目標類物件); 的引數在新增的時候,目標類物件已經自動提升為Object類物件。
括號內相當於Object obj = new 目標類物件;所以轉換成陣列的時候全部轉換成了Object陣列。雖然儲存著目標類物件,但已經提升為Object型別。
多型的弊端:不能使用子類特有的屬性和功能,需要向下轉型。
5、Collection集合所有帶All功能的演示:
boolean addAll(Collection c) 將指定collection中的所有元素都新增到此collection中(可選操作)。如果錯用了add(Collection c) 將指定collection看成一個物件(單個元素)新增到此集合中。
boolean removeAll(Collection c) 刪除指定集合中包含的所有此集合的元素(可選操作)。 刪除的是交集。
boolean containsAll(Collection c) 如果此集合包含指定 集合中的所有元素,則返回true。即使僅指定集合或僅此集合某元素重複,但各存在要素都包含即返回true。
boolean retainAll(Collection c) 僅保留此集合中包含在指定集合中的元素(可選操作)。取交集,如果呼叫的集合改變就返回true,如果呼叫的集合不變就返回false。
解析:即使目標集合包含呼叫集合所有元素,返回值也是false。即目標集合包含此集合時,返回false。此集合真包含目標集合時,返回true。即使目標集合無與此集合相同的元素,變數中儲存值變成空中括號,返回值為true。
總結:當集合變數中元素在呼叫此方法後,不發生任何變化,則retain()返回為true;變數元素減少,則返回為false。
Iterator介面+迭代器
1、迭代器概述:
集合是用來儲存元素,儲存的元素需要檢視,那麼就需要迭代(遍歷) 。迭代就是遍歷,便利就是迭代。
2、Collection<E>介面方法:
Iterator<E> iterator() 返回在此collection的元素上進行迭代的迭代器。(獲取迭代器)
3、Iterator<E>介面概述:
public interface Iterator<E> 對collection進行迭代的迭代器。java.util包下,使用需要導包。
4、迭代器與列舉有兩點不同:
ⅰ.迭代器允許呼叫者利用定義良好的語義在迭代期間從迭代器指向的集合移除元素。
ⅱ.方法名稱得到了改進。
5、Iterator介面方法:
boolean hasNext() 如果仍有元素可以迭代,則返回true。
E next() 返回迭代的下一個元素。
void remove() 從迭代器指向的collection中移除迭代器返回的最後一個元素(可選操作)。
6、案例演示:迭代器的使用:
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
//對集合中的元素迭代(遍歷)
/*Iterator it = c.iterator(); //獲取迭代器
boolean b1 = it.hasNext(); //判斷集合是否有元素,有就返回true
Object obj1 = it.next();
System.out.println(b1);
System.out.println(obj1);
Object obj2 = it.next();
System.out.println(it.hasNext());
System.out.println(obj2);*/
Iterator it = c.iterator();
while(it.hasNext()) { //利用while迴圈實現上述操作
System.out.println(it.next());
}
}
7、案例演示:Collection儲存自定義物件並用迭代器遍歷(while實現+for實現)
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import com.bean.Student;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Demo6_Iterator {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(new Student("張三",23));
c.add(new Student("李四",24));
c.add(new Student("王五",25));
c.add(new Student("趙六",26));
//僅使用for的判斷功能,達到和while實現相同的功能,即用迭代器對自定義物件進行遍歷
for(Iterator it = c.iterator(); it.hasNext(); ) {
Student s = (Student)it.next(); //向下轉型
System.out.println(s.getName() + "," + s.getAge()); //獲取物件中的姓名和年齡
}
System.out.println("------------------------------");
Iterator it = c.iterator(); //獲取迭代器
while(it.hasNext()) { //判斷集合中是否有元素
//System.out.println(((Student)(it.next())).getName() + "," + ((Student)(it.next())).getAge());
Student s = (Student)it.next(); //向下轉型
System.out.println(s.getName() + "," + s.getAge()); //獲取物件中的姓名和年齡
}
}
}
8、迭代器原理:
迭代器是對集合進行遍歷,而每一個集合內部的儲存結構都是不同的,所以每一個集合存和取都是不一樣的,那麼就需要在每一個類中都定義方法hasNext()和next()。雖然這樣做是可以的,但是會讓整個集合體系過於臃腫。迭代器則是將這樣的方法向上抽取出介面,然後在每個類的內部,定義了自己的迭代方式。
這樣做的好處有二:
第一,規定了整個集合體系的遍歷方式都是hasNext()和next()方法。
第二,程式碼由底層內部實現,使用者不用管怎麼實現的,會用即可。
9、迭代器原始碼解析:
1,在eclipse中ctrl + shift + t找到ArrayList類。
2,ctrl+O查詢iterator()方法。
3,檢視返回值型別是new Itr(),說明Itr這個類實現Iterator介面。
4,查詢Itr這個內部類,發現重寫了Iterator中的所有抽象方法 。
原始碼:
public Iterator<E> iterator() {
return new Itr(); //Itr應該是Iterator的子類,這裡返回的是Iterator的子類物件。因為不能new一個Iterator返回,只能new一個Iterator的子類物件。
}
private class Itr implements Iterator<E> { //Itr實現Iterator,是Iterator子類,把Iterator所有的方法進行了重寫。所以真正呼叫的是Itr中的hasNext()和Next()。
int cursor; // index of next element to return //cursor指標,初始化為0。
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() { //重寫了hasNext() 判斷是否有元素
return cursor != size; //判斷元素個數size是否等於0,不等於則返回true。證明呼叫集合是有元素的。
}
@SuppressWarnings("unchecked")
public E next() { //重寫了next() 獲取元素,並把指標向後加一。
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1; //每呼叫一次next(),cursor向後加一一次,指標向後移動一次。
return (E) elementData[lastRet = i];
}
public void remove() { //重寫了remove()
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
List介面
1、介面List<E>集合的概述:
public interface LIst<E> extends Collection<E>,有序集合(也稱為序列 )。
此介面的使用者可以精確控制列表中每個元素的插入位置。 使用者可以根據元素整數索引(列表中的位置)訪問元素,並搜尋列表中的元素。
與Set不同,列表通常允許重複的元素。更確切的講,列表通常允許滿足e1.equals(e2)的元素對e1和e2,並且如果列表半身允許null元素的話,通常它們允許多個null元素。
java.util包,使用需要導包。Collection的子介面。同樣是介面不能new,找他的子類ArrayList。
2、List集合的特有功能概述:
void add(int index, E element) 將指定的元素插入此列表中的指定位置(可選操作)。
add功能演示:
import java.util.ArrayList; import java.util.List; @SuppressWarnings({ "rawtypes", "unchecked" }) public class Demo1_List { public static void main(String[] args) { List list = new ArrayList(); //父類引用指向子類物件,鎮長開發的時候,直接ArrayList list = new ArrayList(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); list.add(4, "f"); //index <= size && index >= 0都不會報異常。允許在屁股後面加元素。 // list.add(1, "e"); //在索引為1的位置上插入元素"e" // list.add(10, "z"); //IndexOutOfBoundsException索引越界異常,當儲存是使用不存在的索引就會出現索引越界異常 System.out.println(list); } }
E remove(int index) 刪除該列表中指定位置的元素(可選操作)。
使用:
Object obj = list.remove(索引);通過索引刪除元素,將被刪除的元素返回。
一個問題:
public static void main(String[] args) { List list = new ArrayList(); list.add(111); list.add(222); list.add(333); list.remove(111); //刪除的時候不會自動裝箱,不會刪除一個new Integer物件。只要給整數都被當做索引。 System.out.println(list); //IndexOutOfBoundsException索引越界異常,他把111當成索引了。 }
E get(int index) 返回此列表中指定位置的元素。Object obj = list.get(索引); 通過該方法遍歷集合中所有元素,屬於List集合特有的方式,Set集合沒有索引。
演示:通過索引遍歷List集合:
public static void main(String[] args) { List list = new ArrayList(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } }
E set(int index, E element) 用指定的元素(可選操作)替換指定位置的元素。
3、案例演示:List集合儲存學生物件並遍歷(通過size()和get()方法結合使用遍歷)。
import java.util.ArrayList;
import java.util.List;
import com.bean.Student;
/**
* 向List集合中儲存學生物件。
* 通過size()和get()方法結合使用遍歷。
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Demo2_List {
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Student("張三", 23)); //Object obj = new Student("張三" ,18);
list.add(new Student("李四", 24));
list.add(new Student("王五", 25));
list.add(new Student("趙六", 26));
for(int i = 0; i < list.size(); i++) {
Student s = (Student)list.get(i);
System.out.println(s.getName() + "," + s.getAge());
}
}
}
併發修改異常
1、案例演示:需求:我有一個集合,請問,我想判斷裡面有沒有"world"這個元素。如果有,我就新增一個"javaee"元素,請寫程式碼實現。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* 需求:我有一個集合,請問,我想判斷裡面有沒有"world"這個元素。如果有,我就新增一個"javaee"元素,請寫程式碼實現。
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Demo3_List {
public static void main(String[] args) {
List list = new ArrayList(); //父類引用指向子類物件
list.add("a"); //Object obj = new String("a");自動型別提升
list.add("b");
list.add("world");
list.add("c");
list.add("d");
list.add("e");
/*Iterator it = list.iterator(); //獲取迭代器
while(it.hasNext()) { //判斷集合中是否有元素
String str = (String)it.next(); //向下轉型
if(str.equals("world")) {
list.add("javaee"); //這裡會丟擲ConcurrentModificationException併發修改異常.集合遍歷的同時在增加元素,叫做併發修改。
}
}*/
ListIterator lit = list.listIterator(); //獲取迭代器(List集合特有的)如果想在遍歷的過程中新增元素,可以用ListIterator中的add方法。
while(lit.hasNext()) {
String str = (String)lit.next();
if(str.equals("world")) {
// list.add("javaee"); //ConcurrentModificationException
lit.add("javaee");
}
}
System.out.println(list);
}
}
2、併發修改異常概述:
public class ConcurrentModificationException extends RuntimeException,當方法檢測到物件的併發修改,但不允許這種修改時,丟擲此異常。java.util包下。
3、解決方案:
ⅰ.迭代器迭代元素,迭代器修改元素(通過ListIterator的特有功能add)。集合不動。
ⅱ.集合遍歷元素,集合修改元素。
ListIterator介面
1、List<E>介面方法:
ListIterator<E> listIterator() 返回此列表元素的列表迭代器(按適當順序)。
2、介面ListIterator<E>概述:
public interface ListIterator<E> extends Iterator<E> 系列表迭代器,允許程式設計師按任一方向遍歷列表、迭代期間修改列表,並獲得迭代器在列表中的當前位置。java.util包下。
3、介面ListIterator<E>方法:
void add(E e) 將指定的元素插入列表(可選操作)。
boolean hasNext() 以正向遍歷列表時,如果列表迭代器有多個元素,則返回true(換句話說,如果next返回一個元素而不是丟擲異常,則返回true)。翻譯:是否有下一個。
E next() 返回列表中的下一個元素。翻譯:返回下一個元素。
boolean hasPrevious() 如果以逆向遍歷列表,列表迭代器有多個元素,則返回true。翻譯:是否有前一個。
E previous() 返回列表中的前一個元素。翻譯:返回上一個元素。
4、演示:
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Demo4_ListIterator {
public static void main(String[] args) {
List list = new ArrayList(); //父類引用指向子類物件
list.add("a"); //Object obj = new String("a");自動型別提升
list.add("b");
list.add("world");
list.add("c");
list.add("d");
list.add("e");
ListIterator lit =list.listIterator(); //獲取迭代器
while(lit.hasNext()) {
System.out.println(lit.next()); //獲取元素並將指標向後移動
}
System.out.println("------------------");
while(lit.hasPrevious()) { //如果指標指向0,直接返回false。所以需要先正著遍歷,再反著遍歷。
System.out.println(lit.previous()); //獲取元素並將指標向前移動
}
}
}
Vector類
1、Vector類概述:
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serialisable,Vector類可以實現可增長的物件陣列。
與陣列一樣,它包含可以使用整數索引進行訪問的元件。但是,Vector的大小可以根據需要增大縮小,以適應建立Vector後進行新增或移除項的操作。
從父類繼承了Iterator的方法。
2、Vector類特有功能:
public void addElement(E obj) 將指定的元件新增到此向量的末尾,將其大小增加1。 Element元素。
public E elementAt(int index) 返回指定索引處的元件。
public Enumeration elements() 返回此向量的元件的列舉。
3、介面Enumeration<E> 概述:
public interface Enumeration<E> ,實現Enumeration介面的物件,它生成一系列元素,一次生成一個。連續呼叫nextElement方法將返回一系列的連續元素。
注:此介面的功能與Iterator介面的功能是重複的。此外,Iterator介面添加了一個可選的移除操作,並使用較短的方法名。新的實現應該優先考慮使用Iterator介面而不是Enumeration介面。
5、介面Enumeration方法:
boolean hasMoreElements() 測試此列舉是否包含更多的元素。相當於Iterator中的hasNext()
E nextElement() 如果此列舉物件至少還有一個可提供的元素,則返回此美劇的下一個元素。相當與Iterator中的next()。
6、案例演示 :Vector的迭代:
import java.util.Enumeration;
import java.util.Vector;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Demo5_Vector {
public static void main(String[] args) {
Vector v = new Vector(); //建立集合物件,List的子類
v.addElement("a");
v.addElement("b");
v.addElement("c");
v.addElement("d");
//Vector迭代
Enumeration en = v.elements(); //獲取列舉
while(en.hasMoreElements()) { //判斷集合中是否有元素
System.out.println(en.nextElement());//獲取集合中的元素
}
}
}
List的三個子類
1、資料結構之陣列、連結串列特點:
資料結構之陣列: 查詢快,修改也快,增刪慢。資料結構之連結串列:查詢慢,修改也慢,增刪快。
2、List的三個子類的特點:
ArrayList:底層資料結構是陣列,查詢快,增刪慢。執行緒不安全,效率高。
Vector:底層資料結構是陣列,查詢快,增刪慢。執行緒安全,效率低。
Vector相對ArrayList查詢慢(執行緒安全的)。
Vector相對LinkedList增刪慢(陣列結構)。
LinkedList:底層資料結構是連結串列,查詢慢,增刪快。執行緒不安全,效率高。
3、LinkedList底層連結串列實現的程式碼:
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) { //當索引小於集合長度的一半
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next; //集合從頭向尾遍歷
return x;
}else { //當索引大於集合長度的一半
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev; //集合從尾向頭遍歷
return x;
}
}
4、Vector和ArrayList、LinkedList三者之間的區別,共同點。
Vector和ArrayList的區別:Vector是執行緒安全的,效率低。ArrayList是執行緒不安全的,效率高。
Vector和ArrayList的共同點:都是陣列實現的。
ArrayList和LinkedList的區別:ArrayList底層是陣列結構的,查詢和修改快。LinkedList底層是連結串列結構的,增和刪比較快,查詢和修改比較慢。
ArrayList和LinkedList的共同點:都是執行緒不安全的。
5、List有三個兒子,我們到底使用誰呢?
查詢多用ArrayList,增刪多用LinkedList,如果都多ArrayList。Vector只在面試的時候用,已經被ArrayList替代了,之後會學個工具類,其中有個方法能使執行緒不安全的集合,變成執行緒安全的。預設用ArrayList。