數組、ArrayList、鏈表、LinkedList


數組 

  數組
數組類型

不可重復

無序(線性查找)

可重復(找到第一個即可)

無序(線性查找)

不可重復

有序(二分查找)

可重復(找到第一個即可)

有序(二分查找)

插入 O(N)

O(1)

O(logN+N) O(logN+N)
查詢 O(N) O(N) O(logN) O(logN)
刪除(無洞) O(N) O(N) O(lonN+N) O(logN+N)
總結 可重復無序插入快、下標已知更新查找快;查找刪除慢、大小固定 查找快;插入刪除慢、大小固定
應用   員工表,雇用解雇不經常發生
java數組 無序、可重復;插入快、查詢刪除慢、大小固定;如果已知下標,更新查找快
ArrayList 大小可以擴展;但這是以犧牲效率為代價的
Vector 大小可以擴展;但這也是以犧牲效率為代價的

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 java 數組(無序、可重復)

已知下標查找更新快O(1)
String str = strs[1];
strs[1] = "花";

查找慢O(N)
int index = findChar("花", strs);
刪除慢O(N)
deleteChar("花", strs);
中部插入慢O(N)
insertCharWithMiddle("興", 1, strs);
大小固定

    public static void main(String[] args) {
        String[] strs = {"中", "華", "人", "民", "共", "和", "國", null, null, null, null};
        print(strs);
        // 已知下標查找更新快
        System.out.println(strs[1]);
        strs[1] = "花";
        print(strs);
        // 查找慢,需要花費O(N)的時間
        int index = findChar("花", strs);
        if (index == strs.length) {
            System.out.println("Can't find this char");
        } else {
            System.out.println("Find this char");
        }
        // 刪除慢,需要花費O(N)的時間
        deleteChar("花", strs);
        print(strs);
        // 中部插入慢,需要花費O(N)的時間
        insertCharWithMiddle("興", 1, strs);
        print(strs);
    }

    private static void insertCharWithMiddle(String str, int index, String[] strs) {
        for (int i = strs.length - 2; i >= index; i--) {
            strs[i + 1] = strs[i];
        }
        strs[index] = str;
    }

    private static void deleteChar(String str, String[] strs) {
        int index = findChar(str, strs);
        if (index != strs.length) {
            for (int i = index; i < strs.length - 2; i++) {
                strs[i] = strs[i + 1];
            }
            strs[strs.length - 1] = null;
        }
    }

    public static int findChar(String str, String[] strs) {
        for (int i = 0; i < strs.length; i++) {
            if (strs[i].equals(str)) {
                return i;
            }
        }
        return strs.length;
    }

    public static void print(String[] strs) {
        System.out.println(Arrays.asList(strs));
    }
View Code

 ArrayList

末尾插入快,已知下標查找快更新快
一個參數的add("xxx")方法效率高O(1)
get(1)方法效率高O(1)——已知下標查找快
set(1, "xxx")方法效率高O(1)——已知下標更新快

中部插入、查詢、刪除慢
add(1, "xxx")方法效率低O(N)——中部插入
contains、indexOf方法效率低O(N)——查詢慢
remove(1),remove("xxx")方法效率低O(N)——刪除慢

數組固定大小,雖然ArrayList可以自動擴展,但是以犧牲效率為代價的。

    public static void main(String[] args) {
        List<String> list = new ArrayList<>(8);
        // add方法效率高O(1)
        list.add("中");
        list.add("華");
        list.add("人");
        list.add("民");
        list.add("共");
        list.add("和");
        list.add("國");
        System.out.println(list);
        // get(1),方法效率高O(1),如果知道下標查找快
        System.out.println(list.get(1));

        // add(1, "xxx")方法效率低O(N),中部插入慢
        list.add(1, "花");
        System.out.println(list);
        // 刪除慢O(N)
        list.remove(1);
        list.remove("人");
        System.out.println(list);
        // contains、indexOf方法都比較慢,需要O(N)的時間
        System.out.println(list.contains("中"));;
        System.out.println(list.indexOf("中"));
    }
View Code

ArrayList容量擴展源碼分析

package java.util;

import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import sun.misc.SharedSecrets;

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

    // 默認容量是10
    private static final int DEFAULT_CAPACITY = 10;
    
    // 默認最大容量是MAX_ARRAY_SIZE,實際可以擴展至Integer的最大值
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    public boolean add(E e) {
        // ensure /ɪn'ʃʊə/ 確保  Capacity /kəˈpæsəti/ 容量 
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        // 原來數組的元素個數加上新集合的容量
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == EMPTY_ELEMENTDATA) {
            // System.out.println(Math.max(10, 11)); 輸出11,比較兩個數字,返回大的數字
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
    private void ensureExplicitCapacity(int minCapacity) {
        // 父類的一個成員變量,應該是修改次數的記錄
        modCount++;
        // 數組容量不夠
        if (minCapacity - elementData.length > 0)
            // grow /grəʊ/ 擴大
            grow(minCapacity);
    }
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        // System.out.println(20 >> 1); 結果是20的二分之一,10
        // 1、擴展后的數組是原來數組加上原來數組的一半,適用於add(E e)方法
        // add(int index, E e)指定的下標越界會報異常,下標必須正確,不存在擴容
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 2、擴展后的數組是指定的下標值,比如原有容量是10,addAll一個有8個元素的集合
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 3、擴展后的數組是Integer的最大值,默認的最大值是Integer.MAX_VALUE - 8        
        // private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    }
}    
View Code

鏈表

除非頻繁通過下標訪問各個數據,否則都可以使用鏈表代替數組

鏈表也可以是有序的無序的,可重復的不可重復的

簡單的一個鏈表定義

class Link {
    private long id;        // data
    private String name;    // data
    private byte age;        // data
    private String address; // data
    private Link next;
}
class LinkList {
    private Link first;
}
View Code

最后一個元素的next引用是null

單鏈表

insertFirst();
deleteFirst();
isEmpty(); // 是否為空
find(); // 遍歷,查找指定Link
delete(); // 遍歷,刪除指定Link
insertAfter(); // 遍歷,插入

雙端鏈表

新增對最后一個Link的引用
insertLast();
表頭多次插入會顛倒Link插入的順序;表尾多次插入會保持Link插入的順序
雙端鏈表也不能提高刪除最后一個鏈接點的效率

鏈表的效率

表頭插入查詢刪除快,O(1)
中部插入查詢刪除慢,需要O(N)次比較;在數組中執行這些操作也需要O(N)次比較,但是鏈表仍然要快一些,因為鏈表不需要移動元素,只需要比較,而復制時間大於比較時間

有序鏈表

只有insert()方法與無序鏈表中的insert()方法不同
效率:插入刪除O(N),刪除最小值O(1)

雙向鏈表

每個Link多了一個指向前一個元素的引用
第一個元素指向前一個元素的引用是null
雙向鏈表的缺點是每次插入或刪除一個Link的時候,要處理四個鏈接點的引用,而不是兩個
雙向鏈表不必是雙端鏈表
deleteLast();

鏈表迭代器

實現從鏈接點到鏈接點步進,以提高效率

java LinkedList

java里的LinkedList是一個雙端鏈表、雙向鏈表。

    public static void main(String[] args) {
        LinkedList<String> strings = new LinkedList<>();
        strings.add("1");// 末尾添加
        strings.add(1,"2");// 遍歷,for循環的i和index比較,在Node里並沒有成員變量index
        strings.addFirst("3");
        strings.addLast("4");
        strings.contains("5");// 遍歷
        strings.element();//返回第1項
        strings.get(1);// 遍歷,for循環的i和index比較
        strings.getFirst();
        strings.getLast();
        strings.indexOf("6");// 遍歷,for循環里index遞增並返回
        strings.offer("7");// 末尾添加
        strings.offerFirst("8");// 表頭添加
        strings.offerLast("9");// 末尾添加
        strings.peek();// 返回第1項
        strings.peekFirst();
        strings.peekLast();
        strings.poll();
        strings.pollFirst();
        strings.pollLast();
        strings.pop();// 彈出第1項
        strings.push("");// 添加到第1項
        strings.remove(); //刪除第1項
        strings.remove(1);
        strings.remove("10");
        strings.removeFirst();
        strings.removeLast();
    }
View Code

java ArrayList和LinkedList

ArrayList底層是一個無序的可重復的數組,LinkedList底層是一個雙端雙向鏈表。 

除非頻繁地通過下標查詢數據,否則都可以使用LinkedList來代替;LinkedList不需要擴容,直接在鏈表末尾添加元素,如果是添加一個集合,使用for循環。

首尾查詢插入刪除

ArrayList尾部插入快O(1)

LinkedList首尾插入快O(1)

中部查詢插入刪除

ArrayList中部插入刪除O(N),N

LinkedList中部插入刪除O(N),N/2

 


免責聲明!

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



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