Java中的集合类
1.集合的特点:
都位于 java.util包中,不能存放基本类型的数据,而只能存放对象的引用,操作的数目可以不固定(类似于动态数组)。
2.分类:
有三个类:
a.Set(集):对象不按特定方式排序,没有重复元素。这个与数学中的集合概念最相似。
b.List(列表):按照索引位置排序,可以有重复元素,允许按照对象在集合中的索引位置检索对象。
c.Map(映射):每一个元素包含一个键值对。没有重复的键值对,但是值对象可以重复。
有两个接口:
Collection接口适用于Java集合中的Set和List(这两个类直接继承了这个接口),提供了一些通用操纵的静态方法。
Iterator接口隐藏了底层集合的数据结构,对外提供了遍历各种数据类型集合的统一接口。由collection集合的iterator得到一个Iterator。语法如下:
Iterator it = set.iterator();
(注意此后若通过collection方法修改了集合则使用next()方法时会出现异常,因为其运用了所谓快速失败机制。避免了潜在的共享资源竞争而导致的并发问题)。
3.Set集合:
主要有两个实现类:HashSet和TreeSet。
前者使用哈希算法,存取速度快,它还有一个子类LinkedHashSet类,性能更高。HashSet向集合中加入一个对象时,会调用对象的hashCode()方法得到哈希码,然后根据这个哈希码计算出对象在集合中的存放位置。
为了保证其能正常工作,要求当两个对象用equals()方法比较的结果为true时它们的哈希码也必须相同。这就要求我们如果在我们自己设计的类中覆盖了equals方法,那么也也应该覆盖hashCode()方法。用equals()方法比较的结果为false时,最好hashCode不同,减少哈希冲突,提高性能。
而后者则实现了SortedSet接口,具有排序功能。由于用户属性变化不会导致重新排序,所以适合加入不可变类。向其中加入自定义的类时要注意,实现其Comparable接口,并且euqals()得出的结论要与compareTo()得出的结论相同。另外,它还支持Comparator接口,可以定义一个实现了该接口的类CustomerComparator实现自定义的排序。在定义时使用如下的语法:
Set set = new TreeSet(new CustomerComparator);
1. HashSet
实现 Set 接口的 hash table( 哈希表 ) ,依靠 HashMap 来实现的。
我们应该为要存放到散列表的各个对象定义 hashCode() 和 equals()
因为 hashSet 是实现 set 接口,所以不可以有重复的元素。那么这个散列表是如何判定元素是否重复,通过 hashcode() 来判别的,对于不同的对象他们的 hashcode 是不同的,为了按照自然排序的方式把对象存放到散列表中,我要把对象的 hashcode() 和 equals() 重写。
2 . TreeSet
TreeSet 是依靠 TreeMap 来实现的。
TreeSet 是一个有序集合 , TreeSet 中元素将按照升序排列 , 缺省是按照自然顺序进行排列 , 意味着 TreeSet 中元素要实现 Comparable 接口。
我们可以在构造 TreeSet 对象时,传递实现了 Comparator 接口的比较器对象
HashSet 和 TreeSet 的比较
HashSet 是基于 Hash 算法实现的 , 其性能通常都优于 TreeSet 。我们通常都应该使用 HashSet , 在我们需要排序的功能时 , 我们才使用 TreeSet
4.List列表:
主要特征是以线性方式存储,集合中可存放重复元素。Thoughts Works 公司的一个顾问曾拟文说在Java 中强烈建议使用List替代所有的数组。在数组与List转换时使用asList的方法。
主要有两个实现类:ArrayList和LinkedList。
前者的特点是允许对元素进行快速随机访问,但插于和删除元素的速度较慢。一般用于代表可变数组。
后者的特点是采用链表数据结构,对顺序访问和删除、插入元素速度较快,但随机访问速度较慢。一般用于代表堆栈、队列和双向队列。
一个比较实用的接口是ListIterator接口,使用listIterator方法返回得到,它继承了Iterator接口,并且提供了专门操纵列表的方法。
Arrays类的一个方法asList()可以把一个数组包装成List对象,但此时这个List固定长度了,因为所有操作都会作用于底层数组。
以前还有一个叫做Vector类,现在已经不提倡使用了。
性能上,Java数组的随机访问和迭代操作速度最快,ArrayList的随机访问速度也较快,而LinkedList则进行插入和删除操作有最快的速度。
因此尽量不要在List中进行迭代操作,而使用addAll
或 removeAll
,传入包含要对其添加或移除元素的集合作为参数,将一个集合(特别是由数组转化而成的集合)的内容转移到另一个集合,或者从一个较大对象集合中移除一个较小对象集合,从而完成成员的增减。迭代可能会出现的问题如下:
- 每次添加或移除元素后重新调整集合将非常低效。
- 每次在获取锁、执行操作和释放锁的过程中,都存在潜在的并发困境。
- 当添加或移除元素时,存取集合的其他线程会引起竞争条件。
1、 ArrayList :我们可以将其看作是能够自动增长容量的数组。它的底层是用对象数组实现的 , 它有 size(),get(index),add(), 方法 , 并且实现了 toString() 方法 , 只要它所包含的对象也实现了这个方法就可以直接通过打印列表的方式来将各个元素输出。
利用 ArrayList 的 toArray() 返回一个数组 , 可以作为数组和列表转换的桥梁。
Arrays.asList() 返回一个列表,但这个列表是固定尺寸的,不可以通过 add() 方法添加对象,只能通过 set(index,object) 方法来修改对应索引的值。
迭代器 (Iterator) 给我们提供了一种通用的方式来访问集合中的元素。
一般实现的方法如下,
// 利用迭代器设计了一种通用的方法 , 用来取出集合中的元素 , 因为有些集合类中并没有 get() 方法 .
public static void printElements(Collection c)
{
Iterator it=c.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
因为后面讲到的 set 接口的实现类,就没有 get() 方法只能通过迭代器来访问列表元素。 Iterator 中只有三个方法如下:
boolean |
hasNext () |
E |
next () |
void |
remove () |
而且remove ()方法是可选的,这样做的目的也是为了避免产生很多的借口,增加了学习难度。另外还要提醒大家注意两点:
(1) remove() 方法
在使用 remove() 方法时,要注意先要用 next() 方法返回这个元素,然后才能删除这个元素。但是用迭代器很少使用删除方法,一般都是使用输出功能。
(2) Arrays.aslist() 返回的列表,并没有实现 remove() 方法 。
(3) iterator 和 Enumeration 的比较 , 首选是前者 , 只有在 hashmap 这个类中会涉及到这方面的东西。
Collections 类中所有的方法都是静态方法 , 提供了一个自然排序的方法 sort(list) 和 sort(list,comparator(java.util))
(a) 有比较器的排序方法
要用一个内部类(最好)实现这个比较器接口中的
compare (T o1, T o2)
L 另外一个方法一般不用实现,因为一般的类都是继承自 Object 这个基类,已经实现了 equals() 方法。
b) 没有比较器的排序方法
必须使 list 中的对象实现 Comparable 接口中的 compareTo () 方法。
另外 Collections 类还实现了下面的方法 :
1、取最大和最小的元素 :Collections.max() 、 Collections.min() 。
2、在已排序的 List 中搜索指定的元素 :Collectons.binarySearch() 。
2、LinkedList
1 LinkedList 是采用双向循环链表实现的。
2 利用 LinkedList 实现栈 (stack) 、队列 (queue) 、双向队列 (double-ended queue ) 。
具体的代码可以参考文件夹LinkList下的代码,模拟了栈和队列。
ArrayList 和 LinkedList 的比较
1、ArrayList 底层采用数组完成 , 而 LinkedList 则是以一般的双向链表 (double-linked list) 完成 , 其内每个对象除了数据本身外 , 还有两个 引用 , 分别指向前一个元素和后一个元素。
2、 如果我们经常在 List 的开始处增加元素,或者在 List 中进行插入和删除操作,我们应该使用 LinkedList ,否则的话,使用 ArrayList 将更加快速。
5.Map映射:
每一个元素包含一个键值对,而且值对象还可以Map映射类型,键对象不允许重复。
Map的entrySet()方法返回了一个Set集合,这个集合存放了Map.entry类型的元素,每个Map.Entry对象代表Map中的一个键值对。而keySet()方法返回集合中所有键对象的集合。
主要有两个实现类:HashMap和TreeMap,与Set的两个实现类特性和使用须知基本相同。
注意:永远不要将可变对象类型用作 HashMap
中的键,否则支持哈希码的键依赖于可变字段的内容,这样hashCode()
容易改变,产生 bug。
6.使用的注意事项:
1)在泛型中可以使用类似Set 表示只接受Object类型及其子类型。或者Set表示接受String类型和其父类型。
2)可以使用更简便的遍历方法以代替Iterator,这个增强适用于实现
Iterable
接口的任何对象,而不仅仅是Collections
:for(String element : set){
System.out.println(element);
}
3)Set、List和Map实现类都没有采取同步机制,要注意并发问题。
4)Collections实用类,使用方法例如:Collections.sort(list);直接调用(因为是静态的嘛),注意与Collection接口区分。
5)List在移除一个成员时后一个其 “后面” 的各项会上升填补空位,这可能是与数组显著的不同吧。
7、几种关系:
(1)Vector 和 ArrayList
Vector非常类似ArrayList,但是Vector是同步的.Vector它允许所有元素,包括null
ArrayList实现了可变大小的数组。它允许所有元素,包括null。
(2)HashMap HashTable
HashTable与HashMap非常类似,除了HashTable是同步的和HashTable不允许null Object作为key或value值.
Hashtable是Dictionary的子类,HashMap是Map接口的一个实现类
(3)HashSet TreeSet
Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。 HashSet 允许null 值
TreeSet可对其插入的元素按自然顺序排序(Comparable or Comparator ),TreeSet不允许null值将抛出java.lang.NullPointerException.