1. 程式人生 > 其它 >集合框架與迭代器

集合框架與迭代器

集合框架

集合是陣列的升級,陣列也是一種集合,集合是一種容器,用於存放物件的容器。不同陣列的是,集合可以存放不同的型別,也不限數量。如果在集合中存放基本資料會自動裝箱轉換成對應的引用型別。

集合存放的是物件的引用(記憶體地址),物件本身還是在堆中。集合也是一個引用

集合結構

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方法混合使用