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

  • 相关阅读:
    iOS crash 追终 ,iOS 如何定位crash 位置
    ios 性能优化策略
    如何提升代码编译的速度 iOS
    关于iOS的runtime
    iOS __block 与 __weak
    spring-boot-framework 如何自动将对象返回成json格式
    spring-boot 热部署 intellij IDE(开发过程)
    MAVEN中的插件放在哪个dependcies里面
    css3 RGBA
    css3 loading 效果3
  • 原文地址:https://www.cnblogs.com/Mike_Chang/p/10186308.html
Copyright © 2020-2023  润新知