Java集合框架 :接口:Collection、List 、Set、 Map;实现类:ArrayList、LinkedList、Vector、HashSet、TreeSet、HashMap、HashTable、TreeMap
java中集合类位于java.util包下,与下面四个接口有关Collection,List,Set,Map接口。
Collection接口 |
无索引,即无get方法 |
List接口 |
元素可重复、可以存放null值、有索引 |
Set接口 |
元素不可重复,无索引 |
ArrayList实现类 |
遍历速度快,插入数据效率低,线程不安全 |
Vector实现类 |
底层类似ArrayList、线程安全 |
LinkedList实现类 |
遍历速度慢,插入数据效率高,线程不安全 |
HashSet实现类 |
元素无顺序,可以存放null值,线程不安全,其有HashMap实现 |
TreeSet实现类 |
元素有顺序,不可存放null值,线程不安全,其有TreeMap实现,元素必须实现Comparable接口或Comparator接口 |
Map接口 |
具有key和value,其key一个Set,value为一个Collection集合 |
HashMap实现类 |
无顺序,线程不安全 |
HashTable实现类 |
无顺序,线程安全 |
TreeMap实现类 |
有顺序,线程不安全 |
一.Collection接口
Collection 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。
Collection常用的方法:
boolean add(Object obj):向集合中加入Object对象,如果结合中已经存在该对象(注意这里的相同的对象是equals()返回的是true,既内容完全相同,而不是==)则直接返回false。
boolean contains(Object obj):集合中是否已经存在object对象,同时是指equals()方法比较对象返回true.
boolean remove(Object obj):从集合中移除Object对象。
void clear():清空集合。
int size():返回集合所包含的对象数目。
Object[] toArray():返回集合中全部对象的数组。
Object[] toArray(Object[] objs):把集合中的对象放入objs对象数组中,objs对象数组需要事先创建。
二.List接口
List集合代表一个元素有序,可重复的集合,集合中的每个元素都有其对应的顺序索引。
常用实现类:ArrayList
LinkedList
遍历方式:for循环,Iterator
-
1.
List接口方法
void add(int
index,Object e):将元素e添加到List集合中的index处;
boolean addAll(int index,Collection c):将集合c所包含的所有元素都插入在List集合的index处;
Object get(int index):返回集合index索引处的元素;
int indexOf(Object o):返回对象o在List集合中第一次出现位置的索引;
int lastIndexOf(object o):返回对象o在List集合中最后一次出现的位置索引;
Object remove(int index):删除并返回index索引处的元素;
Object set(int index,Object e):把集合index处的元素替换为e对象,返回以前在指定位置的元素;
List subList(int fromIndex,int toIndex):返回从所有fromIndex(包括)到toIndex(不包括)处所有集合元素的子集合。
ListIterator
Iterator的子接口,专门用于操作List集合的输出;
List自己还有一个listIterator()方法,该方法返回ListIterator对象,ListIterator继承了Iterator接口,提供了专门操作List的方法。在Iterator上额外增加的方法:
支持双向输出:
boolean hasPrevious():返回该迭代器关联集合是否还有上一个元素;
Object previous():返回该迭代器的上一个元素;
2. List接口中常用类及区别
Vector:线程安全,但速度慢,已被ArrayList替代。
ArrayList:线程不安全,查询速度快。
LinkedList:链表结构,增删速度快。
Java的List接口有3个实现类,分别是ArrayList、LinkedList、Vector,他们用于存放多个元素,维护元素的次序,而且允许元素重复。
3个具体实现类的区别如下:
1).
ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除,允许空元素
2). Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
3). LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,接口中没有定义的方法get,remove,insertList,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建 List时构造一个同步的List:
List
list = Collections.synchronizedList(new LinkedList(...));
查看Java源代码,发现当数组的大小不够的时候,需要重新建立数组,然后将元素拷贝到新的数组内,ArrayList和Vector的扩展数组的大小不同。
关于ArrayList和Vector区别如下:
- Array List在内存不够时默认是扩展50% + 1个,Vector是默认扩展1
- Vector提供indexOf(obj,
start)接口,ArrayList没有。 - Vector属于线程安全级别的,但是大多数情况下不使用Vector,因为线程安全需要更大的系统开销。
3. ArrayList实现类
ArrayList 内部以数组的形式来保存集合中的元素,线程不安全;通常用for循环遍历。Vector线程安全,但Vector是个古老的类,存在很多缺点。
方法名 |
说明 |
boolean add(Object obj) |
将元素添加到集合中 |
boolean add(int index,Object obj) |
将元素obj插入到集合的index索引位置 |
Object get(int index) |
返回集合中索引为index的元素 |
int indexOf(Object obj) |
返回元素在集合中出现的索引 |
Object set(int index,Object obj) |
将index索引位置的元素替换为obj元素 |
Object remove(int index) |
删除并返回index索引位置的元素 |
boolean isEmpty () |
判断集合是否为空 |
boolean contains (Object obj) |
判断集合中是否包含obj |
示例:
- public class ArrayLsitDemo {
- /**
- * @param args
- */
- public static void main(String[] args) {
- ArrayList<String> arr = new ArrayList<String>();
- //增
- arr.add("菠萝");
- arr.add("香蕉");
- arr.add("李子");
- print(arr);
- //删
- arr.remove(0);//根据索引删除,引用对象知识移除地址
- print(arr);
- //改
- arr.set(1, "橙子");
- print(arr);
- //查
- System.out.println(arr.get(0));
- }
- //遍历
- public static void print(ArrayList<String> arr){
- for(String str:arr){
- System.out.print(str + " ");
- }
- System.out.println();
- }
- }
4. LinkedList实现类
LinkedList可以根据索引来访问集合中的元素,此外还实现了Deque接口,所以也可以当成双端队列来使用,即可当“栈”(先进后出),也可以当作队(先进先出);内部是以线性表和链表实现的,保证输入的顺序。通常用Iterator遍历。
方法名 |
说明 |
void |
将给定元素插入当前集合头部 |
void |
将给定元素插入当前集合尾部 |
Object |
获得当前集合的第一个元素 |
Object |
获得当前集合的最后一个元素 |
Object |
移除并返回当前集合的第一个元素 |
Object |
移除并返回当前集合的最后一个元素 |
示例:
- public class LinkedListDemo {
- /**
- * @param args
- */
- public static void main(String[] args) {
- LinkedList<String> list = new LinkedList<String>();
- list.add("哈哈");
- list.add("呵呵");
- list.add("嘿嘿");
- print(list);
- list.addFirst("嘻嘻");//链表可以在头或尾插入
- print(list);
- //将元素加入到对头
- list.offerFirst("来来");
- //将元素加入到队尾
- list.offerLast("去去");
- print(list);
- //访问并不删除栈顶
- System.out.println("访问并不删除栈顶----" +list.peekFirst());
- print(list);
- //访问并删除栈顶
- System.out.println("访问并删除栈顶----" + list.poll());
- print(list);
- }
- public static void print(List list){
- Iterator iterator = list.iterator();
- while(iterator.hasNext()){
- System.out.print(iterator.next() + " ");
- . }
- System.out.println();
- . }
- }
三.Set接口
List集合代表一个元素不可重复的集合。操作数据的方法与List类似,Set接口不存在get()方法
常用实现类:HashSet(无序) TreeSet(有序)
遍历方式:for循环,Iterator
-
1.
Set接口方法
l 添加元素
boolean add(E e) 如果 set 中尚未存在指定的元素,则添加此元素(可选操作)。
boolean addAll(Collection<? extends E> c) 如果 set 中没有指定 collection 中的所有元素,则将其添加到此 set 中(可选操作)。
l 遍历Set
Iterator<E>
iterator() 返回在此 set 中的元素上进行迭代的迭代器。
l 是否为空
boolean
isEmpty() 如果 set 不包含元素,则返回 true。
l 元素个数
int size() 返回 set 中的元素数(其容量)。
l 清除元素
void clear() 移除此 set 中的所有元素(可选操作)。
boolean remove(Object o) 如果 set 中存在指定的元素,则将其移除(可选操作)。
boolean removeAll(Collection<?> c) 移除 set 中那些包含在指定 collection 中的元素(可选操作)。
l 是否包含元素或集合
boolean contains(Object o) 如果 set 包含指定的元素,则返回 true。
boolean containsAll(Collection<?>
c) 如果此 set 包含指定 collection 的所有元素,则返回 true。
l 其他
boolean equals(Object o) 比较指定对象与此 set 的相等性。
int hashCode() 返回 set 的哈希码值。
Object[]
toArray() 返回一个包含 set 中所有元素的数组。
<T> T[] toArray(T[] a) 返回一个包含此 set 中所有元素的数组;返回数组的运行时类型是指定数组的类型。
-
2.
HashSet实现类
此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。
此类为基本操作提供了稳定性能,这些基本操作包括 add、remove、contains 和 size,假定哈希函数将这些元素正确地分布在桶中。对此 set 进行迭代所需的时间与 HashSet 实例的大小(元素的数量)和底层 HashMap 实例(桶的数量)的“容量”的和成比例。因此,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。
注意,此实现不是同步的。如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步。这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:
Set s = Collections.synchronizedSet(new
HashSet(...));
-
3.
TreeSet实现类
基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。
注意,如果要正确实现 Set 接口,则 set 维护的顺序(无论是否提供了显式比较器)必须与 equals 一致。(关于与 equals 一致 的精确定义,请参阅 Comparable 或 Comparator。)这是因为 Set 接口是按照 equals 操作定义的,但 TreeSet 实例使用它的 compareTo(或 compare)方法对所有元素进行比较,因此从 set 的观点来看,此方法认为相等的两个元素就是相等的。即使 set 的顺序与 equals 不一致,其行为也是 定义良好的;它只是违背了 Set 接口的常规协定。
注意,此实现不是同步的。如果多个线程同时访问一个 TreeSet,而其中至少一个线程修改了该 set,那么它必须 外部同步。这一般是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSortedSet 方法来“包装”该 set。此操作最好在创建时进行,以防止对 set 的意外非同步访问:
SortedSet s =
Collections.synchronizedSortedSet(new TreeSet(...));
l TreeSet原理
/*
* TreeSet存储对象的时候, 可以排序, 但是需要指定排序的算法
*
* Integer能排序(有默认顺序), String能排序(有默认顺序), 自定义的类存储的时候出现异常(没有顺序)
*
* 如果想把自定义类的对象存入TreeSet进行排序, 那么必须实现Comparable接口
* 在类上implement Comparable
* 重写compareTo()方法
* 在方法内定义比较算法, 根据大小关系, 返回正数负数或零
* 在使用TreeSet存储对象的时候, add()方法内部就会自动调用compareTo()方法进行比较, 根据比较结果使用二叉树形式进行存储
*/
四.Map接口
Map接口储存一组成对的键-值对象,提供key(键)到value(值)的映射,Map中的key不要求有序,不允许重复。value同样不要求有序,但可以重复。最常见的Map实现类是HashMap,他的储存方式是哈希表,优点是查询指定元素效率高。
常用实现类:HashMap(无序) TreeMap(有序)、HashTable(过时)
遍历方式:for循环,Iterator
-
1.
Map接口方法
l Map 接口的常用方法如下所示:
No. |
方法 |
类型 |
描述 |
1 |
V put(K key,V value) |
普通 |
增加内容 |
2 |
V get(Object key) |
普通 |
取得设置的内容,根据key取得 |
3 |
boolean containsKey(Object key) |
普通 |
查找指定的key 是否存在 |
4 |
boolean containsValue(Object value) |
普通 |
查找指定的value 是否存在 |
5 |
boolean isEmpty() |
普通 |
判断集合是否为空 |
6 |
Set<K> keySet() |
普通 |
将全部的key变为Set 集合 |
7 |
Collection<V> values() |
普通 |
将全部的value 变为Collection集合 |
8 |
V remove(Object key) |
普通 |
根据key 删除内容 |
9 |
void putAll(Map<? extends K,? extends V>m) |
普通 |
增加一组数据 |
-
2.
HashMap实现类
基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
注意,此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。(结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的非同步访问,如下所示:
Map m =
Collections.synchronizedMap(new HashMap(...));
-
3.
TreeMap实现类
基于红黑树(Red-Black
tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
注意,此实现不是同步的。如果多个线程同时访问一个映射,并且其中至少一个线程从结构上修改了该映射,则其必须 外部同步。(结构上的修改是指添加或删除一个或多个映射关系的操作;仅改变与现有键关联的值不是结构上的修改。)这一般是通过对自然封装该映射的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSortedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的不同步访问,如下所示:
SortedMap m = Collections.synchronizedSortedMap(new
TreeMap(...));
-
4.
HashMap和HashTable区别
Hashtable 实际上与Vector 的产生时代是一样,也属于最早的集合操作类。之后只是扩展了其应用实现了Map 接口而已
HashTable类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。
为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。
No |
区别点 |
HashMap |
Hashtable |
1 |
推出时间 |
是在JDK1.2之后推出的,属于新的类 |
是在JDK1.0时推出的,属于旧的操作类 |
2 |
操作 |
采用异步的处理操作 |
采用同步的处理操作 |
3 |
性能 |
性能高 |
性能相对较低 |
4 |
安全 |
非线程安全的操作 |
线程安全 |
五.Collections类
Java中给我们提供了一个工具类Collections来方便我们操作集合,在该类中的全部方法均为static静态方法,可以直接通过类名调用。其中比较常用的方法有以下方法:
void sort(List
list,Comparator com)-- >如果我想将一个List数组进行排序,需要将数组中元素取出再放入一个TreeSet集合然后再传入一个比较器,而用这个方法直接传入一个比较器就可以了
void max(List
list,Comparator com)-- >根据比较器取出最大值
int
binarySearch(List list,Object key)-- >找出List集合元素key并返回搜索键的索引。注意执行该方法时必须要对集合进行sort操作,否则结果是不明确的,或者在参数中再传入一个比较器。如果没有该元素,则返回(-(插入点)-1)。
void fill(List
list,Object obj)-- >将list集合中所有元素换为obj
void replaceAll(List
list,ObjectoldValue,Object newValue)-- >将集合中所有为oldValued的元素换成newValue元素。
void
reverse(List list)-- >将集合翻转
Comparator
reverseOrder()-- >返回一个比较器,可以讲集合内元素顺序倒转。
CollectionsynchronizedCollection(Collection
collection) -- >通过传入集合获得一个同步的集合
List synchronizedList(List
list)
Set
synchronizedSet(Set set)
Map
synchronizedMap(Map map)
void swap(List
list,int index1,int index2)-->将集合list中角标为index1和index2的元素互换位置
六.泛型
泛型是java在jdk1.5之后才出现的。因为操作集合的时候每次都要判断元素类型,会出现ClassCastException。所以引入了泛型机制,使元素类型在编译的时候就做了限定。例如定义一个Collection <String> c=new Collection<String> ();那么这个Collection集合中就只能存入String类型的对象。
参数化类型不考虑类型参数的继承关系,比如Vector<String>v
= new Vector<Object>()或者Vector<Object> v = new Vector<String>();都是错误的。如果想达到这个效果可以用通配符来做到。如Vector<?extends Object> v=new Vector<String>()。
使用?通配符可以引用其他各种参数化类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。如我们定义一个打印参数化集合的方法。
public void printCollection(Collection<?>
collection )
{
for(Object obj:collection)
System.out.println(obj);
}
collection.size();//正确,因为此方法与类型参数没有关系
collection.add(“abc”);//错误,因为collection不能确定自己匹配的参数是String
通配符的上限是?extends T;通配符的下限是? super T。
泛型是提供给Javac编译器使用的。可以限定集合中输入的类型,让编译器挡住原始程序的非法输入,编译器编译带类型说明的集合时会去掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样,由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。
void sort(List
list,Comparator com)-- >如果我想将一个List数组进行排序,需要将数组中元素取出再放入一个TreeSet集合然后再传入一个比较器,而用这个方法直接传入一个比较器就可以了
void max(List
list,Comparator com)-- >根据比较器取出最大值
需要注意的几点是泛型的实际参数只能是引用类型不可以是基本类型,泛型定义在类上时。类中的静态成员不具备该类型参数。