Java集合初識
什麼是集合?
Java集合類是一種特別有用的工具類,可用於儲存數量不等的物件,並可以實現常用的資料結構,如棧、佇列等。除此之外,集合還可用於儲存具有對映關係的關聯陣列。
集合與陣列的區別
- 陣列的長度是固定的;集合的長度是可變的。
- 陣列可以儲存基本資料型別,也可以儲存引用資料型別;集合只能儲存引用資料型別。
- 陣列只能儲存同種資料型別的元素;集合可以儲存不同型別的元素。
集合的用途
為了儲存數量不確定的資料,以及儲存具有對映關係的資料,集合類主要負責儲存、盛裝其他資料,因此也被稱為容器類。集合類和陣列不一樣,陣列元素可以是基本型別的值,也可以是物件(實際上儲存的是物件的引用),而集合只能儲存物件(實際上儲存的是物件的引用變數)。
Java的集合類主要由兩個介面派生而出:Collection和Map,這兩者是集合框架的根介面,其中又包含一些子介面或實現類。
Collection和Iterator介面
Collection是List、Set和Queue介面的父介面,該接口裡定義的方法既可以用於操作Set集合,也可以用於操作List和Queue集合。Collection接口裡定義瞭如下操作集合元素的方法。
boolean add(Object o)
:該方法用於向集合裡新增一個元素,如果集合物件被新增的操作改變了則返回true;
//建立一個集合物件 Collection collection = new ArrayList(); collection.add("孫悟空"); collection.add("豬八戒"); collection.add("唐三藏"); System.out.println(collection.add("沙悟淨"));//true System.out.println(collection);//[孫悟空, 豬八戒, 唐三藏, 沙悟淨]
boolean addAll(Collection c)
:該方法把集合c裡的所有元素新增到指定集合裡,如果集合物件被新增操作改變了,則返回true。
//建立第二個集合物件 Collection collection1 = new ArrayList(); collection1.add("孫悟空"); collection1.add("豬八戒"); System.out.println(collection.addAll(collection1));//true System.out.println(collection);//[孫悟空, 豬八戒, 唐三藏, 沙悟淨, 孫悟空, 豬八戒]
void clear()
:清除集合裡的所有元素,將集合長度變為0;
//清空集合
collection1.clear();
System.out.println(collection1);//[]
boolean contains(Object o)
:返回集合裡是否包含指定元素
System.out.println(collection.contains("孫悟空")); //true
boolean containsAll(Collection c)
:返回集合裡是否包含集合c裡的所有元素
//建立第二個集合物件
Collection collection1 = new ArrayList();
collection1.add("孫悟空");
collection1.add("豬八戒");
System.out.println(collection.retainAll(collection1));//true
collection1.add("白龍馬");
System.out.println(collection.retainAll(collection1));//false
boolean isEmpty()
:返回集合是否為空。放集合長度為0時返回true,否則返回falseIterator iterator()
:返回一個Iterator物件,用於遍歷集合裡的元素;int size()
:該方法返回集合裡的元素個數。
Iterator it=collection.iterator();//[email protected]
for (int i=0;i<collection.size();i++){
System.out.print(it.next());
}
boolean remove(Object o)
:刪除集合中的指定元素o,當集合中包含一個或多個元素o時,該方法只刪除第一個符合條件的元素,該方法將返回true。
System.out.println(collection.remove("孫悟空"));
System.out.println(collection);
boolean removeAll(Collection c)
:從集合中刪除集合c裡包含的所有元素,如果刪除一個或多個元素,則該方法返回true;
collection.removeAll(collection1);
System.out.println(collection);//[唐三藏]
boolean retainAll(Collection c)
:從集合中刪除集合c裡不包含的元素,相當於把呼叫該方法的集合變成該集合和集合c的交集。
collection1.add("白龍馬");
collection.retainAll(collection1);
System.out.println(collection);//[孫悟空, 豬八戒]
Object[] toArray()
:該方法把集合轉換成一個數組,所有集合元素變成對應的陣列元素。
使用Lambda表示式遍歷集合
Java8為Iterable介面新增了一個forEach(Consumer action)預設方法,該方法所需引數的型別是一個函式式介面,而Iterable介面是Collection介面的父介面,所以Collection集合可以直接呼叫該方法。
當程式呼叫Iterable的forEach遍歷集合元素時,程式會依次將集合傳給Consumer的accept(T t)方法。正因為Consumer是函式式介面,因此可以使用Lambda表示式來遍歷集合元素。
Collection collection= new ArrayList();
collection.add("Java");
collection.add("Python");
collection.add("C++");
collection.forEach(obj -> System.out.println("迭代集合元素:"+obj));
/*迭代集合元素:Java
迭代集合元素:Python
迭代集合元素:C++*/
傳給forEach方法的引數是一個Lambda表示式,該Lambda表示式的目標型別是Comsumer。forEach方法會自動將集合元素逐個地傳遞給Lambda表示式的形參。
使用Java8增強的Iterator遍歷集合元素
Iterator介面也是java集合框架的成員,它與Collection和Map不同的是,它主要用於遍歷(迭代訪問)Collection中的元素,而其他兩者主要用來盛放元素。
Iterator接口裡的四種方法:
boolean hasNext()
:如果集合元素還沒有被遍歷完,則返回true;Object next()
:返回集合裡的下一個元素;void remove()
:刪除集合裡上一次next方法返回的元素;void forEachRemaining(Consumer action)
:可使用Lambda表示式來遍歷集合元素。
Iterator必須依賴於Collection物件,若有一個Iterator物件,則必然有一個Collection物件與之關聯。
Collection collection= new ArrayList();
collection.add("Java");
collection.add("Python");
collection.add("C++");
//獲取集合對應的迭代器
Iterator it=collection.iterator();
while (it.hasNext()){
//it.next()方法返回的資料型別是Object型別,依尼翠需要強制型別轉換
String book=(String) it.next();
System.out.println(book);
if(book.equals("C++")){
it.remove();
}
//對book變數賦值,不會改變集合本身
book="go語言";
}
System.out.println(collection);
結論
當使用Iterator對集合進行迭代時,Iterator並不是把集合本身傳給迭代變數,而是把集合元素的值傳給了迭代變數,所以修改迭代變數的值對集合元素本身沒有影響。只有通過Iterator的remove方法刪除上一個next方法返回的集合元素才可以,否則如果在迭代過程中修改集合元素將會引發ConcurrentModificationException異常
Collection collection= new ArrayList();
collection.add("Java");
collection.add("Python");
collection.add("C++");
//獲取集合對應的迭代器
Iterator it=collection.iterator();
while (it.hasNext()){
//it.next()方法返回的資料型別是Object型別,依尼翠需要強制型別轉換
String book=(String) it.next();
System.out.println(book);
if(book.equals("C++")){
collection.remove(book);
}
}
System.out.println(collection);
//Exception in thread "main" java.util.ConcurrentModificationException
上面程式碼中的collection.remove(book);位於Iterator迭代塊內,所以在迭代過程中修改了集合元素,所以引發異常。
Iterator迭代器採用的是快速失敗機制,一旦在迭代過程中檢測到該集合被修改,程式立即引發ConcurrentModificationException異常,而不是顯示修改後的結果,這樣可以避免共享資源而引發的潛在問題
使用foreach迴圈遍歷集合元素
除了使用Iterator介面訪問Collection集合裡的元素之外,使用Java5提供的foreach迴圈迭代訪問集合元素更加便捷。
for (Object obj:collection){
String book=(String) obj;
System.out.println(book);
}
使用java8新增的Predicate操作集合
Java8為Collection集合新增了一個removeIF(Predicate filter)方法,該方法將會批量刪除符合filter新增的所有元素。該方法需要一個Predicate物件作為引數,Predicate也是函式式介面,因此可以使用Lambda表示式作為引數。
collection.removeIf(ele ->((String)ele).length()<10);
public static void main(String[] args) {
Collection collection= new ArrayList();
collection.add("Java");
collection.add("Python");
collection.add("C++");
//統計書名中包含“Python”的元素
System.out.println(calAll(collection,ele->((String)ele).contains("Python")));
//統計元素字串長度大於3的元素數量
System.out.println(calAll(collection,ele->((String)ele).length()>3));
}
public static int calAll(Collection books, Predicate p){
int total=0;
for (Object obj:books){
//使用Predicate的test方法判斷搞物件是否滿足Predicate指定的條件
if(p.test(obj)){
total++;
}
}
return total;
}
上面程式碼Lambda表示式目標型別是Predicate。
使用java8新增的Stream操作集合
java8還新增了Stream、IntStream、LongStream等流式API,這些API代表多個支援序列和並行聚集操作的元素。其中Stream是一個通用的流介面,而其他的則對應int、long的流。
java8還為上面流式API提供了對用的Builder,用來通過這些Builder來建立對應的流。
獨立使用Stream的步驟:
- 使用Stream或XxxStream的builder()類方法建立該Stream對應的Builder;
- 重複呼叫Builder的add方法向該流中新增多個元素。
- 呼叫Builder的build方法獲取對應的Stream。
- 呼叫Stream的聚集方法。
IntStream is=IntStream.builder()
.add(10)
.add(-32)
.add(54)
.add(-23)
.build();
//下面聚集方法的程式碼每次只能執行一次
System.out.println("is所有元素中最大值:"+is.max().getAsInt());
System.out.println("is所有元素中最小值:"+is.min().getAsInt());
System.out.println("is所有元素的總數:"+is.count());
System.out.println("is所有元素的平均值:"+is.average());
System.out.println("is所有元素的平方值是否都大於20:"
+is.allMatch(ele->ele*ele>20));
System.out.println("is任何元素的平方值是否都大於20:"
+is.anyMatch(ele->ele*ele>20));
上面執行方法的程式碼每次只能執行一行,因此需要把其他行註釋掉。
Stream提供了大量的方法進行聚集操作,這些方法既可以是“中間的”也可以是“末端的”。
中間方法:
中間操作允許保持開啟狀態,並允許直接呼叫後續方法;
末端方法:
末端方法是對流的最終操作,當對某個Stream執行末端方法後,該流將會被消耗切不可再用。
除此之外,關於流的方法還有如下特徵:
有狀態的方法:
這種方法會給流增加一些新特性,比如元素的唯一性,元素的最大數量、保證元素以排序的方法被處理等。有狀態的方法往往需要更大的效能開銷。
短路方法:
短路方法可以儘早結束對流的操作,不必檢查所有元素。
中間方法的簡單介紹
filter(Predicate predicate)
:過濾Stream中所有不符合Predicate的元素。mapToXxx(ToXxxFunction mapper)
:使用ToXxxFunction對流中的元素執行一對一的轉換,該方法返回的新流中包含了ToXxxFunction轉換生成的所有元素。peek(Consumer action)
:依次對每個元素執行一些操作,該方法返回的流與原流包含相同的元素。distinct()
:該方法用於排序流中所有重複的元素。這是一個有狀態的方法。sorted():
該方法用於保證流中的元素在後續的訪問中處於有序狀態,這是一個有狀態的方法。limit(long maxSize)
:該方法用於保證對該流的後續訪問中最大允許訪問的元素的個數。這是一個有狀態的、短路的方法。
末端方法介紹
forEach(Consumer action):
遍歷流中的所有元素,對每個元素執行action;toArray():
將流中所有元素轉換成一個數組;reduce():
該方法有三個過載版本,都用於通過某種操作來合併流中的元素。min()、max()
:分別返回流中的最小值和最大值;count():
返回流中所有元素的數量;anyMatch(Predicate predicate):
判斷流中是否至少包含一個元素滿足Predicate條件;AllMatch(Predicate predicate):
判斷流中是否所有元素滿足Predicate條件;noneMatch(Predicate predicate):
判斷流中是否所有元素都不符合Predicate條件;findFirst():
返回流中的第一個元素。findAny():
返回流中的任意一個元素。
除此之外java8允許使用流式API來操作集合,Collection介面提供了一個stream預設方法,該方法可返回該集合對應流,接下來即可通過流式API來操作集合了。
Collection collection= new ArrayList();
collection.add("Java");
collection.add("Python");
collection.add("C++");
collection.add("go語言");
//統計元素中包含字元o的元素個數
System.out.println(collection.stream().filter(ele->((String)ele).contains("o")).count());
//統計元素字串大於3的元素個數
System.out.println(collection.stream().filter(ele->((String)ele).length()>3).count());
//先呼叫Collection物件的Stream方法將集合轉換為Stream物件
//再呼叫Stream的mapToint方法獲取原有Stream對應的IntStream
collection.stream().mapToInt(ele->((String)ele).length())
.forEach(System.out::println);