Java提供一套相当完整的容器类来存储数量不确定的对象。集合类:list,set.queue,map。通过使用泛型,可以在编译器防止将错误类型的对象放置到容器中去,在取出的时候也会帮忙转型。
Collection,一个独立元素序列。List按照插入的顺序保存元素,set不能有重复的元素,Queue按照队列的规则确定对象顺序。
Map键值对,允许用键来找值。
HashSet\TreeSet\LinkedHashSet都是Set类型。HashSet是最快的获取元素的方式,顺序看起来杂乱无章(通常只用关心某个元素是否是其成员,不关心顺序)。TreeSet将元素存储在红黑树数据结构中,按照结果的升序保存对象。LinkedHashSet因为查询速度也使用了hash,同时使用了链表来维护顺序,按照添加的顺序保存对象。
HashMap是最快的获取元素的方式,顺序看起来杂乱无章。TreeMap按照结果的升序保存对象。LinkedHashMap按照添加的顺序保存键,同时还保留了HashMap的查询速度。
List在Collection的基础上添加了较多的方法。ArrayList 和LinkedList两种,前者在随机访问方面有较大优势,在插入和移除的时候较慢。后者插入和删除的代价较低,顺序访问也较快,但是对于随机访问则相对较慢。
迭代器(也是一种设计模式):我们在设计的时候一直考虑的是,怎么去抽象到更高的高度,不对确切的内容进行编程,而是抽象到更高的高度,方便复用。接收对象容器并使用它,使每个对象都能执行操作。
class Pets{
List<String> pets=new ArrayList<>();
public List<String> getPets(){
pets.add("pet1");
pets.add("pet2");
pets.add("pet3");
return pets;
}
}
public class IteratorTest {
public static void display(Iterator<String> it){
while (it.hasNext()){
String s=it.next();
System.out.print(s);
}
System.out.println();
}
public static void display(Collection<String> c){
for (String s:c) {
System.out.print(s);
}
System.out.println();
}
public static void main(String args[]){
Pets p=new Pets();
List<String> petsList=p.getPets();
Set<String> petsSet=new HashSet<>(petsList);
Map<String,String> petsMap=new HashMap<>();
String[] name=("cat,pig,dog".split(","));
for (int i=0;i<3;i++){
petsMap.put(name[i],petsList.get(i));
}
display(petsList);
display(petsSet);
display(petsList.iterator());
display(petsSet.iterator());
System.out.println(petsMap);
System.out.println(petsMap.keySet());
display(petsMap.keySet());
display(petsMap.values());
display(petsMap.values().iterator());
}
}
迭代器就是为了达到这样的目的。迭代器是一个对象,遍历并选择其中的对象。不用关心底层的结构。Java的interator只能单向移动。使用方法interator()要求容器返回一个Interator对象,其准备好返回序列的第一个元素,使用next返回下一个,hasnext检查是否还有下一个,remove将元素删除。在调用remove前一定要调用next。
ListInterator是更强大的interator的子类型。可以双向移动。
Linkedlist额外提供了栈,队列,双端队列的方法。
Stack栈,后进先出的容器LIFO,自助餐托盘。实际上LinkedList就能当栈来使用。
public class Stack<T> {
//大概的了解栈的功能,了解本质上Linkedlist是可以实现栈的功能的
private LinkedList<T> storage=new LinkedList<>();
public void push(T t){storage.add(t);}
public T peek(){return storage.getFirst();}
public T pop(){return storage.removeFirst();}
public boolean isEmpty(){return storage.isEmpty();}
public String toString(){return storage.toString();}
}
public class StackTest {
public static void main(String args[]){
java.util.Stack<String> stack=new Stack<>();
for (String s:"my name is haha".split(" ")
) {
stack.push(s);
}
while (!stack.isEmpty()){
System.out.println(stack.pop());
}
}
}
Set不保存重复的元素,实际上set就是collection。
Map与其他的Collection一样,很容易就扩展为多维的形式。可多重嵌套使用。例如跟踪某个拥有多个宠物的人,Map<Person,List<Pet>>.Map可以返回它的key的Set,它的键值对的set。keyset()方法产生了前者的set集合,可以方便用foreach语法进行遍历。
public class MapTest {
public static void test1(){
Random random=new Random(47);
Map<Integer,Integer> m=new HashMap<>();
for(int i=0;i<1000;i++){
int r= random.nextInt(20);
Integer freq=m.get(r);
m.put(r,freq==null?1:freq+1);//这种语法有点意思
}
System.out.println(m);
}
public static void test2(){
Map<String,Animal> map=new HashMap<>();
map.put("cat",new Animal("miao"));
map.put("dog",new Animal("wang"));
System.out.println(map);
Animal dog=map.get("dog");
System.out.println(dog);
System.out.println(map.containsKey("cat"));
System.out.println(map.containsValue(dog));
}
public static void main(String args[]){
test1();
test2();
}
}
class Animal{
String name;
public Animal(String name){
this.name=name;
}
}
Queue队列,典型的先进先出FIFO,在并发编程中特别重要。实际上LinkedList就能当队列来使用。注意还存在PriorityQueue即是优先级队列。队列规则是给定一组队列中的元素的情况下,下一个弹出队列元素的规则。先进先出声明的是下一个元素应该是等待时间最长的元素。优先级队列返回的元素是最需要的元素,如飞机场某飞机要起飞,该飞机的乘客可以优先登机。当优先队列调用offer方法进行插入对象的时候,对象会自动根据规则排序。同时peek,poll,remove的时候也是获取的优先级最高的。注意可以通过自己提供Comparator方法来修改顺序。
public class QueueTest {
public static void main(String args[]){
Queue<Integer> queue=new LinkedList<>();
//我注意到,如果是对于Stack而言,后面的实现我写出Stack<String> s=new Stack<>();是默认OK的
//但是对于Queue,我写了相似的结构以后,马上出了一堆方法要我覆盖
//offer方法将一个元素插入到队尾或者返回false
//peek方法和element方法都在不移除的情况下返回队头
//peek方法在队列为空的时候返回null,而element方法返回NoSuchElementException异常
//poll和remove方法将移除并返回对头,但poll方法在队列为空的时候返回null,而remove抛出NoSuchElementException异常
Random random=new Random(47);
for (int i=1;i<10;i++){
queue.offer(random.nextInt(i+10));
}
printQ(queue);
System.out.println(queue);
Queue<Character> qc=new LinkedList<>();
for (char c:"Helloworld".toCharArray()){
qc.offer(c);
}
System.out.println(qc);
printQ(qc);
}
public static void printQ(Queue q){
while (q.peek()!=null){
System.out.println(q.remove());
}
}
}
foreach可以用于数组,也可以用于任何collection对象。之所以能够正常工作,是因为Javase5引入了新的Iterator接口,该接口包含一个能产生Iterator的iterator方法。如果你创建了Iterable实现的类,都可以用于foreach。
Java提供了大量关于持有对象的操作,数组将数字与对象关联起来。保存类型明确的对象,查询对象的时候无需类型转换。数组一旦形成就不能改变大小。
public class ForeachCollections {
public static void main(String args[]){
Collection<String> cs=new LinkedHashSet<>();
Collections.addAll(cs,"go home or go big".split(" "));
for (String s:cs
) {
System.out.println(" "+s+" ");
}
}
}
只要实现了Iterable类就可以用于foreach中。
public class IterableClass implements Iterable<String> {
//这段很精彩,要背背
//返回了实现Iterator<String>的匿名内部类的实例
protected String[] words=("you should study hard so you can go big".split(" "));
public Iterator<String> iterator(){
return new Iterator<String>() {
private int index=0;
public boolean hasNext() {
return index<words.length;
}
public String next() {
return words[index++];
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
//注意这个地方的ItearbleClass
public static void main(String args[]){
for (String s:new IterableClass()
) {
System.out.println(s);
}
}
}
Collection保存单一的元素,而map保存相关联的键值对。有了Java泛型就可以指定容器中存储对象的类型,并且取出元素时候无需进行类型转换。容器大小可自动调整,容器不能存放基本数据类型,但是包装机制可以实现双向转换。各种栈和队列的行为,由linkedlist提供支持。
map是一种将对象(而非数字)与对象相关联的设计。hashmap用于快速访问,treemap用于保持键始终是排序状态,linkedhashmap保持插入的顺序同时也提供快速访问的支持。
Set不接受相同的元素。hashset快速查询,treeset保持排序状态,linkedhashset保持插入顺序也提供快速访问机制。