该文章所讲内容基本涵盖了Collection里面的全部东西,尽管基于jdk 1.5的。可是思路非常清晰
1.引言
1.1 Collection框架的介绍
尽管我们能够使用数组去存储具有同样类型的元素集合(包含基本类型和对象类型),可是数组不支持所谓的动态内存分配,一旦分配之后,它的长度就是固定的,无法改变,另外,数组是一个简单的线性结构。在我们的实际开发中,可能会须要更复杂的数据结构。比如linked list, stack, hash table, sets, 或者 trees.
在Java中,有统一的Collection框架来支持这样的动态分配的数据结构(比如:ArrayList, LinkedList, Vector, Stack, HashSet, HashMap, Hashtable),它通过接口统一了全部继承类的基本操作,形成了一套主要的体系。
Java集合框架包(java.util)里包括以下内容:
1、接口集合
2、类实现
3、算法(比如排序和查找)
1.2 Colloction样例— ArrayList (Pre-JDK 1.5)
ArrayList是一个线性的数据结构,类似于数组。可是它是可伸缩的。
以下使用了ArrayList来存放String集合对象。
// Pre-JDK 1.5 import java.util.List; import java.util.ArrayList; import java.util.Iterator; public class ArrayListPreJDK15Test { public static void main(String[] args) { List lst = new ArrayList(); // A List contains instances of Object. Upcast ArrayList to List lst.add("alpha"); // add() takes Object. String upcast to Object implicitly lst.add("beta"); lst.add("charlie"); System.out.println(lst); // [alpha, beta, charlie] // Get a "iterator" instance from List to iterate thru all the elements of the List Iterator iter = lst.iterator(); while (iter.hasNext()) { // any more element // Retrieve the next element, explicitly downcast from Object back to String String str = (String)iter.next(); System.out.println(str); } } }
程序分析
- 2-4行引入了java.util包中集合框架的类和接口
上图能够看到ArrayList的继承关系。我们能够看到ArrayList实现了List, Collection 和 Iterable接口。Collection和Iterable接口定义了全部集合都须要实现的基本操作。Collection接口定义了怎样加入和移除一个元素。Iterable接口定义了一种机制去遍历集合中的全部元素。
一般不直接使用Collection接口,而是使用它的子接口List(支持索引訪问的有序列表)、Set(不同意元素的反复)、Queue(先进先出)。
- 在第8行中。创建了一个ArrayList实例。并将它向上转换为List接口,由于ArrayList实现了List接口,一般比較良好的编程操作都是在接口上面,而不是在它的实现上面。Collection框架提供了一个接口集合,这样你能够使用这些接口而不是它的实现。
- Collection接口定义了一些对主要的操作方法。
// Pre-JDK 1.5 boolean add(Object element) // adds an element boolean remove(Object element) // removes an element int size() // returns the size boolean isEmpty() // checks if empty</span>能够看到。在pre-JDK 1.5上,add(Object)方法操作的是Object对象,Object类是全部类的超类,因此,全部类都是Object类的子类,不论什么Java类都能够向上转换为Object类。然后加入到集合中,而且这个转换是编译器隐式操作的。它是类型安全的。
- Iterable接口包括一个抽象方法去获取集合所关联的Iterator对象,这样通过Iterator对象就能够遍历整个集合。
Iterator iterator(); // returns an Iterator object to iterate thru all the elements of the collection
- Iterator声明了以下的抽象方法进行集合遍历
// Pre-JDK 1.5 boolean hasNext() // returns true if it has more elements Object next() // returns the next element void remove() // removes the last element returned by the iterator</span>
- 第15-20行获取ArrayList相关联的Iterator对象,而且使用while循环来遍历集合。
- 在18行,
iter.next()
方法方法返回的是Object类型的对象。这个在上面add(Object)中说过,我们在得到Object类型的对象之后,须要显式的将类型转换为String类型,由于编译器不知道我们须要的详细类型。 - 事实上我们也能够使用LinkedList, Vector 和 Stack。而且仅仅须要改动第8行的实例化代码就可以,其它不变,这也能够看出接口的统一性。尽管实现不同,可是操作的效果同样。
List lst = new LinkedList(); // use "LinkedList" implementation // or List lst = new Vector(); // use "Vector" implementation // or List lst = new Stack(); // use "Stack" implementation
1.3 Pre-JDK 1.5 Collections不是类型安全的
2、编译器不能在编译时检查向下转换的有效性。错误的向下转换会在执行是抛出ClassCastException异常。
// lst is designed to hold Strings lst.add(new Integer(88)); // adds an Integer, implicitly upcast to Object, okay in compile/runtime Iterator iter = lst.iterator(); while (iter.hasNext()) { String str = (String)iter.next(); // compile okay but runtime ClassCastException System.out.println(str); }
1.4 JDK 1.5中引入泛型
List<String> lst = new ArrayList<String>(); // read as List of Strings, ArrayList of Strings上面我们传递了一个String类型,在ArrayList内部的操作使用的就是String类型,避免了类型的转换。假设加入其它类型的元素,编译器就会报错。
// Post-JDK 1.5 with Generics import java.util.List; import java.util.ArrayList; import java.util.Iterator; public class ArrayListPostJDK15Test { public static void main(String[] args) { List<String> lst = new ArrayList<String>(); // Inform compiler about the type lst.add("alpha"); // compiler checks if argument's type is String lst.add("beta"); lst.add("charlie"); System.out.println(lst); // [alpha, beta, charlie] Iterator<String> iter = lst.iterator(); // Iterator of Strings while (iter.hasNext()) { String str = iter.next(); // compiler inserts downcast operator System.out.println(str); } // lst.add(new Integer(1234)); // ERROR: compiler can detect wrong type // Integer intObj = lst.get(0); // ERROR: compiler can detect wrong type // Enhanced for-loop (JDK 1.5) for (String str : lst) { System.out.println(str); } } }
能够看到,使用泛型之后。就基本不存在前面所说隐式和显式类型转换了,由于内部使用的就是我们传递进去的类型。而不是上面所说的Object类型,假设加入其它类型的话,编译器就会报错。
JDK 1.5是兼容pre-JDK 1.5的使用的。比如在JDK 1.5中使用pre-JDK 1.5的使用方法,仍然是能够运行的,仅仅是相同没有类型检查。
// Pre-JDK 1.5 List lst = new ArrayList(); // No type information lst.add("alpha"); // Without generics, compiler can't check if the type is correct
1.7 自己主动装箱和自己主动拆箱(JDK 1.5)
// Pre-JDK 1.5 Integer intObj = new Integer(5566); // wrap int to Integer int i = intObj.intValue(); // unwrap Integer to int Double doubleObj = new Double(55.66); // wrap double to Double double d = doubleObj.doubleValue(); // unwrap Double to double
从上面能够看到。在JDK 1.5之前,装箱和解箱操作须要我们自己手动去完毕。JDK 1.5就针对这一问题。提出了一个新特性。就是自己主动装箱与解箱。也就是本来须要我们手工完毕的事情。编译器帮助我们完毕了。
// JDK 1.5 Integer intObj = 5566; // autobox from int to Integer int i = intObj; // auto-unbox from Integer to int Double doubleObj = 55.66; // autoboxing from double to Double double d = doubleObj; // atuo-unbox from Double to double
样例—Pre JDK 1.5中集合
// Pre-JDK 1.5 import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.util.Random; public class PrimitiveCollectionPreJDK15 { public static void main(String[] args) { List lst = new ArrayList(); // Add 10 random primitive int into the List Random random = new Random(); for (int i = 1; i <= 10; ++i) { // Wrap the primitive int into Integer, upcast to Object lst.add(new Integer(random.nextInt(10))); } System.out.println(lst); Iterator iter = lst.iterator(); while (iter.hasNext()) { // Explicit downcast to Integer, then unwrap to int int i = ((Integer)iter.next()).intValue(); // un-safe at runtime System.out.println(i); } } }
样例—Post-JDK 1.5中的集合
// Post-JDK 1.5 import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.util.Random; public class PrimitiveCollectionJDK15 { public static void main(String[] args) { List<Integer> lst = new ArrayList<Integer>(); // Add 10 random primitive int into the List Random random = new Random(); for (int i = 1; i <= 10; ++i) { lst.add(random.nextInt(10)); // autobox to Integer, upcast to Object, type-safe } System.out.println(lst); // Transverse via iterator Iterator<Integer> iter = lst.iterator(); while (iter.hasNext()) { int i = iter.next(); // downcast to Integer, auto-unbox to int, type-safe System.out.println(i); } // Transverse via enhance for-loop for (int i : lst) { // downcast to Integer, auto-unbox to int, type-safe System.out.println(i); } // Retrieve via for-loop with List's index for (int i = 0; i < lst.size(); ++i) { int j = lst.get(i); // downcast to Integer, auto-unbox to int, type-safe System.out.println(j); } } }
2. Collection接口
2.1 Iterable<E>接口
Iterable<E>接口。它包括了一个泛型类型E。E相应的就是集合元素类型,这个接口声明了一个抽象方法iterator()
用来获取一个Iterator<E>对象,Iterator用来遍历它所关联集合的全部元素。
Iterator<E> iterator(); // Returns the associated Iterator instance // that can be used to transverse thru all the elements of the collection全部Collection的实现(比如:
ArrayList
, LinkedList
, Vector
)必须实现这种方法,而且返回的对象也实现了Iterator接口。
2.2
Iterator<E>接口
Iterator<E>接口声明了以下三个抽象方法。
boolean hasNext() // Returns true if it has more elements E next() // Returns the next element of generic type E void remove() // Removes the last element returned by the iterator
我们能够使用Iterator和一个while循环进行集合的遍历
List<String> lst = new ArrayList<String>(); lst.add("alpha"); lst.add("beta"); lst.add("charlie"); // Retrieve the Iterator associated with this List via the iterator() method Iterator<String> iter = lst.iterator(); // Transverse thru this List via the Iterator while (iter.hasNext()) { // Retrieve each element and process String str = iter.next(); System.out.println(str); }
2.3 加强版的for循环 (JDK 1.5)
对于使用Iterator进行集合的遍历,在JDK 1.5中引入了新的加强版的for循环,使用它进行集合的遍历更加方便。
以下是主要的使用方法
for ( type item : aCollection ) { body ; }
能够在集合中改动对象吗?
加强版的for循环提供了一个方便的方法去遍历集合元素,可是它将Iterator给隐藏了,因此你不能移除或者替换这个元素。
循环变量接受的是一个引用的副本。因此这个加强版的for循环能够通过该引用改动相应的可变元素(比如:StringBuilder),可是不能改动不可变元素(比如:String和基本数据类型相应的对象类型)。
样例——使用加强版的for循环对于集合的可变元素(比如:StringBuilder)
import java.util.List; import java.util.ArrayList; public class ForEachMutableTest { public static void main(String[] args) { List<StringBuilder> lst = new ArrayList<StringBuilder>(); lst.add(new StringBuilder("alpha")); lst.add(new StringBuilder("beta")); lst.add(new StringBuilder("charlie")); System.out.println(lst); // [alpha, beta, charlie] for (StringBuilder sb : lst) { sb.append("88"); // can modify "mutable" objects } System.out.println(lst); // [alpha88, beta88, charlie88] } }
样例——使用加强版的for循环对于集合的不可变元素(比如:StringBuilder)
import java.util.List; import java.util.ArrayList; public class ForEachImmutableTest { public static void main(String[] args) { List<String> lst = new ArrayList<String>(); lst.add("alpha"); lst.add("beta"); lst.add("charlie"); System.out.println(lst); // [alpha, beta, charlie] for (String str : lst) { str += "change!"; // cannot modify "immutable" objects } System.out.println(lst); // [alpha, beta, charlie] } }
2.4 Collection<E>接口
Collection<E>包括一个泛型类型E,E是集合的元素数据类型,而且Collection<E>是集合框架的超类。它包括了其它类都须要实现的基本操作,比如:
// Basic Operations int size() // Returns the number of elements of this Collection void clear() // Removes all the elements of this Collection boolean isEmpty() // Returns true if there is no element in this Collection boolean add(E element) // Ensures that this Collection contains the given element boolean remove(Object element) // Removes the given element, if present boolean contains(Object element) // Returns true if this Collection contains the given element // Bulk Operations with another Collection boolean containsAll(Collection<?> c) // Collection of any "unknown" object boolean addAll(Collection<? extends E> c) // Collection of E or its sub-types boolean removeAll(Collection<?> c) boolean retainAll(Collection<?> c) // Comparison - Objects that are equal shall have the same hashCode boolean equals(Object o) int hashCode() // Array Operations Object[] toArray() // Convert to an Object array <T> T[] toArray(T[] a) // Convert to an array of the given type T
Collection<E>中仅仅能包括对象类型。不能包括基本数据类型(比如:int和double),所以假设须要将基本数据类型包装成相应的对象类型,JDK
1.5引入了自己主动拆箱和解箱操作来解决问题,这个在前面已经讲过。
2.5 List<E>, Set<E> & Queue<E> — Collection<E>的子接口
在实际开发过程中。我们一般使用Collection的子类接口——List<E>
, Set<E>
,
和 Queue<E>。它们提供了很多其它其它的特性。
List<E>
:它相当于一个可扩展的线性数组,能够进行索引訪问,而且能够包括反复的元素,我们常常会使用到的一些List的实现类为:ArrayList
,LinkedList
,Vector
和Stack。
Set<E>
:相当于一个数据集合,它不能包括反复的元素。常常使用到的Set的实现类为:HashSet和
LinkedHashSet,子接口为
SortedSet<E>
是一个有序的元素集合,TreeSet是SortedSet<E>
的实现类。
Queue<E>
: 相当于一个先进先出的队列,它的子接口Deque<E>相当于一个可在两端操作的双向队列,实现类为PriorityQueue
,ArrayDeque
和LinkedList。
HashMap
, Hashtable
和 LinkedHashMap。它的子接口 SortedMap<K,
V>
表示一个基于键值key有序的键值对集合,实现类为TreeMap。
List
, Set
,
和 Queue来取代Collection接口,这些子接口仅仅是对Collection接口操作的又一次定义。
List<E>相当于一个可扩展的线性数组,它支持索引訪问,它能够包括反复的元素,也能够包括null元素。
List<E>
父接口的基础上,List<E>
也声明了一些抽象方法。// Operations at a specified index position void add(int index, E element) // add E set(int index, E element) // replace E get(int index) // retrieve without remove E remove(int index) // remove last retrieved int indexOf(Object obj) int lastIndexOf(Object obj) // Operations on a range fromIndex (inclusive) toIndex (exclusive) List<E> subList(int fromIndex, int toIndex) ...... // Operations inherited from Collection<E> int size() boolean isEmpty() boolean add(E element) boolean remove(Object obj) boolean contains(Object obj) void clear(); ......
抽象类AbstractList提供了非常多List、Collector和Iterable接口中声明的抽象方法的实现。另外有些方法仍然是抽象的。这些方法会被子类实现,比如
ArrayList
和 Vector。
3.1
ArrayList<E> & Vector<E> —— List<E>的实现类
addElement()
, removeElement()
, setElement()
, elementAt()
, firstElement()
, lastElement()
, insertElementAt()
),毫无疑问,这些遗留的方法是能够使用的。而且保持向后兼容。
E push(E element) // pushes the specified element onto the top of the stack E pop() // removes and returns the element at the top of the stack E peek() // returns the element at the top of stack without removing boolean empty() // tests if this stack is empty int search(Object obj) // returns the distance of the specified object from the top // of stack (distance of 1 for TOS), or -1 if not found
3.3 LinkedList<E> - List<E>的实现类
LinkedList<E>
也实现了Queue<E>
和 Deque<E>,因此它能够充当FIFO
和 LIFO队列。
3.4
转换一个List为一个数组 - toArray()
在超类接口Collection中定义了一个toArray()方法,它能够使用该列表来创建一个数组。返回的数组能够进行自由的改动。
Object[] toArray() // Object[] version <T> T[] toArray(T[] a) // Generic type version
样例:
import java.util.List; import java.util.ArrayList; import java.util.Arrays; public class TestToArray { public static void main(String[] args) { List<String> lst = new ArrayList<String>(); lst.add("alpha"); lst.add("beta"); lst.add("charlie"); // Use the Object[] version Object[] strArray1 = lst.toArray(); System.out.println(Arrays.toString(strArray1)); // [alpha, beta, charlie] // Use the generic type verion - Need to specify the type in the argument String[] strArray2 = lst.toArray(new String[0]); strArray2[0] = "delta"; // modify the returned array System.out.println(Arrays.toString(strArray2)); // [delta, beta, charlie] System.out.println(lst); // [alpha, beta, charlie] - no change in the original list } }
3.5 将数组转换为一个List - Arrays.asList()
import java.util.List; import java.util.ArrayList; import java.util.Arrays; public class TestArrayAsList { public static void main(String[] args) { String[] strs = {"alpha", "beta", "charlie"}; System.out.println(Arrays.toString(strs)); // [alpha, beta, charlie] List<String> lst = Arrays.asList(strs); System.out.println(lst); // [alpha, beta, charlie] // Changes in array or list write thru strs[0] += "88"; lst.set(2, lst.get(2) + "99"); System.out.println(Arrays.toString(strs)); // [alpha88, beta, charlie99] System.out.println(lst); // [alpha88, beta, charlie99] // Initialize a list using an array List<Integer> lstInt = Arrays.asList(22, 44, 11, 33); System.out.println(lstInt); // [22, 44, 11, 33] } }
3.6 ArrayList, Vector, LinkedList 和 Stack的比較
使用Collections.sort()
和Arrays.sort()可进行排序
有些集合,比如
SortedSet
(TreeSet
) 和SortMap
(TreeMap
)是有序的
- 是对象实现java.lang.Comparable,重写compareTo()方法去指定两个对象的排序方法。
- 创建一个指定的java.util.Comparator对象。实现compare()方法去指定两个对象的比較。
int compareTo(T o) // Returns a negative integer, zero, or a positive integer // as this object is less than, equal to, or greater than the given object
一般推荐compareTo()方法与
equals()
和 hashCode()是一致的,即:
- 假设
compareTo()返回0。那么equals()应该返回true。
假设equals()返回true,那么hashCode()应该返回的是同样的int值。
Byte
, Short
, Integer
, Long
, Float
, Double
, Character
and Boolean
)都实现了Comparable接口的compareTo()
方法。java.util.Arrays
和 java.util.Collections相应不同的算法提供了非常多的静态方法,比如排序和查找。
在以下这个样例中,使用Arrays.sort()
和 Collections.sort()
方法去对一个String数组和一个Integer的List进行排序,使用的就是它们默认的Comparable实现。
import java.util.Arrays; import java.util.List; import java.util.ArrayList; import java.util.Collections; public class TestComparable { public static void main(String[] args) { // Sort and search an "array" of Strings String[] array = {"Hello", "hello", "Hi", "HI"}; // Use the Comparable defined in the String class Arrays.sort(array); System.out.println(Arrays.toString(array)); // [HI, Hello, Hi, hello] // Try binary search - the array must be sorted System.out.println(Arrays.binarySearch(array, "Hello")); // 1 System.out.println(Arrays.binarySearch(array, "HELLO")); // -1 (insertion at index 0) // Sort and search a "List" of Integers List<Integer> lst = new ArrayList<Integer>(); lst.add(22); // auto-box lst.add(11); lst.add(44); lst.add(33); Collections.sort(lst); // Use the Comparable of Integer class System.out.println(lst); // [11, 22, 33, 44] System.out.println(Collections.binarySearch(lst, 22)); // 1 System.out.println(Collections.binarySearch(lst, 35)); // -4 (insertion at index 3) } }
在Comparable之外,我们能够传递一个Comparator对象到排序方法中(
Collections.sort()
和 Arrays.sort()
)来实现大小比較,假设Comparator可用,那么Comparator将会重写Comparable。int compare(T o1, T o2) // Returns a negative integer, zero, or a positive integer as the // first argument is less than, equal to, or greater than the second.
compare()
方法去进行对象的比較。在Comparable中,仅仅须要调用对象的compareTo()方法。它仅仅须要传递一个參数,表示当前对象和还有一个对象比較,在Comparator<T>须要传递两个參数。表示两个对象的比較。import java.util.Arrays; import java.util.List; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; public class TestComparator { // Define a Comparator<String> to order strings in case-insensitive manner public static class StringComparator implements Comparator<String> { @Override public int compare(String s1, String s2) { return s1.compareToIgnoreCase(s2); } } // Define a Comparator<Integer> to order Integers based on the least significant digit public static class IntegerComparator implements Comparator<Integer> { @Override public int compare(Integer s1, Integer s2) { return s1%10 - s2%10; } } public static void main(String[] args) { // Use a customized Comparator for Strings Comparator<String> compStr = new StringComparator(); // Sort and search an "array" of Strings String[] array = {"Hello", "Hi", "HI", "hello"}; Arrays.sort(array, compStr); System.out.println(Arrays.toString(array)); // [Hello, hello, Hi, HI] System.out.println(Arrays.binarySearch(array, "Hello", compStr)); // 1 System.out.println(Arrays.binarySearch(array, "HELLO", compStr)); // 1 (case-insensitive) // Use a customized Comparator for Integers Comparator<Integer> compInt = new IntegerComparator(); // Sort and search a "List" of Integers List<Integer> lst = new ArrayList<Integer>(); lst.add(42); // auto-box lst.add(21); lst.add(34); lst.add(13); Collections.sort(lst, compInt); System.out.println(lst); // [21, 42, 13, 34] System.out.println(Collections.binarySearch(lst, 22, compInt)); // 1 System.out.println(Collections.binarySearch(lst, 35, compInt)); // -5 (insertion at index 4) } }