List
常用的List分为ArrayList、Vector、LinkedList
ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,二者都允许直接序号索引元素,但是插入数据要设计到数组元素移动等内存操作,所以索引数据快,插入数据慢;Vector由于使用了synchronized方法(如add、insert、remove、set、equals、hashcode等操作),因此是线程安全,性能上比ArrayList要差。
既然是基于数组实现,那么说一说它与数组存在的一些差别。
定义一个数组,它的长度是不可变的。集合的底层是一个可以扩容的数组,长度是原来的1.5倍,cp原来的数据到新的数组里面,多出来的长度存储新数据。ArrayList 扩容根据位移算法计算——右移:(原数组长度-1)/2 注:{左移:原数组长度*2},
LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快!LinkedList双向链表,是指可以从first依次遍历至last(从头到尾),也可以从last遍历至first(从尾到头),但首尾没有构成环,不同于双向循环链表(注意区分)。一切基于指针。
ArrayList和LinkedList的大致区别:
① ArrayList是实现了基于动态数组的 数据结构,LinkedList基于链表的数据结构。
② 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
③ 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
ArrayList和LinkedList在性能上各有优缺点,都有各自所适用的地方:
① 对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象。
② 在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。
③ LinkedList不支持高效的随机元素访问。
④ ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间
可以这样说:当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能;当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。
Map
HashMap、Hashtable、LinkedHashMap、TreeMap和ConcurrentHashMap
Java为数据结构中的映射定义了一个接口java.util.Map,它有四个实现类,分别是
HashMap、Hashtable、LinkedHashMap和TreeMap。
Map 主要用于存储键(key)值(value)对,根据键得到值,因此键不允许键重复,但允许值重复。 Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。注意,如果在映射中重新插入键,则插入顺序不受影响。(如果在调用 m.put(k, v) 前 m.containsKey(k) 返回了 true,则调用时会将键 k 重新插入到映射 m 中。)
HashMap
HashMap是一个最常用的Map,它的底层是根据数组加链表的方式来存储数据。它根据哈希算法算出键的 HashCode 值,找到数组相应的存储位置进行存储,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null;允许多条记录的值为Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力。
那么问题来了,要是根据key求得的HashCode 值相同,所对应的数组位置就会是重复的,那样如何进行存储数据?
这就是所谓的哈希冲突。根据key求得的HashCode 值相同,所对应的数组位置也相同,这个时候就会以纵向链表的形式延伸来存储数据。这就是它的链表形式。
Hashtable
Hashtable与HashMap类似,它继承自Dictionary类,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtable在写入时会比较慢。
LinkedHashMap
LinkedHashMap 是比 HashMap 多了一个链表的结构。与 HashMap 相比 LinkedHashMap维护的是一个具有双重链表的HashMap。
LinkedHashMap支持两种排序,一种是插入排序,一种是使用排序,最近使用的会移至尾部例如 M1 M2 M3 M4,使用M3后为 M1 M2 M4 M3 了。
LinkedHashMap 输出时其元素是有顺序的,而 HashMap 输出时是随机的,如果 Map 映射比较复杂而又要求高效率的话,最好使用 LinkedHashMap,但是多线程访问的话可能会造成不同步,所以要用 Collections.synchronizedMap 来包装一下,从而实现同步。
TreeMap
TreeMap不仅可以保持顺序,而且可以用于排序(TreeMap能够把它保存的记录根据键排序,默认是按升序排序,也可以指定排序的比较器。)。TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。TreeMap的键和值都不能为空。
一般情况下,我们用的最多的是HashMap,在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。如果需要输出的顺序和输入的相同,那么用LinkedHashMap 可以实现,它还可以按读取顺序来排列。LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,在遍历的时候会比HashMap慢,有HashMap的全部特性。
ConcurrentHashMap
ConcurrentHashMap是线程安全的。但是它不简简单单的只是解决线程安全问题。它默认有16个HashMap,相当于二级hash;也就是说,当我保存一个值的时候,先根据key值找到对应的hashmap,然后在对hash进行操作。而对每个hashmap的操作都是线程安全的。
遍历Map有两种方法:
(1)map的keySet()方法获得键的集合,再调用键集合的iterator方法获得键的迭代器,以此迭代地取出Map中的键,用get方法获得键对应的值,便完成了Map的遍历。
(2)使用Map的entrySet方法获得Map中记录的集合,每条对象都是一个Map.Entry对象,使用其getKey方法获得记录的键,使用其getValue方法获得记录的值。