• JAVA学习笔记——集合


    比较常用的集合一般有4种接口,每种接口可以由多种数据结构去实现。这样我们就可以在实现相同接口(功能)的情况下,根据具体场景选择不同的数据结构。

    Interface Hash Table Resizable Array Balanced Tree Linked List Hash Table + Linked List
    Set HashSet TreeSet LinkedHashSet
    List ArrayList LinkedList
    Deque ArrayDeque LinkedList
    Map HashMap TreeMap LinkedHashMap

    1. List

    ArrayList、LinkedList、Vector都实现了接口List。List接口是一个有顺序的集合,元素可以重复,可以通过索引去访问元素。

    1.1 ArrayList

    ArrayList类维护了一个数组,访问操作效率高,删除和插入操作效率低。

    transient Object[] elementData; // non-private to simplify nested class access
    

    当增加元素且ArrayList的容量不够时,需要对数组进行扩容,扩容使用Arrays.copyOf()方法。
    当删除元素时,会使用System.arraycopy()方法。
    以上两个copy方法都是蛮费时间的。

    1.2 LinkedList

    LinkedList类维护了一个双端链表,first和last分别指向链表的头尾Node。

    1.3 Vector

    Vector类与ArrayList类非常相像,主要的区别是:Vector是线程安全的,ArratList不是线程安全的。

        protected Object[] elementData;
    

    1.3.1 Stack

    Stack类继承了Vector类,因为Vector类维护了一个数组,在数组基础上实现LIFO的功能还是很方便的。但这也会带来2个问题:

    1. Stack类是基于数组实现的,push和pop操作的开销会比较大。
    2. Stack可以使用Vector设置任意位置元素的方法setElementAt(E obj, int index),那么Stack就不是纯粹的栈,栈只能操作栈顶的元素。

    JDK文档建议,如果要使用栈这种数据结构,应该优先使用Deque<Integer> stack = new ArrayDeque<Integer>();

    2. Deque

    Deque是Double End Queue的意思,该双端队列即可以用作FIFO的队列,也可以用作LIFO的栈。Stack类也是栈,但它有些缺点,应该优先使用Deque作为栈。

    2.1 LinkedList

    LinkedList类维护了一个双端链表,first和last分别指向链表的头尾Node。

    2.2 ArrayDeque

    ArrayDeque是在JDK1.6中才出现的。以数组的形式实现双端队列。
    JDK文档建议:

    1. 当要使用栈时,ArrayDeque比Stack要高效;
    2. 当要使用队列时,ArrayDeque要比LinkedList要高效。

    第1点的原因在上面说过解释过,第2点的原因有以下两点:

    1. LinkedList每次push一次需要new一个Node,每次pop一次会产生一个Node垃圾等待回收。ArrayDeque由于是数组,push不会new,pop不会产生垃圾,虽然在容量不够时,还是会做扩大一倍容量的操作。
    2. LinkedList在内存中是分散的,所以缓存命中率会比较低。

    3. Map

    3.1 HashMap

    HashMap(散列表)是一个增删、查找都高效的数据结构。查找散列表一般分为2个步骤:

    1. 计算键的hash值;
    2. 找到相应的位置并解决冲突,放入该键值对。
      解决冲突有2种方法:拉链法和开放地址法(线性探测、二次探测、随机探测)

    java中的HashMap采用的是拉链法,这是数组和链表的结合形式。

    HashMap中的put()方法:

    1. 计算哈希值。通过hashcode()计算出该对象key的hash值
    2. 哈希值取模。对hash值进行取模操作hash%length,length是数组的长度。该长度应该是2的幂方,这样我们就可以对取模操作进行优化hash&(length-1)
    3. 插入链表。得到模值之后,就将其作为索引找到相应的链表表头,该条链表中存储hash值一样的键值对,键值对存储在静态内部类Entry中。Entry保存键、值以及指向下一个键值对的next。

    HashMap中的resize()方法:
    当entry的数量大于阈值时,开始resize()。其中阈值是容量乘以加载因子。
    resize()之后,table长度变为2倍,重新取模,重新链表插入。

    负载因子:当hashmap中的有效容量除以总容量大于负载因子时,hashmap的容量就会翻倍。java的hashmap默认容量是16,负载因子时0.75。
    容量:

    Java8对HashMap的改进:当链表的长度大于一定值时,链表就转换为红黑树,实现了快速的增删改查,时间复杂度从顺序查找的O(n)优化到O(logn)。

    HashTable与HashMap非常相似,他们有以下三点区别:

    1. HashTable是synchronized的,是线程安全的,多个线程可以共享;HashMap相反
    2. HashTable是synchronized的,在单线程下速度会比较慢;HashMap的速度比较快
    3. HashTable不能接受null的键和值;HashMap则可以

    【Reference】

    1. JDK API文档 http://docs.oracle.com/javase/8/docs/api/
  • 相关阅读:
    Mac重装系统 [转·整合]
    cocos2d-x使用plugin-x
    iOS启动其他应用程序[转]
    iOS时间的创建
    [转] 同步与异步的概念
    Apache 虚拟主机文档
    结构变量2
    //结构体的基本使用
    指向函数的指针
    返回指针的函数
  • 原文地址:https://www.cnblogs.com/season-peng/p/6926494.html
Copyright © 2020-2023  润新知