動手實現基礎的ArrayList和LinkedList

基礎

  1. ArrayList:順序列表,它是 Array 的增強版,也稱動態數組,提供了動態的增加和減少數組,如果你閱讀過它的源碼,你會發現它內部就是採用數組來存儲數據,並且動態擴容數組的長度,在日常開發中被廣泛使用。
  2. LinkedList:線性列表,但是和 ArrayList 不同的是,它採用了雙向鏈表的數據結構,既然是鏈表的結構,那麼就不需要考慮它的容量問題。
  3. 各自的優缺點:
    • ArrayList 採用數組的結構,在添加和刪除操作上的效率低,需要大量移動數組中的元素,但是在查找的效率高;
    • LinkedList 採用了鏈表的結構,在查找的方面效率極低,添加和刪除的方面效率高;
    • LinkedList 佔用內存比 ArrayList 更高,因爲它的結點不僅村粗了數據,並且存儲了前一節點和後一節點的引用。

制定手寫方案:

  1. 兩種列表都需要有:添加、刪除、查找、清空、判空、循環遍歷和獲取大小的共有的方法;
  2. LinkedList 添加元素的方法需要區分類型:頭部添加,尾部添加,指定下標添加;ArrayList 也需要有添加整個數組的方法;
  3. LinkedList 在本次的方案中採用的是單向鏈表而不是雙向鏈表的結構。

手寫ArrayList:

import java.util.Arrays;
import java.util.function.Consumer;

class ArrayList<E> {
   

    // 用於保存空列表的引用
    private static final Object[] EMPTY_ELEMENTDATA = {};

    private Object[] _data;
    private int _size;
    private int _realLength;

    // 列表默認的長度
    private static final int LEN = 20;

    public ArrayList(int initLen) {
        if (initLen == 0) {
            this._data = EMPTY_ELEMENTDATA;
        } else if (initLen > 0) {
            this._data = new Object[initLen];
        } else {
            throw new IllegalArgumentException("init length is error");
        }
        this._size = initLen;
        this._realLength = 0;
    }

    public ArrayList() {
        this(LEN);
    }

    /**
     * 追加一個元素
     *
     * @param element 元素
     */
    public void add(E element) {
        checkLen();
        _data[_realLength++] = element;
    }

    /**
     * 指定下標插入
     *
     * @param index   下標
     * @param element 元素
     */
    public void add(int index, E element) {
        checkIndex(index);
        checkLen();
        System.arraycopy(_data, index, _data, index + 1, _realLength - index);
        _data[index] = element;
        _realLength++;
    }

    /**
     * 添加一個數組
     *
     * @param elements 數組元素
     */
    public void addAll(E[] elements) {
        for (E e : elements) {
            checkLen();
            _data[_realLength++] = e;
        }
    }

    /**
     * 獲取指定下標的元素
     *
     * @param index 下標
     * @return 下標對應的元素
     */
    @SuppressWarnings("unchecked")
    public E get(int index) {
        checkIndex(index);
        return (E) _data[index];
    }

    /**
     * 刪除指定下標的元素
     *
     * @param index 下標
     * @return 刪除的元素
     */
    @SuppressWarnings("unchecked")
    public E remove(int index) {
        checkIndex(index);
        Object delete = _data[index];
        System.arraycopy(_data, index + 1, _data, index, _realLength - index - 1);
        _data[--_realLength] = null;
        return (E) delete;
    }

    /**
     * 判斷是否包含某個元素
     *
     * @param element 元素
     * @return 包含返回true
     */
    public boolean contain(E element) {
        if (isEmpty()) return false;
        else {
            for (Object e : _data) {
                if (e == element) {
                    return true;
                }
            }
            return false;
        }
    }

    /**
     * 循環遍歷列表
     *
     * @param consumer Consumer對象
     */
    public void forEach(Consumer<E> consumer) {
        if (isEmpty()) {
            return;
        }
        for (Object object : _data) {
            if (object != null)
                consumer.accept((E) object);
        }
    }

    /**
     * 清空列表元素
     */
    public void clear() {
        for (int i = 0; i < _realLength; i++) {
            _data[i] = null;
        }
        _realLength = 0;
    }

    /**
     * 檢查下標是否越界
     *
     * @param index 下標
     */
    private void checkIndex(int index) {
        if (index < 0 || index > _realLength) {
            throw new IndexOutOfBoundsException();
        }
    }

    public boolean isEmpty() {
        return _realLength == 0;
    }

    public int size() {
        return _realLength;
    }

    /**
     * 檢查是否要擴容數組
     *
     * @return true 表示需要擴容
     */
    private void checkLen() {
        if (_realLength >= _size) {
            grow();
        }


    /**
     * 數組的擴容,擴容大小爲原先的2倍
     */
    private void grow() {
        // 擴容後size也要變化
        _size = _size * 2;
        _data = Arrays.copyOf(_data, _size);
    }

    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(1, 2);
        arrayList.add(3);
        arrayList.add(1, 4);
        System.out.print("原始數據:");
        arrayList.forEach(element -> System.out.print(element + " "));
        System.out.println();
        arrayList.remove(1);
        System.out.print("移除index=1的元素:");
        arrayList.forEach(element -> System.out.print(element + " "));
        System.out.println();
        System.out.println("list has {1}: " + arrayList.contain(1));
        System.out.println("list has {10}: " + arrayList.contain(10));
    }
}

// 原始數據:1 4 2 3 
// 移除index=1的元素:1 2 3 
// list has {1}: true
// list has {10}: false

如上所述,在組織一個 ArrayList 的過程中,我們特別注意以下幾點:

  1. 既然採用數組,那麼就要注意它的容量問題,做好擴容的工作;
  2. 使用數組的過程中,千萬別忘記處理數組越界問題;
  3. 在刪除固定的元素後,別忘記了手動置空下。

實現一個簡單的 ArrayList 還是挺簡單的,自己動手寫一遍會對 ArrayList 有另一番見解,快去動手敲一敲吧。

手寫LinkedList:

import java.util.function.Consumer;

class LinkedList<E> {

    // 列表的長度
    private int _size;
    // 頭結點
    private Node<E> _first;
    // 尾結點
    private Node<E> _last;

    public LinkedList() {
        _size = 0;
    }

    /**
     * 在尾巴插入一個結點
     *
     * @param element 插入的數據
     */
    public void addLast(E element) {
        final Node<E> l = _last;
        final Node<E> newNode = new Node<>(element, null);
        _last = newNode;
        if (l == null) {
            // 如果之前的尾部元素爲空,那說明列表爲空,插入元素後,首尾應該都是newNode
            _first = newNode;
        } else {
            l.next = newNode;
        }
        _size++;
    }

    /**
     * 在頭部插入元素
     *
     * @param element 插入的數據
     */
    public void addFirst(E element) {
        final Node<E> f = _first;
        final Node<E> newNode = new Node<>(element, _first);
        _first = newNode;
        if (f == null) {
            // 如果之前的頭部元素爲空,那說明列表爲空,插入元素後,首尾應該都是newNode
            _last = newNode;
        } else {
            newNode.next = f;
        }
        _size++;
    }

    /**
     * 添加一個元素,默認在尾巴添加
     *
     * @param element 元素值
     */
    public void add(E element) {
        addLast(element);
    }

    /**
     * 指定下標插入元素
     *
     * @param index   下標
     * @param element 元素值
     */
    public void add(int index, E element) {
         // 特殊處理頭尾
        if (index == 0) {
            addFirst(element);
            return;
        }
        if (index == size() - 1) {
            addLast(element);
            return;
        }
        Node<E> newNode = new Node<>(element, null);
        Node<E> pre = _first;
        Node<E> next = _first.next;
        for (int i = 1; i < size() - 1; i++) {
            if (i == index) {
                newNode.next = next;
                pre.next = newNode;
                _size++;
            }
            pre = next;
            next = next.next;
        }
    }

    /**
     * 獲取指定下標的元素值
     *
     * @param index 下標
     * @return 值
     */
    public E get(int index) {
        checkIndex(index);
        Node<E> f = _first;
        for (int i = 0; i < size(); i++) {
            if (i == index) {
                return f.data;
            } else {
                f = f.next;
            }
        }
        return null;
    }

    /**
     * 移除指定下標的元素
     *
     * @param index 下標
     * @return 返回移除的元素
     */
    public E remove(int index) {
        checkIndex(index);
        Node<E> f = _first;
        Node<E> l = _first;
        // 需特殊處理頭結點
        if (index == 0) {
            E element = f.data;
            f = f.next;
            _first = f;
            _size--;
            return element;
        }
        for (int i = 0; i < size(); i++) {
            if (i == index) {
                E element = f.data;
                l.next = f.next;
                _size--;
                f.next = null;
                f.data = null;
                return element;
            } else {
                l = f;
                f = f.next;
            }
        }
        return null;
    }

    /**
     * 檢測是否包含某個元素
     *
     * @param element 包含的對象
     * @return 包含返回true
     */
    public boolean contain(E element) {
        if (!isEmpty()) {
            Node<E> f = _first;
            for (int i = 0; i < size(); i++) {
                if (f.data == element) return true;
                f = f.next;
            }
            return false;
        } else {
            return false;
        }
    }

    /**
     * 遍歷整個列表
     *
     * @param consumer Consumer對象
     */
    public void forEach(Consumer<E> consumer) {
        if (isEmpty()) {
            return;
        }
        Node<E> f = _first;
        while (f != null) {
            E e = f.data;
            consumer.accept(e);
            f = f.next;
        }
    }

    /**
     * 清空
     */
    public void clear() {
        for (Node<E> x = _first; x != null; ) {
            Node<E> next = x.next;
            x.data = null;
            x.next = null;
            x = next;
        }
        _first = _last = null;
        _size = 0;
    }

    /**
     * 列表的長度
     *
     * @return 長度值
     */
    public int size() {
        return _size;
    }

    /**
     * 判斷列表是否爲空
     *
     * @return 爲空返回true
     */
    public boolean isEmpty() {
        return _size == 0;
    }

    /**
     * 檢查下標是否越界
     *
     * @param index 下標
     */
    private void checkIndex(int index) {
        if (index < 0 || index > _size) {
            throw new IndexOutOfBoundsException();
        }
    }

    /**
     * 鏈表節點
     *
     * @param <E> 數據類型
     */
    static class Node<E> {
        E data;
        Node<E> next;

        Node(E data, Node<E> next) {
            this.data = data;
            this.next = next;
        }
    }

    public static void main(String[] args) {
        LinkedList<Integer> linkedList = new LinkedList<>();
        linkedList.add(1);
        linkedList.addFirst(0);
        linkedList.add(2);
        linkedList.add(3);
        System.out.print("原始數據: ");
        linkedList.forEach(integer -> System.out.print(integer + " "));
        System.out.println();
        int remove = linkedList.remove(0);
        System.out.println("remove data is: " + remove);
        System.out.print("刪除index爲0的數據: ");
        linkedList.forEach(integer -> System.out.print(integer + " "));
        System.out.println();
        linkedList.add(1, 10);
        System.out.print("在index=1的地方添加數據10: ");
        linkedList.forEach(integer -> System.out.print(integer + " "));
        System.out.println();
        System.out.println("list has {1}: " + linkedList.contain(1));
        System.out.println("list has {100}: " + linkedList.contain(100));
    }
}
// 原始數據: 0 1 2 3 
// remove data is: 0
// 刪除index爲0的數據: 1 2 3 
// 在index=1的地方添加數據10: 1 10 2 3 
// list has {1}: true
// list has {10}: true

LinkedList 的實現過程中,比 ArrayList 要多出了幾個操作,畢竟是鏈表的結構,插入元素可以分別在頭部和尾部插入數據,對於熟悉鏈表結構的大佬來說一點,這點一點不成問題,不熟悉的小佬可以藉此機會熟悉一下鏈表的操作。下面說一下幾點注意的地方吧:

  1. 對於第一次插入數據的時候,無論實在頭部還是尾部,一定要注意對 _first_last 的處理;
  2. 在指定位置添加和刪除元素的時候,一定要處理好前節點和後結點之間的聯繫,並且在刪除的操作中別忘記了將刪除後的節點數據置空。

如果本文章你發現有不正確或者不足之處,歡迎你在下方留言或者掃描下方的二維碼留言也可!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章