Java中的集合
以下内容引用自http://wiki.jikexueyuan.com/project/java/collections.html:
Java 2之前,Java为对象组的存储和操作提供了特别的类比如字典(Dictionary),向量(Vector),堆栈(Stack)和属性(Properties)。尽管这些类确实有用,它们缺少一个中心的,统一的主题。因此,使用向量的方法和使用属性的方法是不同的。
集合框架被设计来满足几个目标
- 框架需要是高性能的。基础集合(动态数组,链表,树和哈希表)是高效的。
- 框架需要允许不同的集合类型以类似的方式和高度的互操作性工作。
- 扩展或者调整集合必须是简单的。
为此,整个集合框架被设计围绕一系列的标准接口。几个接口的标准实现例如LinkedList, HashSet和TreeSet被提供,如果选择的话,可以使用,也可以实现自己的集合。
一个集合框架是一个统一的体系结构表示和操作集合。所有的集合框架包含以下:
- 接口:这些是代表集合的抽象数据类型。接口允许集合独立操作它们表示的细节。在面向对象的语言中,接口通常形成一个层次结构。
- 实现,即类:这些是集合接口的具体实施。从本质上说,它们是可重用的数据结构。
- 算法:这些是在实现集合接口的对象上进行有用计算的方法,比如搜索和排序。算法被称为多态的,那就是说,同一个方法能被用在许多不同的合适的集合接口的实现上。
除了集合,框架定义了几个Map接口和类Maps存储键值对。尽管Maps不是正确使用集合的术语,但是他们完全由集合整合起来。
一、Collection接口
集合框架定义了几个接口。如下提供了每个接口的概览:
接口 | 描述 |
---|---|
Collection Interface | 可以使用对象组;它是集合层次阶段的顶端 |
List Interface | 它继承了Collection并且List的一个实例存储了元素的一个有序集合 |
Set | 它继承了Collection来处理集,它必须含有特殊的元素 |
SortedSet | 它继承了Set来处理 排序的 set |
Map | 它将独特的键和值匹配 |
Map Entry | 这描述了映射中的一个元素(一个键值对)。它是Map的一个内部类。 |
SortedMap | 它继承了Map因此键按升序保持 |
Enumeration | 它是旧有的接口并定义了在对象的集合中列举(一次获得一个)元素的方法。这个旧有的接口被迭代器取代了。 |
二、Collection类
Java提供了一系列的实现集合接口的标准集合类。一些类提供了完全的能被直接使用的实现,其他就是抽象类,提供的被用来作为创建具体集合的实现。
标准的Collection类在下面的表格中被概括:
类 | 描述 |
---|---|
AbstractCollection | 实现大部分的Collection接口 |
AbstractList | 继承AbstractCollection并且实现大部分List接口 |
AbstractSequentialList | 通过一个使用有序的而不是随机访问它的元素的集合继承AbstractList |
LinkedList | 通过继承AbstractSequentialList实现一个链表 |
ArrayList | 通过继承AbstractList实现一个动态数组 |
AbstractSet | 继承AbstractCollection并实现大部分的Set接口 |
HashSet | 用一个哈希表继承AbstractSet |
LinkedHashSet | 继承HashSet来允许插入顺序迭代 |
TreeSet | 实现在树中存储的一个集。继承AbstractSet |
AbstractMap | 实现大部分的Map接口 |
HashMap | 用一个哈希表继承AbstractMap |
TreeMap | 用一棵树继承AbstractMap |
WeakHashMap | 用一个使用弱键的哈希表来继承AbstractMap |
LinkedHashMap | 继承AbstractMap来允许插入顺序迭代 |
IdentityHashMap | 继承AbstractMap类并且当比较文档时平等使用参考 |
AbstractCollection,AbstractSet,AbstractList,AbstractSequentialList和AbstractMap类提供了核心集合接口的实现,尽量减少努力来实现它们。
以下的由java.util定义的类:
类 | 描述 |
---|---|
Vector | 这实现一个动态数组。它和ArrayList类似,但也有一些不同。 |
Stack | Stack是Vector的实现标准的后进先出栈的子类 |
Dictionary | 是一个抽象的代表一个键值对存储库的类并且操作起来非常像Map |
Hashtable | Hashtable是初始的java.util的一部分并且是Dictionary的具体实现 |
Properties | Properties是Hashtable的一个子类。它被用来保持键是一个字符串并且值也是一个字符串的值的列表 |
BitSet | 一个BitSet类创建一个特殊的保持bit数值的数组类型。这个数组的大小能根据需要增长 |
三、Collection算法
集合框架定义了几个能被应用到Collections和Maps的算法。这些算法在Collection类的内部被定义为静态方法。
几个方法能抛出异常ClassCastException,它发生在想要比较不兼容的类型时;或者异常UnsupportedOperationException,它发生在想要修改一个不能修改的集合时。
集合定义了三个静态变量:EMPTY_SET,EMPTY_LIST和EMPTY_MAP。所有都是不变的。
收集框架算法中定义的方法总结在下表中:
方法 | 说明 |
---|---|
static int binarySearch(List list, Object value, Comparator c) |
根据c.搜索列表中的价值。返回列表中的值的位置,如果未找到值,则返回-1。 |
static int binarySearch(List list,Object value) |
搜索列表中的值。列表必须排序。返回列表中的值的位置,如果未找到值,则返回-1。 |
|
将list2的元素复制到list1。 |
static Enumeration enumeration(Collection c) |
返回c的枚举。 |
static void fill(List list, Object obj) |
将obj分配给列表的每个元素。 |
static int indexOfSubList(List list, List subList) |
子列表首次出现的搜索列表。返回第一个匹配的索引,如果没有找到匹配,则返回.1。 |
static int lastIndexOfSubList(List list, List subList) |
子列表最后一次出现的搜索列表。返回上一个匹配的索引,如果没有匹配,则返回.1。 |
static ArrayList list(Enumeration enum) |
返回一个包含枚举元素的ArrayList。 |
static Object max(Collection c, Comparator comp) |
返回由comp确定的c中的最大元素。 |
static Object max(Collection c) |
返回c中的最大元素,由自然排序确定。集合不需要排序。 |
static Object min(Collection c, Comparator comp) |
返回由comp确定的c中的最小元素。集合不需要排序。 |
static Object min(Collection c) |
返回由自然排序确定的c中的最小元素。 |
static List nCopies(int num, Object obj) |
返回包含在不可变列表中的对象的num副本。num必须大于或等于零。 |
static boolean replaceAll(List list, Object old, Object new) |
在列表中替换所有出现的旧版本。如果至少有一个替换发生,则返回true。返回false,否则。 |
static void reverse(List list) |
反转列表中的序列。 |
static Comparator reverseOrder( ) |
返回反向比较器。 |
static void rotate(List list, int n) |
将列表旋转到右侧的n个位置。要向左旋转,请对n使用负值。 |
static void shuffle(List list, Random r) |
通过使用r作为随机数的来源来洗牌(即随机化)列表中的元素。 |
static void shuffle(List list) |
洗牌(即随机化)列表中的元素。 |
static Set singleton(Object obj) |
将obj作为不可变集返回。这是将单个对象转换为集合的简单方法。 |
static List singletonList(Object obj) |
将obj作为不可变列表返回。这是将单个对象转换为列表的简单方法。 |
static Map singletonMap(Object k, Object v) |
返回键/值对k / v作为不可变地图。这是将单个键/值对转换为地图的简单方法。 |
static void sort(List list, Comparator comp) |
对由comp确定的列表元素进行排序。 |
static void sort(List list) |
根据其自然排序确定列表的元素。 |
static void swap(List list, int idx1, int idx2) |
以idx1和idx2指定的索引交换列表中的元素。 |
static Collection synchronizedCollection(Collection c) |
返回由c支持的线程安全集合。 |
static List synchronizedList(List list) |
返回由列表支持的线程安全列表。 |
static Map synchronizedMap(Map m) |
返回由m支持的线程安全映射。 |
static Set synchronizedSet(Set s) |
返回由s支持的线程安全集。 |
static SortedMap synchronizedSortedMap(SortedMap sm) |
返回由sm支持的线程安全排序集。 |
static SortedSet synchronizedSortedSet(SortedSet ss) |
返回由ss支持的线程安全集。 |
static Collection unmodifiableCollection(Collection c) |
返回由c支持的不可修改的集合。 |
static List unmodifiableList(List list) |
返回列表支持的不可修改列表。 |
static Map unmodifiableMap(Map m) |
返回由m支持的不可修改的地图。 |
static Set unmodifiableSet(Set s) |
返回由s支持的不可修改的集合。 |
static SortedMap unmodifiableSortedMap(SortedMap sm) |
返回由sm支持的不可修改的排序映射。 |
static SortedSet unmodifiableSortedSet(SortedSet ss) |
返回由ss支持的不可修改的排序集。 |
示例:
以下是一个示例,其演示了各种算法。
import java.util.*; public class AlgorithmsDemo { public static void main(String args[]) { // Create and initialize linked list LinkedList ll = new LinkedList(); ll.add(new Integer(-8)); ll.add(new Integer(20)); ll.add(new Integer(-20)); ll.add(new Integer(8)); // Create a reverse order comparator Comparator r = Collections.reverseOrder(); // Sort list by using the comparator Collections.sort(ll, r); // Get iterator Iterator li = ll.iterator(); System.out.print("List sorted in reverse: "); while(li.hasNext()) { System.out.print(li.next() + " "); } System.out.println(); Collections.shuffle(ll); // display randomized list li = ll.iterator(); System.out.print("List shuffled: "); while(li.hasNext()) { System.out.print(li.next() + " "); } System.out.println(); System.out.println("Minimum: " + Collections.min(ll)); System.out.println("Maximum: " + Collections.max(ll)); } } //这将产生以下结果 List sorted in reverse: 20 8 -8 -20 List shuffled: 20 -20 8 -8 Minimum: -20 Maximum: 20
四、如何使用Iterator(迭代器)
通常,想要在集合中循环元素。比如,可能想要显示每个元素。
这么做最简单的方法是使用Iterator,它是一个实现或者是Iterator或者ListIterator接口的对象。
Iterator可以通过一个集合循环,获得或者除去元素。ListIterator继承了Iterator来允许一个列表的双向遍历和元素的修改。
在通过Iterator访问集合之前,必须先获取一个集合。每个集合类都提供了一个iterator()方法,它将Iterator返回到集合的开头。通过使用此Iterator对象,可以一次访问集合中的每个元素,或者一个元素。
一般来说,要使用Iterator循环遍历集合的内容,请按照下列步骤操作:
-
通过调用集合的iterator()方法获取集合开始的Iterator。
-
设置一个调用hasNext()的循环。只要hasNext()返回true,循环迭代。
-
在循环中,通过调用next()来获取每个元素。
对于实现List的集合,还可以通过调用ListIterator获取Iterator。
迭代器(Iterator)声明的方法:
方法 | 说明 |
---|---|
boolean hasNext( ) |
如果有更多元素,则返回true。否则返回false。 |
Object next( ) |
返回下一个元素。如果没有下一个元素,则抛出NoSuchElementException异常。 |
void remove( ) |
删除当前元素。如果尝试调用remove()之前未调用next(),则抛出IllegalStateException异常。 |
ListIterator声明的方法:
方法 | 说明 |
---|---|
void add(Object obj) |
将obj插入到下一次调用next()返回的元素前面的列表中。 |
boolean hasNext( ) |
如果有下一个元素,则返回true。否则返回false。 |
boolean hasPrevious( ) |
如果存在先前的元素,则返回true。否则返回false。 |
Object next( ) |
返回下一个元素。如果没有下一个元素,则抛出NoSuchElementException异常。 |
int nextIndex( ) |
返回下一个元素的索引。如果没有下一个元素,则返回列表的大小。 |
Object previous( ) |
返回上一个元素。如果没有先前的元素,则抛出NoSuchElementException异常。 |
int previousIndex( ) |
返回上一个元素的索引。如果没有以前的元素,返回-1。 |
void remove( ) |
从列表中删除当前元素。如果在调用next()或previous()之前调用remove(),则抛出IllegalStateException异常。 |
void set(Object obj) |
将obj分配给当前元素。这是通过调用next()或previous()最后返回的元素。 |
示例:
下面是一个演示Iterator和ListIterator的例子。它使用一个ArrayList对象,但一般原则适用于任何类型的集合。
当然,ListIterator仅适用于实现List接口的那些集合。
import java.util.*; public class IteratorDemo { public static void main(String args[]) { // Create an array list ArrayList al = new ArrayList(); // add elements to the array list al.add("C"); al.add("A"); al.add("E"); al.add("B"); al.add("D"); al.add("F"); // Use iterator to display contents of al System.out.print("Original contents of al: "); Iterator itr = al.iterator(); while(itr.hasNext()) { Object element = itr.next(); System.out.print(element + " "); } System.out.println(); // Modify objects being iterated ListIterator litr = al.listIterator(); while(litr.hasNext()) { Object element = litr.next(); litr.set(element + "+"); } System.out.print("Modified contents of al: "); itr = al.iterator(); while(itr.hasNext()) { Object element = itr.next(); System.out.print(element + " "); } System.out.println(); // Now, display the list backwards System.out.print("Modified list backwards: "); while(litr.hasPrevious()) { Object element = litr.previous(); System.out.print(element + " "); } System.out.println(); } } //这将产生以下结果: Original contents of al: C A E B D F Modified contents of al: C+ A+ E+ B+ D+ F+ Modified list backwards: F+ D+ B+ E+ A+ C+
五、如何使用Comparator(比较器)
TreeSet和TreeMap都以顺序保存元素。然而,是Comparator精确定义排序。
这个接口将一个给定的集合用不同数量的方法排序。这个接口也能被用来排列任何类的任何实例(甚至是不能修改的类)。
比较器接口定义了两种方法:compare()和equals()。
compare()方法,如下所示,比较了两个元素的顺序:
比较方法:
int compare(Object obj1, Object obj2)
obj1和obj2是要进行比较的对象。如果对象相等,则此方法返回零。如果obj1大于obj2,则返回正值。否则返回负值。
通过重写compare(),可以改变对象的排序方式。例如,要按相反顺序进行排序,可以创建一个比较器来反转比较结果。
等于方法:
这里显示的equals()方法测试对象是否等于调用比较器
boolean equals(Object obj)
obj是要进行相等测试的对象。如果obj和调用对象都是Comparator对象并使用相同的顺序,则该方法返回true。否则返回false。
覆盖equals()是不必要的,大多数简单的比较器都不会这样做。
示例:
import java.util.*; class Dog implements Comparator<Dog>, Comparable<Dog> { private String name; private int age; Dog() { } Dog(String n, int a) { name = n; age = a; } public String getDogName() { return name; } public int getDogAge() { return age; } // Overriding the compareTo method public int compareTo(Dog d) { return (this.name).compareTo(d.name); } // Overriding the compare method to sort the age public int compare(Dog d, Dog d1) { return d.age - d1.age; } } public class Example { public static void main(String args[]) { // Takes a list o Dog objects List<Dog> list = new ArrayList<Dog>(); list.add(new Dog("Shaggy", 3)); list.add(new Dog("Lacy", 2)); list.add(new Dog("Roger", 10)); list.add(new Dog("Tommy", 4)); list.add(new Dog("Tammy", 1)); Collections.sort(list); // Sorts the array list for(Dog a: list) // printing the sorted list of names System.out.print(a.getDogName() + ", "); // Sorts the array list using comparator Collections.sort(list, new Dog()); System.out.println(" "); for(Dog a: list) // printing the sorted list of ages System.out.print(a.getDogName() +" : "+ a.getDogAge() + ", "); } } //这将产生以下结果: Lacy, Roger, Shaggy, Tammy, Tommy, Tammy : 1, Lacy : 2, Shaggy : 3, Tommy : 4, Roger : 10,
六、总结
Java集合框架给了程序员打包数据结构和操作它们的算法的入口。
一个集合是一个能对其他对象引用的对象。Collection接口声明了能在每一个集合类型上操作的操作。
集合框架的类和接口都在java.util包内。
测试工程:https://github.com/easonjim/5_java_example/tree/master/javabasicstest/test23/test1