1.ArrayList
顾名思义,ArrayList是采用数组的方式实现了List接口,非线程安全,可以存储null对象,查找效率搞,可以根据数组的下标直接索引,查找内部存储的对象需要遍历数组比较,增加,删除,插入,都会导致数组的复制,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造ArrayList实例时,就指定其容量,以避免数组扩容的发生。或者根据实际需求,通过调用ensureCapacity方法来手动增加ArrayList实例的容量。ensureCapacity每次扩容数组容量的增长大约是其原容量的1.5倍。ArrayList的toArray()方法返回的对象是Object[]对象,不能强制转换成你想要的类型,可以调用T[] toArray(T[] a)直接返回对应泛型的数组,a可以为空数组。Fail-Fast机制,如果在使用迭代器的过程中有其他线程修改了集合,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount变量,在迭代过程中,判断modCount跟expectedModCount是否相等,如果不相等就表示已经有其他线程修改了,modCount被声明为volatile,保证线程之间修改的可见性。除非通过迭代器本身的 remove 方法,其他任何时间任何方式的修改,迭代器都将抛出ConcurrentModificationException,但是迭代器的快速失败行为不能得到完全保证,编写依赖于此异常的程序的做法是错误的做法。
2.HashMap
HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。HashMap采用数组加链表的实现,在构造函数中会初始化一个存储Entry对象的数组;entry对象是一个链表结构的实现,用Next指向下一个entry,每次put一个元素的时候,现根据key的hash算出应该存放的数组的下标,如果该下标处没有元素则直接新增一个entry放于此处,如果该下标处已经存放了一个entry,就对该entry通过next是否为空进行遍历,找到key值与新增的key值相同的(equals方法)entry进行修改,如果遍历之后没有找到,就新增一个entry放在该数组的下标处,并将next指向以前存放在此处的entry,这样对于根据key值的hash算出的数组下标的entry实际上是,新加入的在链头,最开始加入的在链尾。前面说过HashMap的数据结构是数组和链表的结合,所以我们当然希望这个HashMap里面的 元素位置尽量的分布均匀些,尽量使得每个位置上的元素数量只有一个,那么当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,而不用再去遍历链表,这样就大大优化了查询的效率。当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的,所以在新增元素的时候需要根据loadFactor的值进行判断并扩容,reSize方法是非常耗时的,因为需要根据新的数组容量重新计算以前旧数据的索引,loadFactor的默认值是0.75,当hanshmap中存储的元素大于数组长度的四分之三的时候就会进行扩容,如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。即预先设置,初始容量(数组的大小)initialCapacity,负载因子loadFactor,最大容量threshold (以上两个值相乘)
3.HashSet
HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用null元素。内部用一个hashmap实例来实现set接口,数据存储在hashmap中entry的key中,value都是同一个空对象。