天天看点

Java容器&数据结构——总结&迭代器与适配器

  对于容器类库,Java使用者都不会陌生。“温故而知新,可以为师矣。”,我们仍然有必要对于这个日复一日使用的工具进行更深入的了解。这里仅对前段时间针对“容器”的学习做一个总结。

  Java提供了大量的持有对象的方式:

  1)数组将数字与对象联系起来。它保存类型明确的对象,查询时,不需要对结果做类型转换,可以是多维的,可以保存基本类型的数据。但是,数组一旦生成,容量就不能改变了。

  2)Collection保存单一的元素,Map保存关联的键值对。使用泛型,可以指定容器中存放的对象类型,避免错误的类型放到容器中,并且读取元素时,不必进行类型转换。各种Collection和各种Map都可以在你向其中添加元素时,自动调整尺寸。容器不能持有基本类型。自动包装机制会仔细地执行基本类型到容器中持有的包装器类型之间的双向转换。

  3)与数组一样,List也建立数字索引与对象的关联,数组和List都是排好序的容器。并且,List能自动扩充容量。

  4)如果要进行大量的随机访问,就使用ArrayList(访问快),如果要经常从表中间插入或删除元素,则应该使用LinekdList(更高效)。

  5)各种Queue以及栈的行为,由LinkedList提供。

  6)Map是一种将对象(“键”非数字)与对象关联的设计。HashMap设计用来快速访问,而TreeMap保持“键”始终处于排序状态,所以没有HashMap快。LinkedHashMap保持元素插入的顺序,但是也通过散列提供了快速访问的能力。

  7)Set不能接受重复元素。HashSet提供最快的查询速度,TreeSet保持元素处于排序状态。LinkedHashSet以插入顺序保存元素。

  8)新程序中不应该使用过时的Vector、Hashtable和Stack。(如果实在要使用Stack,可以参考我另一篇博文提供的实现例子Java容器——栈)

  大多数场景下,我们很少会用到上述所有类型的容器,常用的容器有ArrayList,LinkedList,HashSet和HashMap,但我想在这里提一下迭代器与适配器的惯用法。假设:如果现在有一个Iterator类,想要可以选择以向前或向后遍历一个单词列表,如果继承这个类,并且覆盖iterator()方法,就只能替换现有方法,而不能实现选择。一种解决方案是用设计模式中的“适配器”来解决。适配器使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。此模式主要包含三个角色:

  1)目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。

  2)需要适配的类(Adaptee):需要适配的类或适配者类。

  3)适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。

  这里,我希望在默认的前向迭代器的基础上,添加反向迭代器的能力,因此我不能覆盖,而是添加一个可以产生Iterator对象的方法,该对象可以使用foreach语句:

1 package com.test.collection;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Collection;
 5 import java.util.Iterator;
 6 
 7 class ReversibleArrayList<T> extends ArrayList<T> {
 8     public ReversibleArrayList(Collection<T> c) {
 9         super(c);
10     }
11     public Iterable<T> reversed() {
12         return new Iterable<T>() {
13             @Override
14             public Iterator<T> iterator() {
15                 return new Iterator<T>() {
16                     int current = size() - 1;
17                     @Override
18                     public boolean hasNext() {
19                         return current > -1;
20                     }
21 
22                     @Override
23                     public T next() {
24                         return get(current--);
25                     }
26 
27                     @Override
28                     public void remove() {
29                         throw new UnsupportedOperationException();
30                     }
31                 };
32             }
33         };
34     }
35 }      

View Code

package com.test.collection;

import java.util.Arrays;

public class AdapterMethodIdoom {

    public static void main(String[] args) {
        ReversibleArrayList<String> ral = new ReversibleArrayList<String>(Arrays.asList("To be or not to be".split(" ")));
        //用迭代器进行遍历
        for(String s : ral)
            System.out.print(s + " ");
        System.out.println();
        //用需求目标:反向迭代器进行遍历
        for(String s : ral.reversed())
            System.out.print(s + " ");
    }

}      

View Code

执行结果:

  如果直接将ral对象置于foreach语句,将得到默认的前向迭代器。但是如果调用reversed()方法,可以看到,反向迭代器将英文单词在控制台逆序地输出。