1. 程式人生 > >Java集合初識

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,否則返回false
  • Iterator 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);