集合框架
接下來將要學習的內容爲:
1.Collection接口(線性存儲)(容器的最高父接口)
a)List接口(有序(指有下標/索引)、數據可重複)
b)Set接口(無序,數據不可重複)
c)queue隊列
2.Map接口(鍵(key)-值(value)對存儲)(映射關係)
首先了解一個設計模式:適配者模式(通俗的理解是功能基本不變,只是改了方法名)(最主要的功能就是將一個接口轉換爲另一個接口)
List接口的實現類:
-
ArrayList(底層爲數組結構)
在數組中賦值方式爲a[0] = 1; ArrayList底層還是數組,數組賦值是通過腳標賦值的,我現在還要用他賦值的功能,但是對外提供接口變了,賦值變爲了add();
優點:查、改效率高
2.LinkedList(底層爲雙向鏈表)
優點:增、刪效率高
3.Vector(和ArraysList基本上一樣(數組結構),只不過他是線程安全的,訪問效率低)
自己實現容器就需要實現Collection接口,並且實現所有需要實現的方法。Collection沒有get()方法,所以不能定位。它也沒有set()方法。但List裏面有這兩個方法
ArrayList
ArrayList 無參的構造方法,創建了一個長度爲0的數組
ArrayList list = new ArrayList();//默認容量爲0
也有有參的構造方法,指定了初始大小
本質上就是創建了一個參數大小的數組
ArraysList list = new ArrayList(10);//容量設置爲10
//還可以傳一個集合,將傳進的集合中的元素copy給新集合
來看看源碼
public ArrayList(int initialCapacity) { //創建時可傳參
if (initialCapacity > 0) { //如果傳的參數容量大於0
this.elementData = new Object[initialCapacity]; //創建一個大小爲initialCapacity的數組
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA; //如果傳入的參數爲0,那就是默認的那個空數組
} else {
throw new IllegalArgumentException("Illegal Capacity: "+ //如果參數小於0則拋出一個異常
initialCapacity);
}
}
1.添加元素(add())
添加過程:
- 先將底層數組擴容
- 將元素賦值到數組對應位置
- 返回true
list.add(10);//這裏的這個10是基礎數據類型的對象類型:Integer類型
list.add("abc")//添加元素的位置跟add的次序有關,第一個add在數組的第一個位置
源碼
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e; //核心代碼:把這個元素賦給了數組相應位置。
return true; //添加成功,返回一個true。如果添加不成功在上一行代碼中就會報錯,不會執行到這一行
}
//這就是適配者模式,把給數組賦值的功能的名稱轉爲了add,外面添加元素調用add方法就行了,功能沒有變
add還可以給指定位置添加元素
//爲指定位置添加元素 第一個參數:指定位置,第二個參數:元素值
add(index,data);//給index位置添加data數據,其他數據會往後擠
注意:
- 只能向前面有數據的位置插入,不能中間空了好多位置向後插入,比如容量大小爲10,目前只有兩個元素,則只能往0,1,2的位置插入,不能向3,4,5…等位置隔着插入
- ArrayList對象不能存儲基本類型,只能存儲引用類型的數據。類似 不能寫,但是存儲基本數據類型對應的包裝類型是可以的。所以,想要存儲基本類型數據, <> 中的數據類型,必須轉換後才能編寫。
2.獲取元素(get())
list.get(0);
源碼
//get
public E get(int index) {
rangeCheck(index);
return elementData(index);//如果上句不拋異常,則執行到這句,返回相應的元素
}
//rangeCheck()
private void rangeCheck(int index) {
if (index >= size) //如果腳標大於size則拋異常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//elementData()
E elementData(int index) {
return (E) elementData[index];//返回數組相應腳標位置的元素
}
//適配者模式把上面的取值過程轉換爲了一個方法名get()
大家注意一下上面的爲什麼要用index跟size比較而不用index跟elementData.length?(elementData就是我們存數據的數組)
答:size記錄的是現在集合中的元素個數,而elementData.length獲取到的是數組的長度。如果我指定初始容量爲100,而只添加了兩個元素。用elementData.length獲取到的是100,但容量裏面只有兩個數據是有效的,所以存幾個只能取幾個。所以自己在寫底層實現時也要有一個變量來記錄數據的個數,因爲初始數組大小不準。
如果是數組的話,獲取沒放過元素的位置,返回的是默認值。而arraylist的get方法就會報錯
獲取元素個數:size()方法(數組中獲取長度是length,String獲取長度是length())
3.修改元素(set())
返回值爲修改之前的值
list.set(0,"first");//注意他修改後會返回原來的數據
4.刪除元素(remove())
按照下標刪返回刪除之前的元素;按照元素刪除,返回布爾值。
list.remove(int index);//按下標刪除,返回值是刪之前的元素
list.remove(Object o);//按內容刪,返回一個布爾值,刪成功或刪失敗
//刪除一個元素後,後面的元素統一前移
注意:如果元素爲整數,remove的參數爲int,按照下標刪除;參數爲Integer,按照元素刪除
list.remove(3);//按腳標刪
list.remove(new Integer(3));//按元素刪,注意存入的基礎數據類型元素默認是它的對象類型的
如果容器中有多個3,remove一次,刪除的是第一個3
ArrayList的遍歷
ArrayList<String> list = new ArrayList<>();//指定容器寸的內容必須是String的(泛型的一個特點,編譯時就把類型確定了)
//1.8版本之前,後面的<>裏面需要加類型,
//1.8版本之後 可以不用加,因爲1.8版本之後加入了語法糖,可以進行類型推斷
//泛型不支持基本數據類型只支持對象數據類型
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
//遍歷ArrayList
for(int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//迭代器:將Collection集合的遍歷統一起來,迭代器一開始指向空null
Iterator<String> iterator = list.iterator();//注意不是new
/*
//判斷是否有下一個元素,並沒有獲取它的值,而是他的內存地址,如果內存地址都沒有,則值一定沒有
boolean hasNext = iterator.hasNext();
//如果有,返回true,如果沒有,返回false
System.out.println(hasNext);
//過去下一個元素
iterator.next();
*/
while (iterator.hasNext()) {
String e = iterator.next();
// 刪除當前迭代到的對象
//iterator.remove(); 輸出還會是原來的那些元素,這裏是爲了不影響迭代的過程(如果刪除了第一個元素,後面的元素會往前擠),但實際上這個元素確實被刪了,不信你試試輸出size()
System.out.println(e);
}
只有實現Itreable接口才可以使用增強for循環,他的底層就是用迭代器實現的
Iterator只需要記住hasNext()、next()、remove()即可
注:
- 通過迭代器刪除元素,不會影響整個遍歷過程
- 迭代完成後,迭代器的指針就會指向最後一個元素,所以要想再次遍歷就需要重新過去迭代器對象
list集合的迭代器
ArrayList<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
ListIterator<String> listIterator = list.listIterator();
//從上向下
while (listIterator.hasNext()) {
System.out.println(listIterator.next());
}
//從下向上
while (listIterator.hasPrevious()) {
System.out.println(listIterator.previous());
}
//listIterator獨有的方法
//添加元素
listIterator.add("e");
//獲取上一個元素的下標
listIterator.previousIndex();
//獲取下一個元素的下標
listIterator.nextIndex();
Iterator只能從上往下找(相當於單鏈表),ListIterator的特點是既能從上往下找,也能從下往上找。(相當於雙向鏈表)
兩個集合關係的方法
集合的一些方法
ArrayList<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("bb");
list.add("dd");
list.add("ee");
list.add("ff");
//根據元素獲取下標,第一次出現時的下標, -1表示該元素不存在
System.out.println(list.indexOf("aa"));//輸出0
System.out.println(list.indexOf("h"));//輸出-1
System.out.println(list.indexOf("bb"));//輸出1
//最後一次出現的位置,比如獲取某個人的最後一次登錄時間
System.out.println(list.lastIndexOf("bb"));//輸出2
//判斷是否包含某個元素
System.out.println(list.contains("a"));//其實index方法就包含了這個功能,他的底層實現用的就是indexOf
//是否爲空,即size()是不是爲0。返回true爲空
list.isEmpty();
//刪除集合中的所有元素
list.clear();
集合求子集,兩集合求合集、差集、交集
ArrayList<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("bb");
list.add("cc");
list.add("dd");
list.add("ee");
ArrayList<String> list2 = new ArrayList<>();
list2.add("aa");
list2.add("bb");
list2.add("bb");
list2.add("cc");
list2.add("ff");
list2.add("gg");
/*
子集
第一個參數:起始下標(包含)
第二個參數:結束下標(不包含)
*/
List<String> list1 = list.subList(1,3); //父類引用子類對象
System.out.println(list1);
//stulist不能單獨使用,他是定義在ArrayList裏的內部類(且他是私有的,只能由外部類使用),就比如有一個私有成員變量age,如果你都沒這個對象了,那這個age也就不存在了
/*
//求合集 將參數集合中的所有元素都添加到調用方法的集合中
//參數傳的是集合,返回值是布爾型
list.addAll(list2);
System.out.println(list);
*/
/*
//求差集:將list中 list合list1的交集刪除
//參數傳的是集合,返回值是布爾型
//這裏注意一下:如果list2裏只有一個dd元素,而list裏有多個dd元素,則list裏所有的dd都會被刪除
list.removeAll(list2);
System.out.println(list);
*/
//求交集
list.retainAll(list2);
System.out.println(list);
subList源碼
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);//先檢查範圍是否正常
return new SubList(this, 0, fromIndex, toIndex);//當上面不拋異常才返回
}
static void subListRangeCheck(int fromIndex, int toIndex, int size) {
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > size)
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
}
數組和集合的轉換
1.集合轉數組
list.toArray()
,他不靈活,返回值是Object
toArray有個可傳參的方法,他是泛型方法,傳進的參數是什麼類型的它就返回什麼類型<T> T[] toArray(T[] a);
eg:
List<String> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");
list1.add("c");
Object[] objectArray = list1.toArray();
List<String> list2= new ArrayList<>();
list2.add("A");
list2.add("B");
list2.add("C");
String[] strArray= list2.toArray(new String[list2.size()]);
2.數組轉集合
int[] array_int = {1,2,3};
//asList:如果參數爲基本數據類型數組,將整個數組作爲集合的一個元素。如果是對象類型的數組,將數組中的元素轉換到集合中
List list = Arrays.asList(array_int); //將會輸出數組首元素的內存地址
Integer[] array_integer = {1,2,3};//這些元素就是一個一個的Integer對象
Arrays.asList(array_integer);//這樣就將元素一個一個的放進了集合中