【Java入門提高篇】Day20 Java容器類詳解(三)List接口


  今天要說的是Collection族長下的三名大將之一,List,Set,Queue中的List,它們都繼承自Collection接口,所以Collection接口的所有操作,它們自然也是有的。

  List,Set,Queue,分別是列表,集合,隊列的意思,代表着Collection家族下的三種不同的勢力,它們各有所長,也各有所短,就像騎兵,步兵和水兵,各有各的優勢,並沒有誰一定比誰更好的說法,合適的才是最好的。接下來,將會分別介紹這三名大將,從中你也會看到它們各自的特點。

  本篇先來介紹一下List接口。

  我們先來看看List的源碼:

public interface List<E> extends Collection<E> {
    // 查詢接口

    /**
     * 列表元素個數
     */
    int size();

    /**
     * 是否為空
     */
    boolean isEmpty();

    /**
     * 是否包含某元素
     */
    boolean contains(Object o);

    /**
     * 返回一個List迭代器
     */
    Iterator<E> iterator();

    /**
     * 將List轉換為Object數組
     */
    Object[] toArray();

    /**
     * 轉換為指定類型數組
     */
    <T> T[] toArray(T[] a);

    // 修改操作

    /**
     * 添加元素,成功返回true
     */
    boolean add(E e);

    /**
     * 移除某一個元素,成功返回true
     */
    boolean remove(Object o);

    // 批量操作

    /**
     * 判斷是否包含集合C 中的所有元素
     */
    boolean containsAll(Collection<?> c);

    /**
     * 將集合C 中所有元素添加到列表
     */
    boolean addAll(Collection<? extends E> c);

    /**
     * 將集合C 中所有元素添加到列表,添加在序號為index的元素之后
     */
    boolean addAll(int index, Collection<? extends E> c);

    /**
     * 從列表中移除集合C 中所有元素
     */
    boolean removeAll(Collection<?> c);

    /**
     * 從列表中移除所有不在集合C 中的元素
     */
    boolean retainAll(Collection<?> c);

    /**
     * 全部替換
     */
    default void replaceAll(UnaryOperator<E> operator) {
        Objects.requireNonNull(operator);
        final ListIterator<E> li = this.listIterator();
        while (li.hasNext()) {
            li.set(operator.apply(li.next()));
        }
    }

    /**
     * 根據指定的比較器來排序,如果傳入的比較器是null,則元素必須實現Comparable 接口
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }

    /**
     * 移除所有元素
     */
    void clear();

    // 比較和hash

    boolean equals(Object o);

    int hashCode();

    // 根據序號進行的操作

    /**
     * 獲取指定序號的元素
     */
    E get(int index);

    /**
     * 替換指定序號的元素
     */
    E set(int index, E element);

    /**
     * 在指定序號的元素后插入元素
     */
    void add(int index, E element);

    /**
     * 移除指定序號的元素
     */
    E remove(int index);

    // 搜索操作

    /**
     * 返回元素第一次出現的位置,如果未找到則返回-1
     */
    int indexOf(Object o);

    /**
     * 返回元素出現的最后一個位置
     */
    int lastIndexOf(Object o);

    // List迭代器

    /**
     * 返回一個List迭代器
     */
    ListIterator<E> listIterator();

    /**
     * 返回一個序號從Index開始的List迭代器
     */
    ListIterator<E> listIterator(int index);

    // 視圖

    /**
     * 返回一個子隊列,序列從fromIndex到toIndex,包含fromIndex,不包含toIndex
     * 對子隊列的修改會影響原隊列
     * 如果原隊列修改,那么對子隊列的影響是未定義的
     */
    java.util.List<E> subList(int fromIndex, int toIndex);

    /**
     * 創建一個可分割的迭代器(用於並行計算)
     */
    @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, Spliterator.ORDERED);
    }
}

  其實JDK里的注釋已經十分豐富,大家平時有時間可以多看看,為了方便閱讀,我這里用簡單粗暴的語言進行了精簡翻譯。

  List即列表,存儲的是有序集合,里面的元素有序存儲,可以重復,所謂有序集合,顧名思義,就是里面的元素存放是有順序的,每個插入的元素都對應着一個序號,可以根據序號獲取元素。

  List支持的操作也很豐富,最常用的增刪改查,批量添加,批量替換,批量刪除,還有搜索,排序操作,還支持普通迭代器和可分割式迭代器,前者主要用於遍歷,后者則主要用於並行式計算,關於迭代器的知識后面會統一介紹。下面是使用常見操作的一個小栗子:

public class Test {

    public static void main(String[] args){
        test();
    }

    static void test(){
        List<Integer> integers = new ArrayList<>();
        List<Integer> integersA = new ArrayList<>();

        //添加元素
        integers.add(1);
        integers.add(2);
        integers.add(3);
        integers.add(4);

        integersA.add(1);
        integersA.add(2);
        integersA.add(33);
        integersA.add(44);
        System.out.println("列表大小:" + integers.size());
        System.out.println("是否為空:" + integers.isEmpty());
        System.out.println("是否包含某元素:" + integers.contains(2));
        System.out.println("是否包含全部元素:" + integers.containsAll(integersA));

        //轉換為數組
        Integer[] integerArray = integers.toArray(new Integer[0]);
        System.out.println("遍歷數組:");
        for (int i = 0; i < integerArray.length; i++){
            System.out.println(integerArray[i]);
        }
        System.out.println("當前列表integers:" + integers);

        //批量添加
        System.out.println("批量添加元素");
        integers.addAll(integersA);
        System.out.println("當前列表integers:" + integers);

        //移除元素
        System.out.println("移除元素");
        integers.remove(1);
        System.out.println("當前列表integers:" + integers);

        //批量移除
        System.out.println("批量移除元素");
        integers.removeAll(integersA);
        System.out.println("當前列表integers:" + integers);

        //開始替換
        System.out.println("批量替換元素");
        integers.replaceAll(it -> it + 1);
        System.out.println("當前列表integers:" + integers);

        //從列表中移除所有不在集合integersA中的元素
        integersA.add(2);
        integersA.add(4);
        System.out.println("保留元素");
        integers.retainAll(integersA);
        System.out.println("當前列表integers:" + integers);

        //插入
        System.out.println("開始插入");
        System.out.println("當前列表integersA:" + integersA);
        integersA.add(2,155);
        integersA.add(1,125);
        System.out.println("當前列表integersA:" + integersA);

        //排序
        System.out.println("開始排序——使用內部比較器");
        integersA.sort(null);
        System.out.println("當前列表integersA:" + integersA);

        System.out.println("開始排序——使用外部比較器");
        integersA.sort((itA, itB) -> itB - itA);
        System.out.println("當前列表integersA:" + integersA);

        //序號操作
        Integer a = integersA.get(2);
        System.out.println("integersA第三個元素是:" + a);
        System.out.println("開始替換");
        integersA.set(3, 66);
        System.out.println("當前列表integersA:" + integersA);
        System.out.println("開始移除");
        integersA.remove(3);
        System.out.println("當前列表integersA:" + integersA);

        //搜索操作
        System.out.println("查找元素2(第一次出現)位置:" + integersA.indexOf(2));
        System.out.println("查找元素2(最后一次出現)位置:" + integersA.lastIndexOf(2));

        //子隊列操作
        List<Integer> subList = integersA.subList(0, 4);
        System.out.println("子隊列:" + subList);
        subList.add(5);
        subList.add(5);
        subList.add(5);
        System.out.println("當前子列表:" + subList);
        System.out.println("當前列表integersA:" + integersA);

        integersA.add(1, 233);
        integersA.add(1, 233);
        integersA.add(1, 233);
        System.out.println("當前列表integersA:" + integersA);
        System.out.println("當前子列表:" + subList);
    }
}

  大家可以先想想結果,再下看面的答案。

  實際輸出如下:

列表大小:4
是否為空:false
是否包含某元素:true
是否包含全部元素:false
遍歷數組:
1
2
3
4
當前列表integers:[1, 2, 3, 4]
批量添加元素
當前列表integers:[1, 2, 3, 4, 1, 2, 33, 44]
移除元素
當前列表integers:[1, 3, 4, 1, 2, 33, 44]
批量移除元素
當前列表integers:[3, 4]
批量替換元素
當前列表integers:[4, 5]
保留元素
當前列表integers:[4]
開始插入
當前列表integersA:[1, 2, 33, 44, 2, 4]
當前列表integersA:[1, 125, 2, 155, 33, 44, 2, 4]
開始排序——使用內部比較器
當前列表integersA:[1, 2, 2, 4, 33, 44, 125, 155]
開始排序——使用外部比較器
當前列表integersA:[155, 125, 44, 33, 4, 2, 2, 1]
integersA第三個元素是:44
開始替換
當前列表integersA:[155, 125, 44, 66, 4, 2, 2, 1]
開始移除
當前列表integersA:[155, 125, 44, 4, 2, 2, 1]
查找元素33(第一次出現)位置:4
查找元素33(最后一次出現)位置:5
子隊列:[155, 125, 44, 4]
當前子列表:[155, 125, 44, 4, 5, 5, 5]
當前列表integersA:[155, 125, 44, 4, 5, 5, 5, 2, 2, 1]
當前列表integersA:[155, 233, 233, 233, 125, 44, 4, 5, 5, 5, 2, 2, 1]
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239)
    at java.util.ArrayList$SubList.listIterator(ArrayList.java:1099)
    at java.util.AbstractList.listIterator(AbstractList.java:299)
    at java.util.ArrayList$SubList.iterator(ArrayList.java:1095)
    at java.util.AbstractCollection.toString(AbstractCollection.java:454)
    at java.lang.String.valueOf(String.java:2994)
    at java.lang.StringBuilder.append(StringBuilder.java:131)
    at com.frank.chapter20.Test.test(Test.java:115)
    at com.frank.chapter20.Test.main(Test.java:15)

  不知道符不符合你的預期,這里關於內部比較器和外部比較器的知識只一筆帶過,Integer類型是實現了Comparable接口的,所以sort方法傳入null時會使用Integer的內部比較器進行排序,而使用外部比較器時,使用的是Java8的新特性,lamada表達式,省去了方法名和參數類型,因為函數式接口不存在重載方法,所以編譯器可以推斷出參數類型,這樣就不用再大費周章的用new語法去創建一個比較器(當然,只是語法糖而已,如果不是很理解比較器,可以先行百度,后面的文章里也會有詳細介紹)。在最后報出了一個ConcurrentModificationException,因為原隊列修改后,子隊列視圖就被破壞了,所以再次訪問子視圖時就會報錯。

  List是最常用的容器類,List最大的特點便是要求元素有序存儲,List跟數組相比,最大的優勢在於List大小可以動態擴展,但數組支持隨機存取,所以當元素個數的固定的時候,使用數組往往效率更高。(當然,一般情況下還是使用List吧,因為支持的操作更加豐富,比如進行排序時不需要自己寫算法)。

  一般來說,對元素沒有特殊要求,不需要去重存儲,沒有先進先出的要求的場景下,List是最好的選擇。

  List接口下有多個常用的實現類,每個類都有其特點,具體選擇哪種類需要根據實際情況進行選擇。

  希望大家能通過這篇文章,了解List的主要方法及其使用方法以及常用場景,關於List的常見具體實現類的講解將在之后的文章里進行說明和比較。

  本篇到此結束,歡迎大家繼續關注。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM