数组、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

原文地址:https://www.cnblogs.com/Mike_Chang/p/10186308.html