集合框架與迭代器
集合框架
集合是陣列的升級,陣列也是一種集合,集合是一種容器,用於存放物件的容器。不同陣列的是,集合可以存放不同的型別,也不限數量。如果在集合中存放基本資料會自動裝箱轉換成對應的引用型別。
集合存放的是物件的引用(記憶體地址),物件本身還是在堆中。集合也是一個引用
集合結構
java.util.*
該包下存放所有的集合和集合的介面
集合又分三類,有序集合List,無序集合Set,另類是Map介面系列的集合。其中,List和Set都是Collection的子介面。而Collection又是Iterator的子介面,Iterator是迭代器,用於遍歷集合中元素的介面;Map系列的集合是以鍵-值對儲存的,Collection系列的集合是單個儲存的。
不同的集合,底層對應的資料結構不同,資料結構分:陣列、二叉樹、連結串列等。資料結構不同,儲存分式不同。
Collection結構圖
Map結構圖
以上,圖中展示的是常用的類,當然還有其他的集合,其中的方法也差不多大同小異,可以自行查閱jdk文件
Q:無序不可重複,無序指的是儲存和讀取時的順序,而不是排列順序。不可重複是指儲存的元素不可以重複,比如列表裡已經有了1,那麼再存進去1,元素個數也不會增加
Iterable介面
是Collection集合的頂層介面,Iterable是迭代介面,用於獲取迭代器。
主要方法:
Iterator<T> iterator()
返回一個 T 型別的迭代器
Iterator迭代器
用於遍歷集合中的元素。E代表泛型,用於表示未確認的資料型別。
方法
boolean hasNext()
判斷容器是否還有可供訪問的元素,如果具有更多元素,則返回
true
。
用於判斷集合是否還有下一個元素
E next()
讓迭代器向前進一位 ,並返回前進反指向的元素。
返回指向的當前元素
例:Collection集合的迭代遍歷
Collection list = new ArrayList(); list.add(10.12); list.add(true); list.add("String"); list.add(new Object()); //獲取迭代器 Iterator iterator = list.iterator(); //判斷是否還有下一位元素 while(iterator.hasNext()){ //前進到下一位,並返回當前元素 Object o = iterator.next(); System.out.println(o); } /* 10.12 true String java.lang.Object@1b6d3586 Process finished with exit code 0*/
Q:迭代器最開始並不是指向集合的第一位,當第一次呼叫next時迭代器才會進入集合指向第一個元素
Collection介面結構
Collection下又細分Set、List兩種。
List系列特點:儲存的元素是有序的,先存進去的在讀取時先出來,集合中的元素都有對應的下標(索引),可以存在重複的元素
Set系列特點:儲存的元素無序,集合中的元素沒有下標,且儲存的元素不可重複
在沒有使用泛型前,Collection可以儲存任意的Object子型別,使用泛型後,Collection只能儲存某個具體的型別。
Q:集合中不能儲存基本型別,如果在新增元素時放的是基本型別,會自動裝箱;集合中儲存的是物件的引用,不是物件本身
常用方法
boolean add(E e);
向集合中新增元素。如果新增成功,則返回true,否則反之
int size();
返回素閤中的元素個數
void clear();
清空該集合的元素
boolean contains(Object o);
判斷集合中是否包含指定的元素。包含返回true,否則反之
boolean remove(Object o);
刪除集合中指定的元素。刪除成功返回true,否則反之
boolean isEmpty();
判斷集合是否為空。
Object[] toArray();
將該集合轉換為一個數組,返回一個Object型別的陣列
例:
Collection colls = new ArrayList();
colls.add(100);
colls.add("string");
colls.add(10.12);
colls.add(new Object());
colls.add(true);
System.out.println(colls.isEmpty());
System.out.println(colls.contains(false));
Object[] objects = colls.toArray();
for (Object o : objects) {
System.out.println(o);
}
System.out.println(colls.size());
System.out.println(colls.remove(10.12));
System.out.println(colls.size());
colls.clear();
System.out.println(colls.size());
/*
false
false
100
string
10.12
java.lang.Object@1b6d3586
true
5
true
4
0
Process finished with exit code 0*/
contains、remove方法
以ArrayList原始碼為例。
contains
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
由原始碼可知,contains方法底層是通過被引數去呼叫equals方法,從而去比較集合中的各個元素
儲存到集合的元素強烈建議重寫equals方法,不然在判斷集合中是否包含某個物件時比較的是地址值
例:contains比較兩個String元素
Collection c = new ArrayList();
String s1 = new String("123");
c.add(s1);
String x = new String("123");
System.out.println(c.contains(x));
/*
true
Process finished with exit code 0*/
c.contains(x) == x.equals(s1),而String的equals是比較內容的,也就是 “123” =?= “123” => true,所以上面返回的是true
如果比較的引數型別繼承Object沒有重寫equals,那麼其比較的必須是地址值,兩個不同的物件去比較equals那麼就返回false。
如果比較的引數型別重寫了equals,比較的是內容,就算是兩個不同的物件,只要他們的內容相同,那麼就返回true
remove
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
ArrayList在做刪除元素動作時,底層也會做equals校驗
如果集合中有多個重複的元素,那麼會刪除第一個校驗成功的元素。
Q:如果儲存的元素型別沒有重寫equals,那麼可能無法刪除。因為沒有重寫過equals的型別,其比較的是兩個物件的地址值。
例:remove刪除ArrayList中的元素
Collection c = new ArrayList();
String s1 = new String("123");
c.add(s1);
String s2 = new String("123");
c.add(s2);
System.out.println(c.size());// 2
Sting x = new String("123");
c.remove(x);
System.out.println(c.size());//1
/*
2
1
Process finished with exit code 0*/
存在重複的元素只會刪除第一個與之校驗成功的元素。上面刪除的是‘s1’
迭代器的remove方法
獲取迭代器Iterator時,迭代器會綁定當前狀態下的集合。如果集合結構發生了改變,則需要重新獲取迭代器,不然會出現:java.util.CocurrentModificationExcetion
異常。
Collection list = new ArrayList();
c.add("hello");
c.add("word");
c.add(12153);
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Object o = iterator.next();
if (o.equals(10.12))
iterator.remove(o);
}
/*
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.collection.IteratorBase.main(IteratorBase.java:7)
Process finished with exit code 1*/
在獲取了迭代器後禁止再去改變集合結構(add、remove)。因為此時的迭代器無法主動同步集合的操作,java會認為這是執行緒不安全的
如果想在迭代時刪除集合的元素可以使用迭代器的remove方法,它會去同步集合的刪除操作。
Collection list = new ArrayList();
c.add("hello");
c.add("word");
c.add(12153);
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Object o = iterator.next();
if (o.equals(10.12))
iterator.remove(o);
}
集合遍歷拓展
1.foreach迴圈
foreach迴圈可以與任何實現了Iterable介面的物件一起工作。Collection介面繼承了Iterable介面,因此,類庫中的任何集合都可以使用foreach做元素遍歷
Collection list = new ArrayList();
list.add(10.12);
list.add(true);
list.add("String");
list.add(new Object());
System.out.println("-------------");
for (Object obj:
list) {
System.out.println(obj);
}
2.forEachRemaining方法
在Java SE 8中,還可以通過呼叫forEachRemaining
方法並提供一個lambda表示式完成對集合遍歷
Collection list = new ArrayList();
list.add(10.12);
list.add(true);
list.add("String");
list.add(new Object());
Iterator iterator = list.iterator();
iterator.forEachRemaining(element -> System.out.println(element));
forEachRemaining方法最好不要與next方法混合使用