您的位置:首页 > 编程语言 > Java开发

java容器学习总结(一)

2016-07-17 11:13 288 查看

java中的容器

java的三大类容器List,Map,Set

Collection

┃━List

┃ ┃━LinkedList

┃ ┃━ArrayList

┃ ┗Vector

┃ ┗Stack

┗Set

Map

┃━Hashtable

┃━HashMap

┗WeakHashMap

类图如下所示:



从容器类图中可以知道,容器主要分为两类:Collection和Map两个接口。Collection用于存放多个单对象,Map用于存放Key-Value形式的键值对。

Collection中常用的接口分为两类List和Set。两者主要差别为List支持放入重复的对象,而Set不支持。从图中可以知道List接口常用的实现类有:ArrayList,LinkedList,Vector和Stack;Set常用实现类有HashSet、TreeSet。

Map中常用的实现类有HashMap和TreeMap;

List介绍



List可以将元素维护在特定的序列中,并且允许一个相同元素在集合中多次出现。List接口在Collection接口的基础上增加了大量的方法,使得可以在List中间插入和移除元素。除了Abstract类之外,常用的类有ArrayList(基于数组实现),LinkedList(基于循环链表实现),Vector(基于数组实现,线程安全),Stack(是Vector的子类,基于数组实现),CopyOnWriteArrayList(基于数组实现,线程安全)。

List接口中提供的面向位置操作的各种方法:(集合中已有的方法略去)

• void add(int index, E element) : 在列表的指定位置插入指定元素。

• boolean addAll(int index, Collection

ArrayList的特点、实现机制

a) ArrayList特点:

ArrayList是用数组实现的一种线性表。可以直接按位置进行索引,查找和修改速度较快,缺点是插入或者删除速度较慢。在执行插入删除时调用的是System.arraycopy方法,是一个native方法。

b) ArrayList的实现机制:

在JDK源码中可以看到ArrayList总共只有两个属性,一个是Object数组类型的elementData,一个是int型的size。

在构造方法中也可以看到,无参构造方法调用的是this(10),调用的带一个参数的构造方法,默认无参构造方法分配一个size为10的数组。按照Collection接口中定义的构造方法,它必须有一个通过其它集合对象构造自身对象的方法。这是一个相对比较简单的线性表。并且JDK中提供了大量的比较好用的方法可以使用。该动态数组在存储空间不足时按照下面方法重新分配空间:

newCapacity = (oldCapacity*3)/2 + 1;

if(newCapacity < minCapacity) newCapacity = minCapacity; 可通过ensureCapacity操作初始化大小;

数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中;

c)注意

ArrayList数组方式实现,无容量的限制;

ArrayList在执行插入元索时可能要扩容,在删除元索时并不会减小数组的容量(如希望相应的缩小数组容量,可以调用ArrayList的tirmToSize())在查找元素时要遍历数组,对于非null的元索采取equals的方式寻找;

ArrayList是作线程安全的。

LinkedList的特点、实现机制

a) LinkedList的特点:

使用链表实现的。故其优点就是插入删除比较快,但是不能按索引直接存取,所以执行更新操作比较快,执行查询操作比较慢。

b) LinkedList实现机制:

LinkedList常见的List实现,LinkedList基于双向链表机制,所谓双向链表机制,就是集合中的每个元素都知道其前一个元素和其后一个元素的位置。在LinkedList中,以一个内部的Entry类来代表集合中的元索,元索的值赋给element属性,Entry中的next属性指向元素的后一个元素,Entry中的previous属性指向元素的前一个元素,基于这样的机制可以快速实现集合中元素的移动。

c) 注意

LinkedList基于双向链表机制实现;

LinkedList在插入元素时,须创建一个新的Entry,并切换相应元素的前后元素的引用;在查找元素时,须遍历链表:在删除元素时,要遍历链表,找到要删除的元素,然后从链表上将此元素删除即可。

LinkedList是非线程安全的。

Vector的特点、实现机制

a) Vector的特点:

Vector是直接实现了List接口。Vector中的所有方法前面都有一个synchronized关键字做修饰。Vector是有序可重复的。

b) Vector的实现机制:

Vector和ArrayList一样,也是基于Object数组的方式来实现的默认大小为10,并且capacity Increment默认设为0。

Vector扩容策略:如果capacity Increment大于0,则将Object数组的大小为现有Size加上capacity Increment的值;如果capacity Increment等于或小于0,则将Object数组的大小扩大为现在size的两倍。

c)注意

Vector是基于Synchronized实现的线程安全的ArrayList,但是插入元素时容量扩容机制和ArrayList不同,可以传入capacity Increment来控制容量。

Stack的特点、实现机制

a) Stack的特点:

Stack(栈)是一种后进先出的序列,主要操作有判空、压栈、退栈、取栈顶元素等。

b) Stack的实现机制:

Stack继承自Vector,同样使用数组保存数据,根据该数据结构的特点进行了限制性操作。JDK中共提供了6个方法用于实现特定要求的操作:

• Stack() : 构造一个空的栈

• empty() : 判断栈是否为空

• peek() : 查看栈顶元素并返回栈顶对象

• pop() : 删除栈顶元素并返回栈顶对象

• push(E element) : 将一个元素压入当前栈中

• search(Object o) : 查看指定对象是否在当前栈中

c)注意

stack基于Vector实现,支持后进先出。

CopyOnWriteArrayList的特点、实现机制

a) CopyOnWriteArrayList的特点:

CopyOnWriteArrayList是java.util.concurrent包中的一个类,此类是一个线程安全类。由于用到了ReentrantLock(重入锁)同步,所以在修改效率上较ArrayList差。读的时候不需要加锁,如果读的时候有多个线程正在向CopyOnWriteArrayList添加数据,读还是会读到旧的数据,因为写的时候不会锁住旧的CopyOnWriteArrayList。

b) CopyOnWriteArrayList的实现机制:

CopyOnWriteArrayList是线程安全、并且在读操作时无锁的ArrayList,使用默认CopyOnWriteArrayList()此步的做法是创建一个大小为0的的数组。

当往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

c) 注意

1. 减少扩容开销。根据实际需要,初始化CopyOnWriteMap的大小,避免写时CopyOnWriteMap扩容的开销。

  2. 使用批量添加。因为每次添加,容器每次都会进行复制,所以减少添加次数,可以减少容器的复制次数。如使用上面代码里的addBlackList方法。

内存占用问题。因为CopyOnWrite的写时复制机制,所以在进行写操作的时候,内存里会同时驻扎两个对象的内存,旧的对象和新写入的对象(注意:在复制的时候只是复制容器里的引用,只是在写的时候会创建新对象添加到新容器里,而旧容器的对象还在使用,所以有两份对象内存)。如果这些对象占用的内存比较大,比如说200M左右,那么再写入100M数据进去,内存就会占用300M,那么这个时候很有可能造成频繁的Yong GC和Full GC。

  针对内存占用问题,可以通过压缩容器中的元素的方法来减少大对象的内存消耗,比如,如果元素全是10进制的数字,可以考虑把它压缩成36进制或64进制。或者不使用CopyOnWrite容器,而使用其他的并发容器,如ConcurrentHashMap。

  数据一致性问题。CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 容器-集合