Java中的集合包括三大类,它们是Set、List和Map,
它们都处于java.util包中,Set、List和Map都是接口,它们有各自的实现类。
List、Set都继承自Collection接口,Collection (全部集合类的接口)
容器内每一个为之所存储的元素个数不一样。Collection类型者,每一个位置只有一个元素。Map类型者,持有 key-value pair,像个小型数据库。html
List,Set都是继承自Collection接口,Map则不是java
(a) Collection
--List:将以特定次序存储元素。因此取出来的顺序可能和放入顺序不一样。
--ArrayList / LinkedList / Vector
--Set : 不能含有重复的元素
--HashSet / TreeSet
(b) Map
--HashMap
--HashTable
--TreeMap web
List,Set,Map将持有对象一概视为Object型别。数据库
Collection、List、Set、Map都是接口,不能实例化。 继承自它们的 ArrayList, Vector, HashTable, HashMap是具象class,这些才可被实例化。
vector容器确切知道它所持有的对象隶属什么型别。vector不进行边界检查。编程
8. map的 key 集合和 set集合都不容许有重复的元素存在,list能够有重复的元素存在 数组
(2)List中的对象按照索引位置排序,能够有重复对象,容许按照对象在集合中的索引位置检索对象,如经过list.get(i)方式来得到List集合中的元素。
(3)Set中的对象不按特定方式排序,而且没有重复对象。但它的有些实现类能对集合中的对象按特定方式排序,例如TreeSet类,它能够按照默认排序,也能够经过实现 java.util.Comparator<Type>接口来自定义排序方式。
(4) Map中的每个元素包含一个键对象和值对象,它们成对出现。键对象不能重复,值对象能够重复。安全
关系如图:
或者:数据结构
Collections (操做集合的工具类)
对于集合类的操做不得不提到工具类Collections,它提供了许多方便的方法,如求两个集合的差集、并集、拷贝、排序等等。
因为大部分的集合接口实现类都是不一样步的,可使用Collections.synchronized*方法建立同步的集合类对象。
如建立一个同步的List:
List synList = Collections.synchronizedList(new ArrayList());
其实现原理就是从新封装new出来的对象,操做对象时用关键字synchronized同步。看源码很容易理解。
Collections部分源码: 多线程
static class SynchronizedCollection<e> implements Collection<e>, Serializable { final Collection<e> c; // Backing Collection final Object mutex; // Object on which to synchronize SynchronizedCollection(Collection<e> c) { if (c==null) throw new NullPointerException(); this.c = c; mutex = this; } //... public boolean add(E e) { //操做集合时简单调用本来的ArrayList对象,只是作了同步 synchronized (mutex) {return c.add(e);} } //...
ArrayList、Vector是线性表,使用Object数组做为容器去存储数据的,添加了不少方法维护这个数组,使其容量能够动态增加,极大地提高了开发效率。它们明显的区别是ArrayList是非同步的,Vector是同步的。不用考虑多线程时应使用ArrayList来提高效率。app
LinkedList是链表,略懂数据结构就知道其实现原理了。链表随机位置插入、删除数据时比线性表快,遍历比线性表慢。
由此可根据实际状况来选择使用ArrayList(非同步、非频繁删除时选择)、Vector(需同步时选择)、LinkedList(频繁在任意位置插入、删除时选择)。
HashMap结构的实现原理是将put进来的key-value封装成一个Entry对象存储到一个Entry数组中,位置(数组下标)由key的哈希值与数组长度计算而来。若是数组当前下标已有值,则将数组当前下标的值指向新添加的Entry对象。
public class HashMap<k,v> extends AbstractMap<k,v> implements Map<k,v>, Cloneable, Serializable { transient Entry<k,v>[] table; public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); //遍历当前下标的Entry对象链,若是key已存在则替换 for (Entry<k,v> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } addEntry(hash, key, value, i); return null; } } static class Entry<k,v> implements Map.Entry<k,v> { final K key; V value; Entry<k,v> next; int hash; }
TreeMap是由Entry对象为节点组成的一颗红黑树,put到TreeMap的数据默认按key的天然顺序排序,new TreeMap时传入Comparator自定义排序。红黑树网上不少资料,我讲不清,这里就不介绍了。
之因此先讲Map是由于Set结构其实就是维护一个Map来存储数据的,利用Map结构key值惟一性。
HashSet部分源码:
public class HashSet<e> extends AbstractSet<e> implements Set<e>, Cloneable, java.io.Serializable { //无心义对象来做为Map的value private static final Object PRESENT = new Object(); public boolean add(E e) { return map.put(e, PRESENT)==null; } }
线程安全类
在集合框架中,有些类是线程安全的,这些都是jdk1.1中的出现的。在jdk1.2以后,就出现许许多多非线程安全的类。 下面是这些线程安全的同步的类:
vector:就比arraylist多了个同步化机制(线程安全),由于效率较低,如今已经不太建议使用。在web应用中,特别是前台页面,每每效率(页面响应速度)是优先考虑的。
statck:堆栈类,先进后出
hashtable:就比hashmap多了个线程安全
enumeration:枚举,至关于迭代器
除了这些以外,其余的都是非线程安全的类和接口。
线程安全的类其方法是同步的,每次只能一个访问。是重量级对象,效率较低。
具体的区别请参考:HashMap、HashTable、ConcurrentHashMap、HashSet区别 线程安全类
StringBuffer是线程安全,而StringBuilder是线程不安全的。对于安全与不安全没有深刻的理解状况下,易形成这样的错觉,若是对于StringBuffer的操做均是线程安全的,然而,Java给你的保证的线程安全,是说它的方法是执行是排它的,而不是对这个对象自己的屡次调用状况下,仍是安全的。看看下边的例子,在StringBufferTest中有一个数据成员contents它是用来扩展的,它的每一次append是线程安全的,但众屡次append的组合并非线程安全的,这个输出结果不是太可控的,但若是对于log和getContest方法加关键字synchronized,那么结果就会变得很是条理,若是换成StringBuider甚至是append到一半,它也会让位于其它在此基础上操做的线程: