数组和集合
警惕数组的浅拷贝
通过copyOf
的方法得到的数组是一个浅拷贝,而且,数组的clone
方法也只能得到浅拷贝。
需要注意的是,集合并没有提供拷贝的方法,这时不要使用诸如List.toArray
的方法尝试利用Array
的copyOf
方法,这样就恰好撞到枪口上了。
在明确场景下,为集合指定初始容量
常用的ArrayList
会按照1.5倍的速度扩容,每次扩容都是一次数组的拷贝,如果数据量大,这样的拷贝会非常耗资源。与之类似,Vector
默认按照2倍的速度扩容,但是这个速率可以在构造函数中自己指定。
原始类型数组不能作为asList的参数
1 | int[] data = {1,2,3,4,5}; |
这时list
的长度为1,且list[0]
为data
数组,修改方法如下:
1 | Integer[] data = {1,2,3,4,5}; |
asList方法产生的List对象不可更改
asList
产生的List
对象仅有以下五个方法:
size
toArray
get
set
contains
而常用的add
和remove
方法都不支持,这是JDK设计的缺陷,值得我们注意。
频繁插入和删除时使用LinkedList
ArrayList
修改的效率高,但插入和删除都完败LinkedList
。
列表相等只关注元素数据
AbstractList
中定义了equals
的方法,其实现中只比较各个元素是否相等。
推荐使用subList处理局部列表
subList
产生的列表只是一个视图(View),所有的修改直接作用于原列表。
例如要删除list
的第20到30项元素,可以这样写:
1 | list.subList(20, 30).clear(); |
但是值得注意的是,subList
生成子列表后,要将原列表置为只读状态,一旦修改,再去操作子列表时,会抛出ConcurrentModificationException
异常。
多线程中使用Vector或HashTable
Vector
是ArrayList
的多线程版本。
HashTable
是HashMap
的多线程版本。
集合大家族
List
ArrayList
:动态数组LinkedList
:双向链表Vector
:线程安全的动态数组Stack
:栈
Set
EnumSet
:枚举类型专用的HashSet
:可以快速插入和查找TreeSet
:自动排序
Map
TreeMap
:根据Key值自动排序HashMap
:可以快速插入和查找HashTable
:线程安全的HashMap
Properties
:是HashTable
的子类,可以从Property文件中加载数据EnumMap
:Key必须是枚举类型WeakHashMap
:其对象的存在并不会阻止GC对键值对的回收(不会内存泄漏,但存在安全隐患)
Queue
ArrayBlockingQueue
:数组实现的有界阻塞队列PriorityBlockingQueue
:依照优先级组件的队列LinkedBlockingQueue
:通过链表实现的有界阻塞队列PriorityQueue
:优先级无界队列ArrayDeque
:数组实现的双端队列LinkedBlockingDeque
:链表实现的双端队列LinkedList
:也分在了List
中
异常
提倡异常的封装
自己封装异常可以提高系统的友好性和可维护性,要避免使用catch来捕捉所有异常。
不要在finally块中return
finally
块中的return
会覆盖try
中的return
,而且会使throw
的异常被屏蔽。
多线程和并发
线程优先级只使用三个等级
Java的线程共有10个等级,这个优先级只是概率性的,并不是优先级高的就一定先运行,建议只使用MIN_PRIORITY
、NORM_PRIORITY
和MAX_PRIORITY
。
Lock和synchronized是不一样的
Lock
支持更细粒度的锁控制:可以做到读写分离等等Lock
是无阻塞锁,而synchronized
是阻塞锁:等待状态≠阻塞状态(虽然对外部表现为阻塞)Lock
可实现公平锁,synchronized
是非公平锁:Lock
会选择等待时间最长的Lock
是代码级的,synchronized
是JVM级的:synchronized
的性能更好
总之,灵活、强大选择Lock
,快捷、安全选择synchronized
其它
若非必要,不要克隆对象
克隆对象的效率比构造对象的效率要低。
POLA
Principle Of Least Surprise,最小惊诧原则。要使用最常见而不是最新颖的功能。不要为了炫技而“吓”了你的同伴或用户。