1.前言Java性能优化那些影响性能的细节——续。打算把整个系列的文章都写成这个标题,以后慢慢积累再写。这是第一个条目。这次的内容主要来自《Java程序性能优化实战》这本书,算是读书笔记吧。有兴趣的朋友可以看看这本书。2.List接口?先看这几个List的类图:ArrayListVectorLinkedListArrayList、Vector和LinkedList都是来自AbstractList的实现,而AbstractList直接实现了List接口,扩展自AbstractCollection。1.ArrayList和VectorArrayList和Vector基本类似,所以把这两个放在一起。这两个实现类使用了几乎相同的算法,它们唯一的区别可以认为是对多线程的支持。ArrayList不对任何方法进行线程同步,因此不是线程安全的;Vector中的大部分方法都有线程同步,是线程安全的实现;ArrayList和Vector使用数组。可以认为ArrayList或Vector封装了对内部数组的操作,例如向数组中添加、删除、插入新元素,或者扩展和重新定义数组。(ArrayList和Vector的一些优化方法其实是基于数组的,所以一般情况也适用于其他底层使用数组的情况)ArrayList当前容量够大【默认初始化长度为10】,add()操作的效率非常高。ArrayList在扩容过程中【默认扩容为原来的1.5倍】,会进行大量的数组复制操作。相对来说,频繁扩容会对性能产生影响;扩展的核心源代码如下:/***增加容量以确保它至少可以容纳最小容量参数指定的*个元素。**@paramminCapacity所需的最小容量*/privatevoidgrow(intminCapacity){//溢出意识代码intoldCapacity=elementData.length;//这是使用位操作实现的。相当于newCapacity=oldCapacity+(oldCapacity/2)intnewCapacity=oldCapacity+(oldCapacity>>1);如果(newCapacity-minCapacity<0)newCapacity=minCapacity;如果(newCapacity-MAX_ARRAY_SIZE>0)newCapacity=hugeCapacity(minCapacity);//minCapacity通常接近于size,所以这是一个胜利:elementData=Arrays.copyOf(elementData,newCapacity);}从源代码中,我们可以得到另一个细节:代码中的整数乘法和除法是使用bit实现的操作,可以大大提高计算效率。从尾部删除元素效率很高,但从头部删除元素很耗时。原因是每次删除后都要重新组织数组;插入操作会执行一个数组复制[除了在末尾插入],而这个操作在向List末尾添加元素时是不存在的。大量的数组重组操作会导致系统性能低下,插入元素在List中的位置越高,数组重组的代价越大;尽量直接访问内部元素,而不是调用相应的接口。因为函数调用需要消耗系统资源,直接访问元素效率更高,比如直接使用List数组的下标索引,而不是get接口。2.LinkedListLinkedList采用循环双向链表数据结构。与基于数组的List相比,这是两种完全不同的实现技术,也决定了它们将适用于完全不同的工作场景。LinkedList链表是由一系列条目连接而成。一个表项包括3部分,即元素内容、前驱表项和从动表项。不管LinkedList是否为空,链表中都有一个headerentry,它不仅表示链表的开始,也表示链表的结束。循环链表示意图如下:LinkedList对堆内存和GC要求很高,因为每次添加元素都要newEntry(),每次都要封装数据,因为前指针和后指针必须先设置指针,然后才能将其添加到链表中;LinkedList从头尾删除元素的效率差不多,但是从List中间删除元素时性能很差,因为每次都要遍历链表的一半;(以下是上篇文章提到的LinkedList注意点)使用ListIterator(forEach,使用指针遍历)遍历LinkedList【链表的特点】;避免任何接受或返回列表中元素索引的LinkedList方法[类似获取索引的操作],性能很差,实现的是遍历列表;3.Map接口首先看这几个Map的类图:HashMapLinkedHashMapTreeMap这三个Map都实现了Map接口,继承了AbstractMap类。HashMap和LinkedHashMap直接继承了AbstractMap类,而LinkedHashMap则继承了HashMap。1、HashMapHashMap的性能在一定程度上取决于hashCode()的实现。一个好的hashCode()算法可以尽可能减少冲突,从而提高HashMap的访问速度。另外,负载因子越大,意味着使用的内存空间越少,空间越小,越容易造成Hash冲突。HashMap初始化默认数组大小为16,创建时指定大小【默认使用达到75%自动扩容,每次扩容为原来大小的两倍,最大长度为2^30】,参数必须为2的指数(如果不是,强制转换);扩展部分的源代码如下:/***初始化或加倍表大小。如果为空,则根据字段阈值中的初始容量目标在*中进行分配。*否则,因为我们使用的是二次幂扩展,所以每个bin中的*元素必须保持相同的索引,或者在新表中以二次方偏移量移动*。**@return表格*/finalNode
