Step By Step(Java 集合篇)
1.
Java集合类库中最重要的两个接口Collection<E>和Map<K,V>,其中Collection接口又再次划分为
List和Set两大子接口,List中可以包含重复的元素,Set中则不可以。以下列举出一些常用的集合实现类,他们均分别继承自这两个接口:
1) ArrayList: 一种可以动态增长和缩减的索引序列(动态数组,类似于C++中的vector);
2) LinkedList: 一种可以在任何位置进行高效的插入和删除操作的有序序列(类似于C++中list);
3) ArrayDeque: 一种用循环数组实现的双端队列(类似于C++中的deque);
4) HastSet:一种没有重复元素的无序集合(C++的标准库中并未提供hashset集合,但是Windows的VC和Linux平台下的gcc均各自提供了hashset容器);
5) TreeSet: 一种有序集(类似于C++中的set);
6) EnumSet: 一种包含枚举类型值的集;
7) LinkedHashSet: 一种可以记住元素插入次序的集,在其内部由LinkedList负责维护插入的次序,HashSet来维护Hash;
8) HashMap:一种存储键值对关联的数据结构(C++的标准库中并未提供hashmap集合,但是Windows的VC和Linux平台下的gcc均各自提供了hashmap容器);
9) TreeMap:一种键值有序排列的映射表(类似于C++中的map);
10) EnumMap:一种键值属于枚举类型的映射表;
11) LinkedHashMap:一种可以记住键值项插入次序的映射表;
2.
ArrayList:该集合的底层是通过动态数组来实现的,集合构造的时候可以指定一个初始容量,当插入的元素过多导致已有的容量不能容纳新元素是,其底
层数组的容量将自动增长原有容量的1.5
倍,这样会带来一定的空间浪费,但是为了避免经常扩张而带来的性能开销,只能是用空间换取时间了。如果在容器的中间添加或者删除一个元素都将会导致后面的
元素向后或向前移动一个位置,如果元素数量较多且该操作比较频繁,将会导致系统的性能降低,然而对于容器中元素的随机访问性能较好,以下为
ArrayList的常用示例代码:
1 public static void showIterator() {
2 ArrayList<String> list = new ArrayList<String>();
3 list.add("Monday");
4 list.add("Tuesdag");
5 list.add("Wednesday");
6 Iterator<String> iterator = null;
7 iterator = list.iterator();
8 //while
9 while (iterator.hasNext()) {
10 String element = iterator.next();
11 System.out.println(element);
12 }
13 //for
14 for (iterator = list.iterator(); iterator.hasNext();) {
15 String element = iterator.next();
16 System.out.println(element);
17 }
18 //for each
19 for (String element : list) {
20 System.out.println(element);
21 }
22 }
23
24 public static void showSetAndGet() {
25 ArrayList<String> nums = new ArrayList<String>();
26 nums.clear();
27 nums.add("One");
28 nums.add("Two");
29 nums.add("Three");
30 System.out.println(nums);
31 nums.set(0, "Uno");
32 nums.set(1, "Dos");
33 nums.set(2, "Tres");
34 for (int i = 0; i < nums.size(); ++i)
35 System.out.println(nums.get(i));
36 }
37
38 public static void showRemoveAndSize() {
39 ArrayList<String> al = new ArrayList<String>();
40 System.out.println("Initial size of al: " + al.size());
41 al.add("C");
42 al.add("A");
43 al.add("E");
44 al.add("B");
45 al.add(1, "A2");
46 System.out.println("Size of al after additions: " + al.size());
47 System.out.println("Contents of al: " + al);
48 al.remove("F");
49 al.remove(2);
50 System.out.println("Size of al after deletions: " + al.size());
51 System.out.println("Contents of al: " + al);
52 Iterator<String> it = al.iterator();
53 //Notes:remove() must be called after next()
54 it.next();
55 it.remove();
56 System.out.println("Size of al after deletions: " + al.size());
57 System.out.println("Contents of al: " + al);
58 }
59
60 public static void showSubListAndCopyToArray() {
61 ArrayList<String> arrayList = new ArrayList<String>();
62 arrayList.add("1");
63 arrayList.add("2");
64 arrayList.add("3");
65 arrayList.add("4");
66 arrayList.add("5");
67 List<String> lst = arrayList.subList(1, 3);
68 for (int i = 0; i < lst.size(); i++)
69 System.out.println(lst.get(i));
70 // remove one element from sub list
71 String obj = lst.remove(0);
72 System.out.println(obj + " is removed");
73 for (String str: arrayList)
74 System.out.println(str);
75 //get object array with normal method
76 Object[] objArray = arrayList.toArray();
77 for (Object obj1 : objArray)
78 System.out.println(obj1);
79 //get object array with generic method
80 String[] strArray = arrayList.toArray(new String[0]);
81 for (String str : strArray)
82 System.out.println(str);
83 }
84
85 public static void showListIterator() {
86 ArrayList<String> aList = new ArrayList<String>();
87 aList.add("1");
88 aList.add("2");
89 aList.add("3");
90 aList.add("4");
91 aList.add("5");
92
93 ListIterator<String> listIterator = aList.listIterator();
94 while (listIterator.hasNext()) {
95 System.out.println(listIterator.next());
96 System.out.println("Previous: " + listIterator.previousIndex());
97 System.out.println("Next: " + listIterator.nextIndex());
98 }
99 while (listIterator.hasPrevious()) {
100 System.out.println(listIterator.previous());
101 System.out.println("Previous: " + listIterator.previousIndex());
102 System.out.println("Next: " + listIterator.nextIndex());
103 }
104 listIterator = aList.listIterator(2);
105 listIterator.next();
106 listIterator.set("100");
107 listIterator.next();
108 listIterator.remove();
109 for (String str : aList)
110 System.out.println(str);
111
112 if (aList.contains("4"))
113 System.out.println("True");
114 else
115 System.out.println("False");
116 }
117
118 public static void showFillAndReplace() {
119 ArrayList<String> arrayList = new ArrayList<String>();
120 arrayList.add("A");
121 arrayList.add("B");
122 arrayList.add("A");
123 arrayList.add("C");
124 arrayList.add("D");
125 Collections.replaceAll(arrayList, "A", "Replace All");
126 System.out.println(arrayList);
127 Collections.fill(arrayList, "REPLACED");
128 System.out.println(arrayList);
129 }
130
131 public static void showCollectionOperation() {
132 List<String> colours = new ArrayList<String>();
133 colours.add("red");
134 colours.add("green");
135 colours.add("blue");
136
137 System.out.println(colours);
138 Collections.swap(colours, 0, 2);
139 System.out.println(colours);
140
141 Collections.reverse(colours);
142 System.out.println(colours);
143
144 Collections.sort(colours);
145 System.out.println(Arrays.toString(colours.toArray()));
146 Collections.sort(colours, Collections.reverseOrder());
147 System.out.println(Arrays.toString(colours.toArray()));
148
149 int index = Collections.binarySearch(colours, "green");
150 System.out.println("Element found at : " + index);
151 ArrayList<Integer> arrayList = new ArrayList<Integer>();
152 arrayList.add(new Integer("3"));
153 arrayList.add(new Integer("1"));
154 arrayList.add(new Integer("8"));
155 arrayList.add(new Integer("3"));
156 arrayList.add(new Integer("5"));
157 System.out.println(Collections.min(arrayList));
158 System.out.println(Collections.max(arrayList));
159 }
160
161 public static void showMinMax() {
162 ArrayList<Integer> arrayList = new ArrayList<Integer>();
163 arrayList.add(new Integer("3"));
164 arrayList.add(new Integer("1"));
165 arrayList.add(new Integer("8"));
166 arrayList.add(new Integer("3"));
167 arrayList.add(new Integer("5"));
168 System.out.println(Collections.min(arrayList));
169 System.out.println(Collections.max(arrayList));
170 }
171
172 public static void showSynchronizedList() {
173 ArrayList arrayList = new ArrayList();
174 List list = Collections.synchronizedList(arrayList);
175 //list之后的并发操作将不再需要synchronized关键字来进行同步了。
176 }
3. LinkedList: 该集合是通过双向链表来实现的,对于指定位置元素的遍历其内部做了一个小的优化,如果position大于len/2,由于内部的数据结构是双向链表,因 此可以从后向前遍历,以减少遍历过程中节点跳转的次数。ArrayList中的大部分代码示例都适用于LinkedList,以下近给出 LinkedList特有的常用示例代码:
1 public static void showNewFeatures(String[] args) {
2 LinkedList<String> lList = new LinkedList<String>();
3 lList.add("1");
4 lList.add("2");
5 lList.add("3");
6 lList.add("4");
7 lList.add("5");
8
9 System.out.println("First element of LinkedList is : " + lList.getFirst());
10 System.out.println("Last element of LinkedList is : " + lList.getLast());
11 System.out.println(lList);
12
13 String str = lList.removeFirst();
14 System.out.println(str + " has been removed");
15 System.out.println(lList);
16 str = lList.removeLast();
17 System.out.println(str + " has been removed");
18 System.out.println(lList);
19
20 lList.addFirst("1");
21 System.out.println(lList);
22 lList.addLast("5");
23 System.out.println(lList);
24 }
4. ArrayList和LinkedList的相同点:
1) 都是接口List<E>的实现类;
2) 都可以通过iterator()方法返回Iterator<E>迭代器对象,并可以通过该对象遍历容器中的元素;
3)
如果多个Iterator<E>实例同时引用同一个集合对象,那么当有一个正在遍历,而另外一个修改(add/remove)了集合对象中的
元素,对于第一个迭代器再进行迭代时将会引发ConcurrentModificationException异常的发生。
5. ArrayList和LinkedList的不同点:
1)
对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加一
项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象。
2) 在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。
3) LinkedList不支持高效的随机元素访问。
4) ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间
在C++的标准库中,ArrayList和LinkedList之间的使用差异以及各自的优缺点也同样反映于vector和list。
6. HashSet:
散列表为每个对象计算出一个整数(通过Object缺省的或类重载的hashCode域方法),称为散列码。在Java中,散列表是使用链表的数组来实现
的,每个链表被称为一个哈希桶。如果想在哈希表中查找一个对象,则需要先计算对象的hashCode,之后对表内的桶数取余数,得到的结果就是哈希桶的索
引,此时如果该桶为空桶,就可以直接将该对象插入到该桶中,如果已经有对象存在了,则需要逐个进行比较,一旦发现该与该对象相等的对象(equals()
返回true),就会放弃本次插入操作,否则将该对象插入到该桶的末端。HashSet在构造的时候提供两个参数,一个是initialCapacity
指定了桶的数量(实际的桶数为2的initialCapacity次幂),另一个是loadFactortian(0.0--1.0,推荐值为
0.75),当桶的填充百分比达到该值后,哈希表内的哈希桶数将double,重新填充已有对象,该操作被称为rehash,rehash是十分影响效率
的操作,因为为了尽可能避免该事件的发生,因可能在构造HashSet的时候给出一个合理的参数。以下为HashSet的常用示例代码:
1 public static void showAddAndIterator() {
2 HashSet<String> hs = new HashSet<String>();
3 hs.add("B");
4 hs.add("A");
5 hs.add("D");
6 hs.add("E");
7 hs.add("C");
8 hs.add("F");
9 System.out.println(hs);
10 Iterator<String> it = hs.iterator();
11 while (it.hasNext())
12 System.out.println(it.next());
13 }
14 public static void showRemoveAndClearAndContains() {
15 HashSet<Integer> hSet = new HashSet<Integer>();
16 hSet.add(new Integer("1"));
17 hSet.add(new Integer("2"));
18 hSet.add(new Integer("3"));
19 System.out.println(hSet.contains(new Integer("3")));
20 System.out.println(hSet);
21 boolean blnRemoved = hSet.remove(new Integer("2"));
22 System.out.println(hSet.contains(new Integer("2")));
23 System.out.println(blnRemoved);
24 System.out.println(hSet);
25 hSet.clear();
26 System.out.println(hSet);
27 System.out.println(hSet.isEmpty());
28 }
29
30 public static void showToArrayListAndArray() {
31 HashSet<String> hs = new HashSet<String>();
32 hs.add("B");
33 hs.add("A");
34 hs.add("D");
35 System.out.println(hs);
36 List<String> list = new ArrayList<String>(hs);
37 Object[] objects = list.toArray();
38 for (Object obj : objects)
39 System.out.println("Object = " + obj);
40 String[] strs = list.toArray(new String[0]);
41 for (String str : strs)
42 System.out.println("String = " + str);
43 }
44
45 public static void showToHashSetAndRemoveDuplicate() {
46 Integer[] numbers = { 7, 7, 8, 9, 10, 8, 8, 9, 6, 5, 4 };
47 List<Integer> list = Arrays.asList(numbers);
48 Set<Integer> set = new HashSet<Integer>(list);
49 for (Integer o : set)
50 System.out.print(o + ", ");
51 }
52
53 public static void showMinMax() {
54 HashSet<Long> hashSet = new HashSet<Long>();
55 hashSet.add(new Long("9"));
56 hashSet.add(new Long("4"));
57 hashSet.add(new Long("2"));
58 hashSet.add(new Long("2"));
59 hashSet.add(new Long("3"));
60 Object objMin = Collections.min(hashSet);
61 System.out.println("Minimum Element of Java HashSet is : " + objMin);
62 Object objMax = Collections.max(hashSet);
63 System.out.println("Maximum Element of Java HashSet is : "+ objMax);
64 }
65
66 public static void showSynchronizedSet() {
67 HashSet hashSet = new HashSet();
68 Set set = Collections.synchronizedSet(hashSet);
69 //set之后的并发操作将不再需要synchronized关键字来进行同步了。
70 }
7. TreeSet:该集合为有序集合,数据在插入集合后便按照自定义的排序规则将插入的对象进行排序,该集合主要是通过两种方式排序插入对象的,一种是要求 集合元素类必须是Comparable<T>的实现类,这样在插入对象的时候,集合可以根据compareTo方法的返回值来确定新插入对象 的位置,另外一种方式是通过在构造该集合对象的时候,传入Comparator<T>的实现类作为参数,之后所有插入的对象都是通过 Comparator<T>的compare方法的返回值来决定新对象的位置。该集合是通过RBTree(红黑树)来实现排序的,这一点和 C++标准库中的set和map是一致的。由于对象插入集合之后是有序的,因此该集合的插入效率要低于HashSet的插入效率。TreeSet和 HashSet相比主要的差异来自于对象的排序规则,以上HashSet的示例代码均适用于TreeSet,下面只是列出对象比较的代码:
1 public class Item implements Comparable<Item> {
2 public int compareTo(Item other) {
3 return partNumber - other.partNumber;
4 }
5 }
6 public static void main(String[] args) {
7 SortedSet<Item> parts = new TreeSet<Item>();
8 parts.add(new Item("Toaster",1234));
9 parts.add(new Item("Widget",4562));
10 parts.add(new Item("Modem",9912));
11 System.out.println(parts);
12
13 SortedSet<Item> sortByDescription = new TreeSet<Item>(new Comparator<Item>() {
14 public int compare(Item a,Item b) {
15 String descA = a.getDescription();
16 String descB = b.getDescription();
17 return descA.compareTo(descB);
18 }
19 });
20 sortByDescription.addAll(parts);
21 System.out.println(sortByDescription);
22 }
8. PriorityQueue(优先级对象): 该容器也是有序集合,和TreeSet不同的是,该集合只是保证当从集合的头部取出数据的时候,总是取出队列中最小(优先级最高)的元素。该集合内部是通 过"堆"的数据结构实现的,该结构只有第一个元素(头部元素)保证为该集合中最大的或最小的元素,其余元素没有固定的顺序,但是当集合有新的对象从尾部插 入或是从头部取出时,集合内部的相关元素会进行比较的比较最终再次决定出谁是最大或最小的元素作为头部元素。在JDK提供的classes中Timer是 通过该数据结构实现的,从而保证了Timer每次获取任务时都是最应该被调度的TimerTask,见如下代码:
1 public class TestMain {
2 public static void main(String[] args) {
3 PriorityQueue<String> pq = new PriorityQueue<String>();
4 pq.add("1");
5 pq.add("6");
6 pq.add("4");
7 pq.offer("5");
8 pq.offer("3");
9 pq.offer("2");
10 pq.offer("7");
11 //以下输出将以无序的结果输出
12 System.out.println(pq);
13 //以下输出将以有序的结果输出
14 while (pq.peek() != null) {
15 String str = pq.poll();
16 System.out.println(str);
17 }
18 int initCapacity = 20;
19 PriorityQueue<TestComparator> pq1 = new PriorityQueue<TestComparator>(initCapacity,
20 new Comparator<TestComparator>() {
21 public int compare(TestComparator t1, TestComparator t2) {
22 return t1.getID() - t2.getID();
23 }
24 });
25 pq1.offer(new TestComparator(1));
26 pq1.offer(new TestComparator(6));
27 pq1.offer(new TestComparator(4));
28 pq1.offer(new TestComparator(5));
29 pq1.offer(new TestComparator(3));
30 pq1.offer(new TestComparator(2));
31 pq1.offer(new TestComparator(7));
32 System.out.println("The following is for TestComparator.");
33 System.out.println(pq1);
34 while (pq1.peek() != null) {
35 int id = pq1.poll().getID();
36 System.out.println(id);
37 }
38 }
39 }
40
41 class TestComparator {
42 public TestComparator(int id) {
43 _id = id;
44 }
45 public int getID() {
46 return _id;
47 }
48 public String toString() {
49 return Integer.toString(_id);
50 }
51 private int _id;
52 }
9. HashMap:其内部数据结构的逻辑类似于HashSet,只是Map存储的都是key/value的键值映射关系,key相当于HashSet中的对 象,value只是附着于key的对象,使用者一般都是通过key的值去HashMap中搜索,找到并返回该key以及相关联的value,其内部实现机 制可参考HashSet。在接口方面主要的不同点是Map对象可以生成3个集合的视图(仅仅是视图,没有对象copy,视图的底层容器仍然来自于原映射对 象集合,如果在其中任意视图或原集合删除和添加数据都会及时反应与其他视图和映射集合)。该规则同样适用于TreeMap。见以下常用代码示例:
1 public static void showGetAndPut() {
2 HashMap<String, Double> hm = new HashMap<String, Double>();
3 hm.put("A", new Double(3.34));
4 hm.put("B", new Double(1.22));
5 hm.put("C", new Double(1.00));
6 hm.put("D", new Double(9.22));
7 hm.put("E", new Double(-19.08));
8 //随机的获取HashMap中的数据
9 Set<Map.Entry<String, Double>> set = hm.entrySet();
10 for (Map.Entry<String, Double> me : set) {
11 System.out.print(me.getKey() + ": ");
12 System.out.println(me.getValue());
13 }
14 double balance = hm.get("A");
15 //输出1003.34
16 hm.put("A", balance + 1000);
17 System.out.println(hm.get("A"));
18 }
19
20 public static void showContainsAndRemove() {
21 HashMap<String, String> hashMap = new HashMap<String, String>();
22 hashMap.put("1", "One");
23 hashMap.put("2", "Two");
24 hashMap.put("3", "Three");
25 System.out.println(hashMap.containsValue("Three"));
26 String str = hashMap.remove("2");
27 System.out.println("old value of 2 = " + str);
28 System.out.println(hashMap.containsValue("Two"));
29 Set<String> st = hashMap.keySet();
30 st.remove("1");
31 System.out.println("The size is equal to " + hashMap.size());
32 System.out.println(hashMap.containsKey("3"));
33 }
34 /* 结果如下
35 true
36 old value of 2 = Two
37 false
38 The size is equal to 1
39 true */
40
41 public static void showIterator() {
42 String names[] = { "Mercury", "Venus", "Earth", "Mars", "Jupiter"
43 , "Saturn", "Uranus", "Neptune", "Pluto" };
44 float diameters[] = { 4800f, 12103.6f, 12756.3f, 6794f, 142984f
45 , 120536f, 51118f, 49532f, 2274f };
46 Map<String,Float> map = new HashMap<String,Float>();
47 for (int i = 0, n = names.length; i < n; i++)
48 map.put(names[i], new Float(diameters[i]));
49 Iterator<String> it = map.keySet().iterator();
50 while (it.hasNext()) {
51 String str = it.next();
52 System.out.println(str + ": " + map.get(str));
53 }
54 }
55
56 public static void showSynchronizedMap() {
57 TreeMap<String,String> treeMap = new TreeMap<String,String>();
58 Map<String,String> map = Collections.synchronizedMap(treeMap);
59 //Map之后的并发操作将不再需要synchronized关键字来进行同步了。
60 }
10. TreeMap:TreeMap和TreeSet之间的相似性和主要差异就像HashMap之于HashSet,见以下常用代码示例:
1 public static void showSubMapAndHeadMapAndTailMap() {
2 TreeMap<String, String> sortedMap = new TreeMap<String, String>();
3 sortedMap.put("Adobe", "Mountain View, CA");
4 sortedMap.put("IBM", "White Plains, NY");
5 sortedMap.put("Learning Tree", "Los Angeles, CA");
6 sortedMap.put("Microsoft", "Redmond, WA");
7 sortedMap.put("Netscape", "Mountain View, CA");
8 sortedMap.put("O'Reilly", "Sebastopol, CA");
9 sortedMap.put("Sun", "Mountain View, CA");
10 System.out.println(sortedMap);
11 //firstKey and lastKey 是SortedMap中提供的方法,HashMap中没有。
12 String low = sortedMap.firstKey(), high = sortedMap.lastKey();
13 System.out.println(low);
14 System.out.println(high);
15 Iterator<String> it = sortedMap.keySet().iterator();
16 int i = 0;
17 while (it.hasNext()) {
18 if (i == 3)
19 low = it.next();
20 if (i == 6)
21 high = it.next();
22 else
23 it.next();
24 i++;
25 }
26 System.out.println(low);
27 System.out.println(high);
28 //以下3个方法也是SortedMap中提供的方法,HashMap中没有。
29 System.out.println(sortedMap.subMap(low, high));
30 System.out.println(sortedMap.headMap(high));
31 System.out.println(sortedMap.tailMap(low));
32 }
11. LinkedHashSet和LinkedHashMap:这两个集合与HashSet和HashMap唯一的差异是LinkedHashSet和LinkedHashMap通过内部实现的双向链表保留了集合元素的插入顺序,见如下代码:
1 public static void main(String[] a) {
2 Map<String, String> map = new LinkedHashMap<String, String>();
3 map.put("1", "value1");
4 map.put("2", "value2");
5 map.put("3", "value3");
6 map.put("2", "value4");
7 for (Iterator<String> it = map.keySet().iterator(); it.hasNext();) {
8 String key = it.next();
9 String value = map.get(key);
10 System.out.println(value);
11 }
12 }
13 /* 结果如下:
14 value1
15 value4
16 value3 */
17
18 //基于Values的排序后输出Keys
19 public static void main(String[] a) {
20 Map<String, String> yourMap = new HashMap<String, String>();
21 yourMap.put("1", "one");
22 yourMap.put("2", "two");
23 yourMap.put("3", "three");
24
25 Map<String, String> map = new LinkedHashMap<String, String>();
26 List<String> keyList = new ArrayList<String>(yourMap.keySet());
27 List<String> valueList = new ArrayList<String>(yourMap.values());
28 Set<String> sortedSet = new TreeSet<String>(valueList);
29 String[] sortedArray = sortedSet.toArray(new String[0]);
30 int size = sortedArray.length;
31
32 for (int i = 0; i < size; i++)
33 map.put(keyList.get(valueList.indexOf(sortedArray[i])), sortedArray[i]);
34
35 Set<String> ref = map.keySet();
36 Iterator<String> it = ref.iterator();
37 while (it.hasNext()) {
38 String i = (String) it.next();
39 System.out.println(i);
40 }
41 }
12. EnumSet和EnumMap,这两个集合可以看做是类型参数特化后的Set<E extends Enum<E>>和Map<E extends Enum<E>,V>,EnumSet没有显示的构造函数,而是通过一组工厂方法来创建了,见如下代码:
1 enum Weekdays {
2 Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
3 }
4 public static void main(String[] a) {
5 EnumSet<Weekdays> es1 = EnumSet.allOf(Weekdays.class);
6 EnumSet<Weekdays> es2 = EnumSet.noneOf(Weekdays.class);
7 EnumSet<Weekdays> es3 = EnumSet.range(Weekdays.Thursday, Weekdays.Sunday);
8 EnumSet<Weekdays> es4 = EnumSet.of(Weekdays.Monday,Weekdays.Saturday);
9 System.out.println("es1 = " + es1);
10 System.out.println("es2 = " + es2);
11 System.out.println("es3 = " + es3);
12 System.out.println("es4 = " + es4);
13 }
14 /* 结果如下:
15 es1 = [Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday]
16 es2 = []
17 es3 = [Thursday, Friday, Saturday, Sunday]
18 es4 = [Monday, Saturday] */
13.
集合视图:顾名思义,所有的集合视图和数据库中的普通视图一样,并不存储真实是的容器元素,而只是建立一套针对原有底层集合数据的引用机制,针对所有视图
的操作或底层集合的操作都会同时相互影响,如插入和删除等,但是对于视图而言,插入操作如果作用于其范围之外将会引发异常。对于不同类别的视图其操作也存
在不同的限制。
1). subList:获取底层集合中某一范围的元素,如List group2 =
staff.asList(10,20);
这第一个索引上的数据是被包含的,第二个索引的数据是不被包含的。如果执行了group2.clear()操作情况了视图中的数据,那么staff中这段
范围内的数据也会被连同删除。对于有序集合和映射表(Map),可以使用排序顺序而不是元素位置建立子范围,如SortedSet接口声明的
SortedSet<E> subSet(E from,E to); SortedSet<E> headSet(E
to); SortedSet<E> tailSet(E from);
注意这3个参数的类型均不是元素的索引,而是对象本身。SortedMap中也有类似的方法,如SortedMap<K,V>
subMap(K from,K to); SortedMap<K,V> headMap(K to);
SortedMap<K> tailMap(K
from)。在NavigableSet中提供更为丰富的接口,如NavigableSet<E> subSet(E
from,boolean fromInclusive,E to,boolean toInclusive);
NavigableSet<E> headSet(E to,boolean toInclusive);
NavigableSet<E> tailSet(E from,boolean fromInclusive);
2). 不可修改的视图:
Collections.unModifiableCollection();
Collections.unModifiableList(); //用于ArrayList、LinkedList
Collections.unModifiableSet(); //用于HashSet、TreeSet、LinkedHashSet
Collections.unModifiableSortedSet();//用于TreeSet
Collections.unModifiableMap(); //用于HashMap、TreeMap、LinkedHashMap
Collections.unModifiableSortedMap();//用于TreeMap
如List<String> staff = new LinkedList<String>();
List<String> unmodifiableList = new
Collections.unmodifiableList(staff);
这里unmodifiableList作为一个List对象,所有访问器的方法均可像普通List一样操作,但是对于更改器方法的调用将会引发
UnsupportedOperationException异常。如果确实需要修改数据只能通过原始集合来完成。
3) 同步视图:
Collections.synchronizedList(); //用于ArrayList、LinkedList
Collections.synchronizedSet(); //用于HashSet、TreeSet、LinkedHashSet
Collections.synchronizedSortedMap();//用于TreeMap
Collections.synchronizedSortedSet();//用于TreeSet
Collections.synchronizedMap(); //用于HashMap、TreeMap、LinkedHashMap
之后所有通过同步视图访问底层集合的操作都将是线程安全的,即synchronized关键字就不需要在集合的外部添加了,但是对于底层集合的直接访问仍然是线程不安全。
4)
被检查视图:见以下两种情况,第一种情况将会在集合执行(String)rawList.get()操作时抛出ClassCastException异
常,第一种情况则会在rawList.add()操作时就立即抛出ClassCastException。
1 public void uncheckedListTest() {
2 ArrayList<String> strings = new ArrayList<String>();
3 ArrayList rawList = strings;
4 rawList.add(new Date()); //no exception is raised here.
5 String ss = (String)rawList.get(0); //exception will be thrown here.
6 }
7
8 public void checkedListTest() {
9 ArrayList<String> strings = new ArrayList<String>();
10 ArrayList<String> safeStrings = Collections.checkedList(strings,String.class);
11 ArrayList rawList = safeStrings;
12 rawList.add(new Date()); //exception will be thrown here.
13 }
注:从集合框架的整体设计技巧、扩展性、效率等方面综合比较,Java在此方面和C++的stl相比还是存在一定的差距。特别是扩展性和效率方面,C++
的标准库定义了更多更灵活的模参扩展。由于标准库都是通过模板的方式实现的,因此其执行效率要明显优于基于虚函数的虚指针和虚表的这种动态绑定的多态方
式,其不足只是编译速度稍微慢罢了。
14. 算法:
在C++中提供了一套泛型算法,主要涵盖排序、搜索和比较等通用操作,由于这些泛型算法完全基于各种集合提供的迭代器(iterator)统一接口,因此可以达到一种高度的泛型操作,以及算法函数和集合之间的完全松耦合。见如下C++代码:
1 #include <algorithm>
2 #include <set>
3 #include <vector>
4 using namespace std;
5 int main()
6 {
7 vector<int> v1;
8 for (int i = 0; i < 10; ++i)
9 v1.push_back(i);
10 set<int> s1;
11 for (int i = 0; i < 20; ++i)
12 s1.insert(i);
13 sort(v1.begin(),v1.end());
14 binary_search(v1.begin(),v1.end(),5);
15 binary_search(s1.begin(),s1.end(),5);
16 return 0;
17 }
在Java中同样提供了这样的泛型算法,不同的是这些算法函数都是Collections中静态方法。见如下Java代码:
1 public static void main(String[] args) {
2 List<Integer> v1 = new ArrayList<Integer>();
3 for (int i = 0; i < 10; ++i)
4 v1.add(i);
5
6 List<Integer> l1 = new LinkedList<Integer>();
7 for (int i = 0; i < 20; ++i)
8 l1.add(i);
9
10 Collections.sort(v1);
11 Collections.sort(l1);
12 Collections.binarySearch(v1, 5);
13 Collections.binarySearch(l1, 5);
14 }