【ArrayList LinkedList Vector】
Vector对于ArrayList,因为同步而引起的性能差别并不明显;
LinkedList是循环双向链表,无论是否为空,总包含一个header表项:
|——————————————————————>
header ——> ele1 ——-> ele2 ——> ele3
<<span class="literal" style="color: #268bd2;">-</span><span class="literal" style="color: #268bd2;">-</span><span class="literal" style="color: #268bd2;">-</span><span class="literal" style="color: #268bd2;">-</span><span class="literal" style="color: #268bd2;">-</span> <<span class="literal" style="color: #268bd2;">-</span><span class="literal" style="color: #268bd2;">-</span><span class="literal" style="color: #268bd2;">-</span><span class="literal" style="color: #268bd2;">-</span><span class="literal" style="color: #268bd2;">-</span> <<span class="literal" style="color: #268bd2;">-</span><span class="literal" style="color: #268bd2;">-</span><span class="literal" style="color: #268bd2;">-</span><span class="literal" style="color: #268bd2;">-</span>
<—————————————————————-|
增加元素到表尾,ArrayList 优于 LinkedList:
ArrayList的扩容最终调用System.arraycopy()方法;
数组是连续的,ArrayList只有在扩容时才会低效一些;
而链表,虽然无须扩容,但新建对象和大量赋值操作会低效;
插入元素到任意位置,LinkedList优于ArrayList:
因为Array的连续性,每次插入操作都需要是数组重排;
LinkedList在任意位置插入和尾部插入是一样的;
删除任意位置元素,ArrayList尾部删除、LinkedList在头部删除和尾部删除这三个都是高效的,其他的都是低效的:
ArrayList任意位置删除需要重排数组;
LinkedList删除元素并不是从头到尾找,而是头尾分别找(循环双向链表),所以只有中间元素的删除是低效的;
迭代器本质的遍历对于ArrayList和LinkedList来说效率是一样好的:
for-each本质就是迭代器实现,但如果不直接显式用迭代器,编译器将for-each转换为迭代器的时候总是不够聪明,导致for-each比迭代器性能略差;
随机访问遍历,ArrayList获得最高表现,LinkedList获得最低表现;
【HashMap Hashtable】链表root元素组成的数组
Hashtable和HashMap的性能几乎一样,差异有:
Hashtable是同步的,HashMap不是线程安全的;
Hashtable不允许key或value是null,HashMap允许;
Hashtable和HashMap的hash算法和hash值到内存的索引算法不同;
HashMap的底层存储就是个数组,hash值索引到内存地址就是索引到数组下标:
Object.hashCode是native方法;
通过将hash值和数组长度按位取与直接得到数组下标;
HashMap实际上是一个链表数组,每个数组元素又是个链表的root节点,hashCode冲突的项成链表存储:
Entry 1 | key
Entry 2 ——-> | value
… | next
Entry n | hash
Entry被存储在数组里,重复hashCode的Entry之间又是链表链接;
HashMap的高效源自对数组随机访问的高效,如果hashCode冲突越大,则HashMap越趋于几个链表,随机访问的性能低下;
判断hash冲突的条件:
e.hash == hash && (e.key == key || key.equals(e.key))
HashMap的容量参数有两个:数组长度和负载因子;
【负载因子】 = 元素个数 / 内部数组的总大小, 默认是0.75;如果元素个数超过了负载因子规定的阈值,则数组就会扩容;
存储同量的元素,负载因子越大,数组空间占用越小,hash冲突越明显;
【LinkedHashMap】此有序非彼有序:元素最后访问顺序和元素插入顺序
所谓的HashMap无序,是说元素插入HashMap之后,由于HashCode的无序映射,使得元素遍历结果的顺序和插入的顺序不一样,从这个有序的意义上说,ArrayList, Vector, LinkedList都是有序的;
而LinkedHashMap仅仅是实现了上述的有序,并不是TreeMap的自动排序;
LinkedHashMap有两种次序:元素最后访问顺序和元素插入顺序:
public LinkedHashMap(int capacity, float loadFactor, boolean accessOrder);
accessOrder == true: 元素最后访问顺序;
accessOrder == false: 元素插入顺序;
元素最后访问顺序对于缓存池淘汰元素很有意义,但要慎用,以防ConcurrentModificationException,因为LinkedHashMap.get()会引起元素重排序:
m = new LinkedHashMap