Java Collections Framework是Java提供的对集合进行定义,操作,和管理的统一的架构。这个集合框架主要由接口、抽象类、实现类构成。
Java的集合有两大接口:Collection和Map。
Collection接口
API解释:Collection是层次结构中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List,这两个是最常用的子接口)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)就是一个对象。
java集合只能保存引用类型的数据,是对象的引用
Java SDK不提供直接继承自Collection的类,只提供了子接口,如Set和List。Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
所有通用的 Collection 实现类(通常通过它的一个子接口间接实现 Collection)应该提供两个“标准”构造方法:一个是 void(无参数)构造方法,用于创建空 collection;另一个是带有 Collection 类型单参数的构造方法,用于创建一个具有与其参数相同元素新的 collection。实际上,后者允许用户复制任何 collection,以生成所需实现类型的一个等效 collection。尽管无法强制执行此约定(因为接口不能包含构造方法),但是 Java 平台库中所有通用的 Collection 实现都遵从它。
Collection常见方法:
add(E e); addAll(Collection c);
contains(Obj o) ---- 返回是否包含指定的元素(true false) 。
containsAll(Collection c) ---- 返回是否包含c中所有元素 。
isEmpty(); iterator(); remove(); clear(); size(); toArray();
Java集合框架的基本接口/类层次结构:
[I]:接口
[C]:类
java.util.Collection [I]
+--java.util.List [I]
+--java.util.ArrayList [C]
+--java.util.LinkedList [C]
+--java.util.Vector [C]
+--java.util.Stack [C]
+--java.util.Set [I]
+--java.util.HashSet [C]
+--java.util.SortedSet [I]
+--java.util.TreeSet [C]
java.util.Map [I]
+--java.util.SortedMap [I]
+--java.util.TreeMap [C]
+--java.util.Hashtable [C]
+--java.util.HashMap [C]
+--java.util.LinkedHashMap [C]
+--java.util.WeakHashMap [C]
List子接口:
1、 可以包含重复的元素。
2、 是一个有序的集合,位置不可以改变,使用此接口能够精确的控制每个元素插入的位置。
3、提供了按索引访问的方式。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
4 、除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。
LinkedList
此外LinkedList提供额外的get,remove,insert方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
List list =Collections.synchronizedList(new LinkedList(...));
ArrayList
List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括null在内的所有元素。
每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法 并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
常用方法:add()
ArrayList list = new ArrayList();
List.add(“aaaaa”);
List.add(“bbbbb”);
//因为是有序的,可以使用for循环输出
for( ){
SOP(list);
}
利用ArrayList的toArray()返回一个对象的数组; 也可以利用Arrays.asList()方法返回一个列表
Arrays.asList()和Collection.toArray()是作为数组和集合类的一个桥
如果想从集合类中获得一个数组可以使用toArray()方法;如果想从数组中获得一个列表可以使用asList()方法 :
import java.util.*;
class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
public String toString() {
return "x=" + x +",y=" + y;
}
}
public class ArrayListToArrayTest {
public static void main(String[] args) {
ArrayList a1 = newArrayList();
a1.add(new Point(3, 3));
a1.add(new Point(4, 4));
a1.add(new Point(5, 5));
for (int i = 0; i <a1.size(); i++) {
System.out.println(a1.get(i));
}
System.out.println(a1);
Object[] objs = a1.toArray();// 利用ArrayList的toArray()返回一个对象的数组.
for (int i = 0; i <objs.length; i++) {
System.out.println(objs[i]);
}
System.out.println(objs);//
List l =Arrays.asList(objs);// Arrays.asList()返回一个列表.
System.out.println(l);
}
}
结果:
x=3,y=3
x=4,y=4
x=5,y=5
[x=3,y=3, x=4,y=4, x=5,y=5]
x=3,y=3
x=4,y=4
x=5,y=5
[Ljava.lang.Object;@1fc4bec
[x=3,y=3, x=4,y=4, x=5,y=5]
和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
Vector
Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和 ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了 Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出 ConcurrentModificationException,因此必须捕获该异常。
Stack
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
Set子接口:
不可以包含重复的元素。即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
Set是一个无序的集合,但是子接口SorttedSet是一个按照升序进行排列元素的Set。
很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
HashSet
此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证集合的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。
HashSet不是同步的,需要用以下语句来进行S同步转换:
Set s = Collections.synchronizedSet(new HashSet(...))
SortedSet
是Set的一个子接口。TreeSet实现了SortedSet。
Set和List比较:
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
比较:
arraylist和linkedlist
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Map接口:
API: 将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射一个值。
Map 接口提供三种collection视图(注意:这个collection和Collection是不同的概念,collection表示集合,Collection表示集合下的一个借口Collection),允许以键集、值集合或键-值映射关系集的形式查看某个映射的内容。映射的顺序 定义为迭代器在映射的 collection 视图中返回其元素的顺序。某些映射实现可明确保证其顺序,如 TreeMap 类;某些映射实现则不保证顺序,如 HashMap 类。
Map没有继承Collection接口。也就是说Map和Collection是2种不同的集合。
Collection可以看作是(value)的集合,而Map可以看作是(key,value)的集合。
Map接口由Map的内容提供3种类型的集合视图,一组key集合,一组value集合,或者一组key-value映射关系的集合。
一个Map中不能包含相同的key,每个key只能映射一个 value。
Map可以出现在k与v的映射中,v为null的情况。
Map集合允许值对象为null,并且没有个数限制,所以当get()方法的返回值为null时,可能有两种情况,一种是在集合中没有该键对象,另一种是该键对象没有映射任何值对象,即值对象为null。因此,在Map集合中不应该利用get()方法来判断是否存在某个键,而应该利用containsKey()方法来判断。
package com;
import java.util.HashMap;
import java.util.Map;
public class te {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
map.put("k", null); // 向列表中添加数据
/**
* 使用get()判断Map中是否包含某个键值对,会出现判断不准确的情况
* */
System.out.println(map.get("kkk")); //输出结果 null
System.out.println(map.get("k")); //输出结果 null
/**
* 正确的做法是:containsKey()方法来判断
* */
map.put("cc", "ggggggggggg"); // 向列表中添加数据
boolean contains = map.containsKey("cc"); //返回true或false
if (contains) {
System.out.println("在Map集合中包含键值对");
} else {
System.out.println("在Map集合中不包含键值对");
}
}
}
操作方法:
可以把这个接口方法分成三组操作:改变、查询和提供可选视图。
改变操作允许从映射中添加和除去键-值对。键和值都可以为 null。但是,不能把Map 作为一个键或值添加给自身。
Object put(Objectkey, Object value)返回值是被替换的值。
Objectremove(Object key)
void putAll(Mapmapping)
void clear()
查询操作允许您检查映射内容:
Object get(Objectkey)
booleancontainsKey(Object key)
booleancontainsValue(Object value)
int size()
boolean isEmpty()
最后一组方法允许您把键或值的组作为集合来处理。
public SetkeySet()
public Collectionvalues()
public SetentrySet()
因为映射中键的集合必须是唯一的,您用 Set 支持。因为映射中值的集合可能不唯一,您用Collection 支持。最后一个方法返回一个实现 Map.Entry 接口的元素 Set。
Map.Entry 接口
Map 的 entrySet() 方法返回一个实现Map.Entry 接口的对象集合。集合中每个对象都是底层 Map 中一个特定的键-值对。
通过这个集合迭代,您可以获得每一条目的键或值并对值进行更改。但是,如果底层 Map 在Map.Entry 接口的setValue() 方法外部被修改,此条目集就会变得无效,并导致迭代器行为未定义。
Hashtable
Hashtable 继承自 Dictiionary
Hashtable实现Map接口,实现一个key-value映射的哈希表。key或者value不允许为空。
Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”:
Hashtable numbers = new Hashtable();
numbers.put(“one”, new Integer(1));
numbers.put(“two”, new Integer(2));
numbers.put(“three”, new Integer(3));
要取出一个数,比如2,用相应的key:
Integer n = (Integer)numbers.get(“two”);
System.out.println(“two = ” + n);
-
由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object。
-
如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同。如 果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希 表的操作。
3、如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。
Hashtable是同步的。
HashMap
继承了AbstractMap。
不是同步的Map。
允许null,即nullvalue和null key。
WeakHashMap
WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。
相互比较:
hashtable与hashmap
一.历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现
二.同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的
三.值:只有HashMap可以让你将空值作为一个表的条目的key或value
HashMap与TreeMap
1、 HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该 使用TreeMap(HashMap中元素的排列顺序是不固定的)。集合框架”提供两种常规的Map实现:HashMap和TreeMap (TreeMap实现SortedMap接口)。
2、 在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了hashCode()和 equals()的实现。 这个TreeMap没有调优选项,因为该树总处于平衡状态。
结过研究,二树map一样,但顺序不一样,导致hashCode()不一样。 同样做测试:
在hashMap中,同样的值的map,顺序不同,equals时,false;
而在treeMap中,同样的值的map,顺序不同,equals时,true,说明,treeMap在equals()时是整理了顺序了的。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
各种情况下适合使用哪种集合:
如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,比如HashMap,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
如何遍历Collection中的每一个元素?
1、增强for循环 for(Objo:c){syso(o)}
2、使用iterator , Iteratorit=c.iterator;
while(it.hasNext()){Object o = it.next()}
3、普通循环:for(Iteratorit=c.iterator();it.hasNext();){it.next() }
注意:不论Collection的实际类型如何,一般都是实现Collection接口的子接口List或者Set,而生成的实际类型。它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
Iterator it =collection.iterator(); // 获得一个迭代子
while(it.hasNext()){
Object obj =it.next(); // 得到下一个元素
}
------------------------------------------------------------------------------------------------------------------------------------------------------------
归纳总结:
接口 | 简述 | 实现 | 操作特性 | 成员要求 |
Set | 成员不能重复 | HashSet | 外部无序地遍历成员 | 成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。 |
TreeSet | 外部有序地遍历成员;附加实现了SortedSet, 支持子集等要求顺序的操作 | 成员要求实现caparable接口,或者使用 Comparator构造TreeSet。成员一般为同一类型。 | ||
LinkedHashSet | 外部按成员的插入顺序遍历成员 | 成员与HashSet成员类似 | ||
List | 提供基于索引的对成员的随机访问 | ArrayList | 提供快速的基于索引的成员访问,对尾部成员的增加和删除支持较好 | 成员可为任意Object子类的对象 |
LinkedList | 对列表中任何位置的成员的增加和删除支持较好,但对基于索引的成员访问支持性能较差 | 成员可为任意Object子类的对象 | ||
Map | 保存键值对成员,基于键找值操作,compareTo或compare方法对键排序 | HashMap | 能满足用户对Map的通用需求 | 键成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。 |
TreeMap | 支持对键有序地遍历,使用时建议先用HashMap增加和删除成员,最后从HashMap生成TreeMap;附加实现了SortedMap接口,支持子Map等要求顺序的操作 | 键成员要求实现caparable接口,或者使用Comparator构造TreeMap。键成员一般为同一类型。 | ||
LinkedHashMap | 保留键的插入顺序,用equals 方法检查键和值的相等性 | 成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。 | ||
IdentityHashMap | 使用== 来检查键和值的相等性。 | 成员使用的是严格相等 | ||
WeakHashMap | 其行为依赖于垃圾回收线程,没有绝对理由则少用 |
|