1.ArrayList
ArrayList类别实作了List接口,List接口是Collection接口的子接口,主要增加了根据索引取得对象的方法。
ArrayList使用数组实作List接口,所以对于快速的随机取得对象来说,使用ArrayList可以得到较好的效能,不过在移除对象或插入对象时,ArrayList就比较慢(使用 LinkedList 在这方面就好的多)。
来看看一个ArrayList的范例:
ArrayListDemo.java
package onlyfun.caterpillar; import java.util.*; public class ArrayListDemo { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); List list = new ArrayList(); System.out.println("输入名称(quit结束)"); while(true) { System.out.print("# "); String input = scanner.next(); if(input.equals("quit")) break; list.add(input); } System.out.print("显示输入: "); for(int i = 0; i < list.size(); i++) System.out.print(list.get(i) + " "); System.out.println(); } }
在 J2SE 5.0 之后新增了泛型(Generic)的功能,使用对象容器时建议容器中将储存的对象型态,如此您的对象在存入容器会被限定为您所宣告的型态,而取出时,也不至于失去原来的型态信息,可以避免型态转换时的问题。
使用add()方法可以将一个对象加入ArrayList中,使用size()方法可以传回目前的ArrayList的长度,使用get()可以传回指定索引处的对象,使用toArray()可以将ArrayList中的对象转换为对象数组。
以下是执行结果:
输入名称(quit结束) # Justin # caterpillar # momor # quit 显示输入: Justin caterpillar momor?
您可以使用get()方法指定索引值取出对象,然而如果您的目的是要循序取出容器中所有的对象,则您可以使用Iterator类,Iterator类实作 Iterator 模式,实际来看个例子:
ArrayListDemo.java
package onlyfun.caterpillar; import java.util.*; public class ArrayListDemo { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); List list = new ArrayList(); System.out.println("输入名称(quit结束)"); while(true) { System.out.print("# "); String input = scanner.next(); if(input.equals("quit")) break; list.add(input); } Iterator iterator = list.iterator(); while(iterator.hasNext()) { System.out.print(iterator.next() + " "); } System.out.println(); } }
iterator()方法会传回一个Iterator对象,这个对象提供的遍访的方法,hasNext()方法测试Iterator中是否还有对象,如果 有的话,可以使用next()取出。
事实上,在J2SE 5.0您也不必须使用iterator()了,使用增强的for循环可以直接遍访List的所有元素,例如:
ArrayListDemo.java
package onlyfun.caterpillar; import java.util.*; public class ArrayListDemo { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); List list = new ArrayList(); System.out.println("输入名称(quit结束)"); while(true) { System.out.print("# "); String input = scanner.next(); if(input.equals("quit")) break; list.add(input); } for(String s : list) { System.out.print(s + " "); } System.out.println(); } }
2.LinkedList
List类是以对象加入(add)容器的顺序来排列它们,如果您的对象加入之后大都是为了取出,而不会常作移除或插入(Insert)的动作,则使用ArrayList,如果您会经常从容器中作移除或插入对象的动作,则使用LinkedList会获得较好的效能。
LinkedList实作了List接口,并增加了一些移除与插入对象的特定方法,像是addFirst()、addLast()、 getFirst()、getLast()、removeFirst( )、removeLast()等等,由于在插入与移除时有较好的效能,适合拿来实作堆栈(Stack)与队列(Queue)。
以下实作一个简单的FILO(First-In, Last-Out)堆栈,可以存入字符串:
StringStack.java
package onlyfun.caterpillar; import java.util.*; public class StringStack { private LinkedList linkedList; public StringStack() { linkedList = new LinkedList(); } public void push(String name) { linkedList.addFirst(name); } public String top() { return linkedList.getFirst(); } public String pop() { return linkedList.removeFirst(); } public boolean isEmpty() { return linkedList.isEmpty(); } }
而对于FIFO(First-In, First-Out)的队列,我们也可以使用LinkedList来实作:
StringQueue.java
package onlyfun.caterpillar; import java.util.*; public class StringQueue { private LinkedList linkedList; public StringQueue() { linkedList = new LinkedList(); } public void put(String name) { linkedList.addFirst(name); } public String get() { return linkedList.removeLast(); } public boolean isEmpty() { return linkedList.isEmpty(); } }
事实上,如果您要使用队列的功能,您也不用亲自实作,在J2SE 5.0中,LinkedList也实作了新加入的java.util.Queue接口,这个接口有五个必须实作的方法:
element() |
取得但不移除队列第一个组件,队列为空时会丢出例外 |
offer() |
加入一个元素至队列中 |
peek() |
取得但不移除队列第一个组件 |
poll() |
取得并移去队列第一个组件,队列为空时传回null |
remove() |
取得并移除队列第一个组件 |
要使用队列的功能,您只要类似这样的宣告:
Queue<String> queue = new LinkedList<String>();
3.HashSet
HashSet实作Set接口,Set接口继承Collection接口,Set容器中的对象都是唯一的,加入 Set容器中的对象都必须重新定义equals()方法,作为唯一性的识别,Set容器有自己的一套排序规则。
HashSet的排序规则是利用Hash Table,所以加入HashSet容器的对象还必须重新定义hashCode()方法,利用Hash的方式,可以让您快速的找到容器中的对象,在比较两个加入Set容器中的对象是否相同时,会先比较hashCode()方法传回的值是否相同,如果相同,则再使用equals()方法比较,如果两者都相同,则视为相同的对象。
事实上,在撰写新的类别时,最好总是重新定义equals()与hashCode()方法,以符合Java的设计规范,您可以参考 Object 类别 中的介绍了解如何重新定义equals()与hashCode()。
来看一个例子:
HashSetDemo.java
package onlyfun.caterpillar; import java.util.*; public class HashSetDemo { public static void main(String[] args) { Set set = new HashSet(); set.add("caterpillar"); set.add("justin"); set.add("momor"); set.add("justin"); Iterator iterator = set.iterator(); while(iterator.hasNext()) { System.out.print(iterator.next() + " "); } System.out.println(); } }
执行结果:
momor justin caterpillar
如上所示的,即使重复加入了"justin"字符串,HashSet中仍只有一个"justin"字符串对象,另一个要注意的是,选代所有的值时,其顺序 与您加入的顺序是不一样的,选代所有值时的顺序是HashSet排序过后的顺序。
LinkedHashSet是HashSet的子类,它在内部实作使用Hash Code进行排序,然而允许您在列举时行为像是LinkedList,简单的改写上面的程序即可了解:
LinkedHashSetDemo.java
package onlyfun.caterpillar; import java.util.*; public class LinkedHashSetDemo { public static void main(String[] args) { Set set = new LinkedHashSet(); set.add("caterpillar"); set.add("justin"); set.add("momor"); set.add("justin"); Iterator iterator = set.iterator(); while(iterator.hasNext()) { System.out.print(iterator.next() + " "); } System.out.println(); } }
执行结果:
caterpillar justin momor
可以在执行结果中看到的,选代时的顺序正是您加入值的顺序。
4.TreeSet
TreeSet实作Set接口与SortedSet接口,提供相关的方法让您有序的取出对应位置的对象,像是 first()、last()等方法,TreeSet是J2SE中唯一实作SortedSet接口的类别,它使用红黑树结构来对加入的对象进行排序。
看个简单的例子:
TreeSetDemo.java
package onlyfun.caterpillar; import java.util.*; public class TreeSetDemo { public static void main(String[] args) { Set set = new TreeSet(); set.add("justin"); set.add("caterpillar"); set.add("momor"); set.add("justin"); Iterator iterator = set.iterator(); while(iterator.hasNext()) { System.out.print(iterator.next() + " "); } System.out.println(); } }
由于加入的是String对象,执行结果会自动依字典顺序进行排序的动作:
caterpillar justin momor
依字典顺序排序String对象是TreeSet预设的,如果您对对象有自己的一套排序顺序,您要实作一个 Comparator 对象,您要实作compare()方法,它必须传回整数值,如果对象顺序相同则传回0,传回正整数表示compare()方法的第一个对象大于第二个对象,反之则传回负整数。
举个实际的例子,假设您想要改变TreeSet依字典顺序排列加入的对象为相反的顺序:
CustomComparator.java
package onlyfun.caterpillar; import java.util.Comparator; public class CustomComparator implements Comparator { public int compare(T o1, T o2) { if (((T) o1).equals(o2)) return 0; return ((Comparable) o1).compareTo((T) o2) * -1; } }
在自订的Comparator中,如果两个对象的顺序相同会传回0,这在TreeSet中表示两个对象是同一个对象,TreeSet要求传入的对象必须实 作java.lang.Comparable接口,范例中只是简单的将原来compareTo()传回的值乘以负一,如此在TreeSet中就可以简单的 让排列顺序相反。
在建构TreeSet实例时一并指定自订的Comparator,例如:
TreeSetDemo2.java
package onlyfun.caterpillar; import java.util.*; public class TreeSetDemo2 { public static void main(String[] args) { // 自订Comparator Comparator comparator = new CustomComparator(); Set set = new TreeSet(comparator); set.add("justin"); set.add("caterpillar"); set.add("momor"); // 使用 enhanced for loop 显示对象 for(String name : set) { System.out.print(name + " "); } System.out.println(); } }
执行的结果是相反的:
momor justin caterpillar
5.EnumSet
EnumSet的名称说明了其作用,它是在J2SE 5.0后加入的新类别,可以协助您建立列举值的集合,它提供了一系列的静态方法,可以让您指定不同的集合建立方式,例如:
EnumSetDemo.java
package onlyfun.caterpillar; import java.util.*; enum FontConstant { Plain, Bold, Italic } public class EnumSetDemo { public static void main(String[] args) { EnumSet enumSet = EnumSet.of(FontConstant.Plain, FontConstant.Bold); showEnumSet(enumSet); showEnumSet(EnumSet.complementOf(enumSet)); } public static void showEnumSet( EnumSet enumSet) { Iterator iterator = enumSet.iterator(); while(iterator.hasNext()) { System.out.print(iterator.next() + " "); } System.out.println(); } }
您可以指定列举值来加入EnumSet中,of()方法会返回一个EnumSet的实例,当中包括您所指定的列举值,您也可以使complementOf()指定一个EnumSet的互补集,以下是执行的结果:
Plain Bold Italic?
EnumSet实作了Set接口,所以您可以使用Set接口的所有方法来测试它所包括的列举值,例如测试一个集合中是否包括 FontConstant.Bold:
if(enumSet.contains(FontConstant.Bold)) { .... }
您也可以建立一个空的EnumSet,然后自己逐个加入列举值,例如:
EnumSetDemo.java
package onlyfun.caterpillar; import java.util.*; enum FontConstant { Plain, Bold, Italic } public class EnumSetDemo { public static void main(String[] args) { EnumSet enumSet = EnumSet.noneOf(FontConstant.class); enumSet.add(FontConstant.Bold); enumSet.add(FontConstant.Italic); showEnumSet(enumSet); } public static void showEnumSet( EnumSet enumSet) { Iterator iterator = enumSet.iterator(); while(iterator.hasNext()) { System.out.print(iterator.next() + " "); } System.out.println(); } }
执行结果:
Bold Italic
您也可以由一个容器对象中建立EnumSet:
List list = new ArrayList(); list.add(FontConstant.Bold); list.add(FontConstant.Italic); showEnumSet(EnumSet.copyOf(list));
更多EnumSet相关的方法,您可以参考 EnumSet 在线API文件。
6.HashMap
HashMap实作Map接口,内部实作使用Hash Table,让您在常数时间内可以寻得key/value对。
所谓的key/value对,简单的说,您将Map容器对象当作一个有很多间房间的房子,每个房间的门有一把钥匙,您将对象储存至房间中时,要顺便拥有一把钥匙,下次要取回对象时,就是根据这把钥匙取得。
以一个简单的例子来作说明:
HashMapDemo.java
package onlyfun.caterpillar; import java.util.*; public class HashMapDemo { public static void main(String[] args) { Map< String, String> map = new HashMap< String, String>(); map.put("caterpillar", "caterpillar's message!!"); map.put("justin", "justin's message!!"); System.out.println(map.get("justin")); System.out.println(map.get("caterpillar")); } }
在宣告Map型态时,您指定了key/value各自的型态,这边都是宣告为String,也就是以String对象作为key对象的型态,而 value也是以String对象作为其型态。
使用Map的put()方法将对象存入,必须同时指定key/value,而要取回对象时,则指定key,程序的执行结果如下:
justin's message!! caterpillar's message!!
HashMap是个被经常使用的对象,您可以参考下面几个例子中HashMap的应用:
- Command 模式
- Thread-Specific Storage 模式
- 控 制器(Servlet)
可以使用values()方法返回一个Collection对象,如果您需要一次选代Map中所有的对象,这会很有用,例如:
HashMapDemo.java
package onlyfun.caterpillar; import java.util.*; public class HashMapDemo { public static void main(String[] args) { Map< String, String> map = new HashMap< String, String>(); map.put("justin", "justin's message!!"); map.put("momor", "momor's message!!"); map.put("caterpillar", "caterpillar's message!!"); Collection collection = map.values(); Iterator iterator = collection.iterator(); while(iterator.hasNext()) { System.out.println(iterator.next()); } } }
执行结果:
momor's message!! justin's message!! caterpillar's message!!
HashMap使用Hash Table,因而它有自己的排序方式,如果您想要在选代所有的对象时,依照插入的顺序来排序,则可以使用LinkedHashMap,它是HashMap 的子类,使用values()所返回的Collection对象,其内含对象之顺序即为当初您加入对象之顺序,例如:
LinkedHashMapDemo.java
package onlyfun.caterpillar; import java.util.*; public class LinkedHashMapDemo { public static void main(String[] args) { Map< String, String> map = new LinkedHashMap< String, String>(); map.put("justin", "justin's message!!"); map.put("momor", "momor's message!!"); map.put("caterpillar", "caterpillar's message!!"); Collection collection = map.values(); Iterator iterator = collection.iterator(); while(iterator.hasNext()) { System.out.println(iterator.next()); } } }
执行结果:
justin's message!! momor's message!! caterpillar's message!!
7.TreeMap
TreeMap实作Map接口与SortedMap接口,提供相关的方法让您有序的取出对应位置的对象,像是 firstKey()、lastKey()等方法,TreeMap是J2SE中唯一实作SortedMap接口的类别,它使用红黑树结构来对加入的对象进 行排序。
看个简单的例子:
TreeMapDemo.java
package onlyfun.caterpillar; import java.util.*; public class TreeMapDemo { public static void main(String[] args) { Map< String, String> map = new TreeMap< String, String>(); map.put("justin", "justin's message!!"); map.put("momor", "momor's message!!"); map.put("caterpillar", "caterpillar's message!!"); Collection collection = map.values(); Iterator iterator = collection.iterator(); while(iterator.hasNext()) { System.out.println(iterator.next()); } } }
由于您加入的是String对象,执行结果会自动依key的字典顺序进行排序的动作:
caterpillar's message!! justin's message!! momor's message!!
依字典顺序排序String对象是TreeMap预设的,如果您对对象有自己的一套排序顺序,您要实作一个 Comparator 对象,它有两个必须实作的方法,compare()与equals(),前者必须传回整数值,如果对象顺序相同则传回0,传回正整数表示compare ()方法的第一个对象大于第二个对象,反之则传回负整数,后者则是定义两个对象是否相等。
8、EnumMap
EnumMap是个专为列举型别设计的类别,方便您使用列举型别及Map对象,直接来举个实例:
EnumMapDemo.java
package onlyfun.caterpillar; import java.util.*; enum Action {TURN_LEFT, TURN_RIGHT, SHOOT} public class EnumMapDemo { public static void main(String[] args) { Map< Action, String> map = new EnumMap< Action, String>(Action.class); map.put(Action.TURN_LEFT, "向左转"); map.put(Action.TURN_RIGHT, "向右转"); map.put(Action.SHOOT, "射击"); for(Action action : Action.values( ) ) { System.out.println(map.get(action)); } } }
执行结果:
向左转 向右转 射击
与单纯的使用HashMap比较起来的差别是,在上面的程序中,EnumMap将根据列举的顺序来维护对象的排列顺序,从下面这个程序可以看个大概:
EnumMapDemo2.java
package onlyfun.caterpillar; import java.util.*; enum Action {TURN_LEFT, TURN_RIGHT, SHOOT} public class EnumMapDemo2 { public static void main(String[] args) { Map< Action, String> map = new EnumMap< Action, String>(Action.class); map.put(Action.SHOOT, "射击");? map.put(Action.TURN_RIGHT, "向右转"); map.put(Action.TURN_LEFT, "向左转"); for(String value : map.values( )) { System.out.println(value); } } }
执行结果:
向左转 向右转 射击
从遍访的结果可以看出,对象的顺序是根据列举顺序来排列的。