通常,程序总是根据运行时才知道的某些条件去创建新对象。在此之前,不会知道所需对象的数量,甚至不知道确切的类型。为解决这个普遍的编程问题,需要在任意时刻和任意位置创建任意数量的对象。
大多数语言都提供某种方法来解决这个基本问题。Java有多种方式保存对象(应该说是对象的引用)。例如数组,它是编译器支持的类型。数组是保存一组对象的最有效的方式,如果你想保存一组基本类型数据,也推荐使用这种方式。但是数组具有固定的尺寸,而在更一般的情况中,你在写程序时并不知道将需要多少个对象,或者是需要更复杂的方式来存储对象,因此数组尺寸固定这一限制显得过于受限了。
Java实用类库还提供了一套相当完整的容器类来解决这个问题,其中基本的类型是List、Set、Queue和Map。这些对象类型也称为集合类,但由于Java的类库中实用了Collection这个名字来指代该类库的一个特殊子集,所以使用了范围更广的术语“容器”称呼它们。容器提供了完善的方法来保存对象,你可以使用这些工具来解决数量惊人的问题。
这里我们来看看一些基本类型的容器的特点
package test2;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
public class PrintingContainers {
static Collection<String> fill(Collection<String> collection) {
collection.add("rat");
collection.add("cat");
collection.add("dog");
collection.add("dog");
return collection;
}
static Map<String, String> fill(Map<String, String> map) {
map.put("rat", "Fuzzy");
map.put("cat", "Rags");
map.put("dog", "Bosco");
map.put("dog", "Spot");
return map;
}
public static void main(String[] args) {
System.out.println("ArrayList: " + fill(new ArrayList<String>()));
System.out.println("LinkedList: " + fill(new LinkedList<String>()));
System.out.println("HashSet: " + fill(new HashSet<String>()));
System.out.println("TreeSet: " + fill(new TreeSet<String>()));
System.out.println("LinkedHashSet: " + fill(new LinkedHashSet<String>()));
System.out.println("HashMap: " + fill(new HashMap<String, String>()));
System.out.println("TreeMap: " + fill(new TreeMap<String, String>()));
System.out.println("LinkedHashMap: " + fill(new LinkedHashMap<String, String>()));
}
}
/**
* List类型: 有序 可重复
* ArrayList: 擅长随机访问元素,但是在List的中间插入和移除元素时较慢
* LinkedList: 与ArrayList相反
*
* Set类型:无序 不可重复
* HashSet:
* TreeSet:
* LinkedHashSet:
*
* Map类型:"键值对"对象 ,被称为"关联数组"或"字典"
* HashMap:
* TreeMap:
* LinkedHashMap
*/
程序运行结果是
ArrayList: [rat, cat, dog, dog]
LinkedList: [rat, cat, dog, dog]
HashSet: [cat, dog, rat]
TreeSet: [cat, dog, rat]
LinkedHashSet: [rat, cat, dog]
HashMap: {cat=Rags, dog=Spot, rat=Fuzzy}
TreeMap: {cat=Rags, dog=Spot, rat=Fuzzy}
LinkedHashMap: {rat=Fuzzy, cat=Rags, dog=Spot}
请大家仔细对比结果的不同简单理解下不同容器的特点。
接下来看一下List的简单使用
package test2;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class ListFeatures {
public static void main(String[] args) {
Random rand = new Random();
List<String> strList = new ArrayList<String>();
strList.add("a");
strList.add("c");
strList.add("b");
strList.add("d");
strList.add("f");
strList.add("g");
strList.add("e");
System.out.println("1: " + strList);
String h = new String("h");
strList.add(h);
System.out.println("2: " + strList);
System.out.println("3: " + strList.contains(h));
strList.remove(h);
String c = strList.get(2);
System.out.println("4: " + c + " " + strList.indexOf(c));
String cymric = "cymric";
System.out.println("5: " + strList.indexOf(cymric));
System.out.println("6: " + strList.remove(cymric));
System.out.println("7: " + strList.remove(c));
System.out.println("8: " + strList);
strList.add(3, "i");
System.out.println("9: " + strList);
List<String> subList = strList.subList(1, 4);
System.out.println("10: " + subList);
System.out.println("11: " + strList.containsAll(subList));
Collections.sort(subList);
System.out.println("sort subList: " + subList);
System.out.println("12: " + strList.containsAll(subList));
Collections.shuffle(subList, rand);
System.out.println("shuffled subList: " + subList);
List<String> copy = new ArrayList<String>(strList);
subList = Arrays.asList(strList.get(1), strList.get(4));
System.out.println("subList: " + subList);
copy.retainAll(subList);
System.out.println("13: " + copy);
copy = new ArrayList<String>(strList);
copy.remove(2);
System.out.println("14: " + copy);
copy.removeAll(subList);
System.out.println("15: " + copy);
copy.set(1, "j");
System.out.println("16: " + copy);
copy.addAll(2, subList);
System.out.println("17: " + copy);
System.out.println("18: " + strList.isEmpty());
strList.clear();
System.out.println("19: " + strList);
System.out.println("20: " + strList.isEmpty());
strList.addAll(subList);
Object[] o = strList.toArray();
System.out.println("21: " + o[1]);
}
}
输出结果是:
1: [a, c, b, d, f, g, e]
2: [a, c, b, d, f, g, e, h]
3: true
4: b 2
5: -1
6: false
7: true
8: [a, c, d, f, g, e]
9: [a, c, d, i, f, g, e]
10: [c, d, i]
11: true
sort subList: [c, d, i]
12: true
shuffled subList: [c, i, d]
subList: [c, f]
13: [c, f]
14: [a, c, d, f, g, e]
15: [a, d, g, e]
16: [a, j, g, e]
17: [a, j, c, f, g, e]
18: false
19: []
20: true
21: f
以上是List的一些较常用的API,必须学会熟练使用
Queue:
队列是一个典型的先进先出(FIFO)的容器。即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序是相同的。队列常被当做一种可靠的将对象从程序的某个区域传输到另一个区域的途径。
LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作Queue的一种实现。
package test2;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
public class QueueDemo {
public static void printQ(Queue queue) {
while(queue.peek() != null) {
System.out.print(queue.remove() + " ");
}
System.out.println();
}
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<Integer>();
Random random = new Random(47);
for (int i = 0; i < 10; i++) {
queue.offer(random.nextInt(i + 10));
}
printQ(queue);
Queue<Character> qc = new LinkedList<Character>();
for (char c : "Brontosaurus".toCharArray()) {
qc.offer(c);
}
printQ(qc);
}
}
结果:
8 1 1 1 5 14 3 1 0 1
B r o n t o s a u r u s
Stack
“栈”通常是指“后进先出”(LIFO)的容器,因为最后“压人”栈的元素,第一个“弹出”栈。
LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用。
package test2;
import java.util.Stack;
public class StackTest {
public static void main(String[] args) {
Stack<String> stack = new Stack<String>();
String[] strs = "My dog has fleas".split(" ");
for (String s : strs) {
stack.push(s);
}
while(!stack.empty()) {
System.out.print(stack.pop() + " ");
}
}
}
结果:
fleas has dog My
Map:
将对象映射到其他对象的能力是一种解决编程问题的杀手锏。例如,考虑一个程序,它将用来检查Java的Random类的随机性。理想状态下,Random可以将产生理想的数字分布,但要想测试它,则需要生成大量的随机数,并对落入各种不同范围的数字进行计数。Map可以很容易地解决该问题。在本例中,键是由Random产生的数字,而值是该数字出现的次数:
package test2;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class MapTest {
public static void main(String[] args) {
Random rand = new Random(47);
Map<Integer, Integer> m = new HashMap<Integer, Integer>();
for (int i = 0; i < 10000; i++) {
//Produce a number between 0 and 20
int r = rand.nextInt(20);
Integer freq = m.get(r);
m.put(r, freq == null ? 1 : freq + 1);
}
System.out.println(m);
}
}
结果:
{0=481, 1=502, 2=489, 3=508, 4=481, 5=503, 6=519, 7=471, 8=468, 9=549, 10=513, 11=531, 12=521, 13=506, 14=477, 15=497, 17=509, 16=533, 19=464, 18=478}
让我们浏览一下Java容器的简图:
![](https://img-my.csdn.net/uploads/201608/08/1470625494_6050.png)
可以看到,其实只有四种容器:Map、List、Set和Queue,它们各有两到三个实现版本。常用的容器用黑色粗线框表示。
后面会详细会介绍容器的底层实现及其优缺点。