Java 集合框架
- 1 Collection接口
- Set接口
- List接口
- Queue接口
由于功能与集合框架相似,Map经常与集合放在一起讨论。
- 2 Map接口
Map主要实现 | 数据结构 | 有序 | 线程安全 | 父类 | 其他特性 |
---|---|---|---|---|---|
HashMap | 哈希表 | 随机存储 | 线程不安全 | AbstractMap | 访问速度快 |
LinkedHashMap | 链表 | 有序存储 | 线程安全 | HashMap | 遍历比HashMap慢 |
Hashtable | 哈希表 | 随机存储 | 线程安全 | HashMap | 写入较慢 |
TreeMap | 树 | 有序存储 | 线程不安全 | AbstractMap | 默认是按键值的升序排序,指定排序的比较器 |
Map接口见->链接
1 Collection接口
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。
一些 Collection允许相同的元素而另一些不行,一些能排序而另一些不行。
Java JDK不提供直接继承自Collection的类,
Java JDK提供的类都是继承自Collection的“子接口”如List和Set。
所有实现Collection接口的类都必须提供两个标准的构造函数:
第一个
:无参数的构造函数,用于创建一个空的Collection;
第二个
:带Collection参数的构造函数,用于创建一个新的Collection,新的Collection与传入的Collection有相同的元素;
该构造函数允许用户复制一个Collection。
1.1 遍历Collection中的元素
Collection接口继承了Iterable接口,不论Collection的实际类型如何,它都支持一个iterator()的方法,
该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个element。
典型的用法如下:
Iterator it = collection.iterator(); // 获得一个迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一个元素
}
由Collection接口派生的接口主要有:List、Set和Queue。
1.2 List接口
List是有序的,使用此接口能够精确的控制每个元素插入的位置。
用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
和下面要提到的Set不同,List允许有相同的元素。
除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个 ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素, 还能向前或向后遍历。
实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。
1.2.1 List接口主要实现类
List接口主要实现 | 数据结构 | 线程安全 | 其他特性 |
---|---|---|---|
Vector | 数组 | 线程安全 | 增删和查询都很慢 |
Stack | 数组 | 线程安全 | 后进先出 |
ArrayList | 数组 | 线程不安全 | 查询速度快 |
LinkedList | 链表 | 线程不安全 | 增删元素的速度很快 |
1.2.2 LinkedList类
LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。
一种解决方法是在创建List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(…));
1.2.3 ArrayList类
ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增 加,但是增长算法 并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
1.2.4 Vector类
Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和 ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了 Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出 ConcurrentModificationException,因此必须捕获该异常。
ArrayList arrayList = new ArrayList(100);
arrayList.ensureCapacity(200);
Vector vector = new Vector(100);
vector.ensureCapacity(200);
1.2.5 Stack 类
Stack继承自Vector,线程安全什么的跟Vector都差不多,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
有几个地方需要注意:
第一:add()和push(),stack是将最后一个element作为栈顶的,所以这两个方法对stack而言是没什么区别的,但是,它们的返回值不一样,add()返回boolean,就是添加成功了没有;push()返回的是你添加的元素。为了可读性以及将它跟栈有一丢丢联系,推荐使用push。
第二:peek()和pop(),这两个方法都能得到栈顶元素,区别是peek()只是读取,对原栈没有什么影响;pop(),从字面上就能理解,出栈,所以原栈的栈顶元素就没了。
1.3 Set接口
Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。
Set集合类的特点就是可以去重,它们的内部实现都是基于Map的,用的是Map的key,所以知道为什么可以去重复了吧。
比较是基于hascode()方法和equals()方法的,所以必要情况下需要重写这两个方法。
既然要去重,那么就需要比较,既然要比较,那么就需要了解怎么比较的,不然它将1等于2了,你怎么办?
1.3.1 Set接口主要实现类
Set主要实现 | 数据结构 | 线程安全 | 其他特性 |
---|---|---|---|
HashSet | 哈希表 | 线程不安全 | 查询速度最快集合 |
LinkedHashSet | 链表 | 线程不安全 | 线程不安全 |
TreeSet | 树 | 线程不安全 | 有序的 |
EnumSet | 不确定 | 线程不安全 | 枚举的专用Set |
1.3.2 HashSet
HashSet是散列表的一个实现,存储是无序的,读取也是无序的;
装填因子:load factor=0.75
装填元素超过了容量的%75时,会进行再散列。
看下面例子:
Set s = new HashSet();
String[] a = "a,b,c,d,e,f,g".split(",");
System.out.println(">>myHashSet1>>start");
for(String aa : a)
s.add(aa);
Iterator i = s.iterator();
while(i.hasNext())
System.out.println(">>>"+i.next());
System.out.println(">>myHashSet1>>end");
运行结果:
>>myHashSet1>>start
>>>f
>>>g
>>>d
>>>e
>>>b
>>>c
>>>a
>>myHashSet1>>end
可以看到当一个数组被有顺序的存进一个HashSet后,存储是随机的。
1.3.3 TreeSet
这是一个树结构的Set,存的时候是以树结构(红黑树)存储的,遍历时拿到的是排好序了的。
Set s = new TreeSet();
String[] a = "d,c,g,f,a,b,e".split(",");
for(String aa:a)
s.add(aa);
Iterator i = s.iterator();
while(i.hasNext())
System.out.println(">>>>"+i.next());
运行结果:
>>>>a
>>>>b
>>>>c
>>>>d
>>>>e
>>>>f
>>>>g
1.4 总结
ArrayList,LinkedList一个无序,一个有序;
HashSet,TreeSet一个无序,一个有序;
HashMap,LinkedHasmMap,一个无序,一个有序;
Vector和HashTable,Stack是线程安全的,但是效率低;
线程不安全的类都可以配合Collections得到线程安全的类。
ArrayList , LinkedList , Vector
ArrayList和Vector本质都是用数组实现的,而LinkList是用双链表实现的;所以,Arraylist和Vector在查找效率上比较高,增删效率比较低;LinkedList则正好相反。
ArrayList是线程不安全的,Vector是线程安全的,效率肯定没有ArrayList高了。实际中一般也不怎么用Vector,可以自己做线程同步,也可以用Collections配合ArrayList实现线程同步。
前面多次提到扩容的代价很高,所以如果能确定容量的大致范围就可以在创建实例的时候指定,注意,这个仅限于ArrayList和Vector。