Java核心技术卷I:基础知识(原书第8版):13.5 遗留的集合

来源:互联网 发布:黑苹果windows双系统 编辑:程序博客网 时间:2024/06/06 19:40

铁文整理

13.5 遗留的集合

    本节将讨论Java程序设计语言自问世以来就存在的集合类;Hashtable类和非常有用的子类PropertiesVector的子类Stack以及BitSet类。

13.5.1 Hashtable

    Hashtable类与HashMap类的作用一样,实际上,它们拥有相同的接口。与Vector类的方法一样。Hashtable的方法也是同步的。如果对同步性或与遗留代码的兼容性没有任何要求,就应该使用HashMap

    注释:这个类的名字是Hashtable,带有一个小写的t。在Windows操作系统下,如果使用HashTable会看到一个很奇怪的错误信息,这是因为Windows文件系统对大小写不敏感,而Java编译器却对大小写敏惑。

13.5.2 枚举

    遗留集合使用Enumeration接口对元素序列进行遍历。Enumeration接口有两个方法,即hasMoreElementsnextElement。这两个方法与Iterator接口的hasNext方法和next方法十分类似。

    例如,Hashtable类的elements方法将产生一个用于描述表中各个枚举值的对象:

        Enumeration<Employee> e = staff.elements();

        while (e.hasMoreElements()) {

            Enployee employee = e.nextElenent();

        }

    有时还会遇到遗留的方法,其参数是枚举类型的。静态方法Collections.enumeration将产生一个枚举对象,枚举集合中的元素,例如:

           List<InputStream> streams = null;

           SequenceInputStream in = new SequenceInputStream(CollectTons.enumeration(streams));

           // the SequenceInputStream constructor expects an enumeration

    注释:在C++中,用迭代器作为参数十分普。遍幸好,在Java的编程平台中,只有极少的程序员沿用这种习惯。传递集合要比传递迭代器更为明智。集合对象的用途更大。当接受方如果需要时,总是可以从集合中获得迭代器,而且,还可以随时地使用集合的所有方法。不过,可能会在某些遗留代码中发现枚举接口,因为这是在Java SE 1.2的集合框架出现之前,它们是泛型集合唯一可以使用的机制。

APIjava.util.Enumeration<E> 1.0

  • boolean hasMoreElements()

  • E nextElement():返回被检测的下一个元素。如果hasMoreElements返回false,则不要调用这个方法。

APIjava.util.Hashtable<K, V> 1.0

  • Enumeration<K> keys():返回一个遍历散列表中键的枚举对象。

  • Enumeration<V> elements():返回一个遍历散列表中元素的枚举对象。

APIjava.util.Vector<E> 1.0

  • Enumeration<E> elements():返回遍历向量中元素的枚举对象。

13.5.3 属性映射表

    属性映射表(properties map)是一个类型非常恃殊的映射表结构。它有下面3个特性:

  • 键与值都是字符串。

  • 表可以保存到一个文件中,也可以从文件中加载。

  • 使用一个默认的辅助表。

    实现属性映射表的Java平台类称为Properties。属性映射表通常用于程序的特殊配置选项,参见第10章。

APIjava.util.Properties 1.0

  • Properties()

  • Properties(Properties defaults):创建一个带有一组默认值的空的属性映射表。

  • String getProperty(String key):获得属性的对应关系,返回与键对应的字符串。如果在映射表中不存在,返回默认表中与这个键对应的字符串。

  • String getProperty(String key, String defaultValue):获得在键没有找到时具有的默认值属性,它将返回与键对应的字符串,如果在映射表中不存在,就返回默认的字符串。

  • void load(InputStream in):从InputStream加载属性映射表。

  • void store(OutputStream out, String commentString):把属性映射表存储到OutputStream。

13.5.4

    1.0版开始,标准类库中就包含了Stack类,其中有大家熟悉的push方法和pop方法。但是,Stack类扩展为Vector类,从理论角度看,Vector类并不太令人满意,它可以让栈使用不属于栈操作的insertremove方法,即可以在任何地方进行插入或刪除操作,而不仅仅是在栈顶。

APIjava.util.Stack<E> 1.0

  • E push(E item)

  • E pop():弹出并返回栈顶的item。如果栈为空,请不要调用这个方法。

  • E peek():返回栈顶元素,但不弹出。如果栈为空,请不要调用这个方法。

13.5.5 位集

    Java平台的BitSet类用于存放一个位序列(它不是数学上的集,称为位向量或位数组更为合适)。如果需要高效地存储位序列(例如,标志)就可以使用位集。由于位集将位包装在字节里,所以,使用位集要比使用Boolean对象的ArrayList更加高效。

    BitSet类提供了一个便于读取、设置或清除各个位的接口。使用这个接口可以避免屏蔽和其他麻烦的位操作。如果将这些位存储在intlong变量中就必须进行这些繁琐的操作。

    例如,对于一个名为bucketOfBitsBitSet

    bucketOfBits.get(i)

    如果第i位处于“开”状态,就返回true;否则返回false。同样地,

    bucketOfBits.set(i)

    将第i位置为“开”状态。最后,

    bucKetOfBfts.clear(i)

    将第i位置为“关”状态。

    C++注释:C++位集糢板与Java平台的BitSet功能一样。

APIjava.util.BitSet 1.0

  • BitSet(int initialCapacity):创建一个位集

  • int length():返回位集的“逻辑长度,即1加上位集的最高设置位的索引。

  • boolean get(int bit)

  • void set(int bit)

  • void clear(int bit)

  • void and(BitSet set)

  • void or(BitSet set)

  • void xor(BttSet set)

  • void andNot(BitSet set):清除这个位集中对应另一个位集中设置的所有位。

    Eratosthenes筛子”基准测试

    作为位集应用的一个示例,这里给出一个采用“Eratosthenes筛子”算法査找素数的实现(素数是指只能被1和本身整除的数,例如235,“Eratosthenes筛子”算法是最早发现的用来枚举这些基本数字的方法之一)。这并不是一种查找素数的最好方法,但是由于某种原因,它已经成为测试编译程序性能的一种流行的基准。(这也不是一种最好的基准测试方法,它主要用于测试位操作。)

    在此,将尊重这个传统,并给出实现。其程序将计算2~2 000 000之间的所有素数(一共有148 933个素数,或许不打算把它们全部打印出来吧)。

    这里并不想深入程序的细节,关键是要遍历一个拥有200万个位的位集。首先将所有的位置为“开”状态,然后,将已知素数的倍数所对应的位都置为“关”状态。经过这个操作保留下来的位对应的就是素数。例13-7是用Java程序设计语言实现的这个算法程序,而例13-8是用C++实现的这个算法程序。

    注释:尽管筛选并不是一种好的基准测试方法,这里还是对这个算法的两个算法的运行时间进行了测试,下面是在1.66 GHz双核IBM ThinkPad计算机上运行时间的结果,这台计算机内存为2GB,操作系统为Ubuntu 7.04

  • C++ (g++ 4.1.2): 360毫秒

  • Java(Java SE 6):105毫秒

    我们已经对《Java核心技术》7个版本进行了这项测试,在最后的3个版本中,Java轻松地战胜了C++。公平地说,如果有人改变C++优化器的级别,将可以用60毫秒的时间战胜Java。如果程序运行的时间长到触发Hotspot即时编译器时,Java只与C++打个平手。

13-7 Sieve.java

import java.util.*;

 

/**

 * This program runs the Sieve of Erathostenes benchmark. It computes all primes

 * up to 2,000,000.

 *

 * @version 1.21 2004-08-03

 * @author Cay Horstmann

 */

public class Sieve {

    public static void main(String[] s) {

        int n = 2000000;

        long start = System.currentTimeMillis();

        BitSet b = new BitSet(n + 1);

        int count = 0;

        int i;

        for (i = 2; i <= n; i++)

            b.set(i);

        i = 2;

        while (i * i <= n) {

            if (b.get(i)) {

                count++;

                int k = 2 * i;

                while (k <= n) {

                    b.clear(k);

                    k += i;

                }

            }

            i++;

        }

        while (i <= n) {

            if (b.get(i))

                count++;

            i++;

        }

        long end = System.currentTimeMillis();

        System.out.println(count +" primes");

        System.out.println((end - start) +" milliseconds");

    }

}

13-8 Sieve.cpp

/**

@version 1.21 2004-08-03

@author Cay Horstmann

*/

 

#include <bitset>

#include <iostream>

#include <ctime>

 

using namespace std;

 

int main()

    const int N = 2000000;

    clock_t cstart = clock();

 

    bitset<N + 1> b;

    int count = 0;

    int i;

    for (i = 2; i <= N; i++)

        b.set(i);

    i = 2;

    while (i * i< = N)

    { 

        if (b.test(i))

        { 

            count++;

            int k = 2 * i;

            while (k <= N)

            { 

                b.reset(k);

                k += i;

            }

        }

        i++;

    }

    while (i< = N)

    { 

        if (b.test(i))

            count++;

        i++;

    }

 

    clock_t cend = clock();

    double millis = 1000.0 * (cend - cstart) / CLOCKS_PER_SEC;

 

    cout << count << " primes\n" << millis <<" milliseconds\n";

 

    return 0;

}

    到此为止,Java集合框架的旅程就结束了,正如所看到的,Java类库提供了大量的集合类以适应程序设计的需要。在本书的最后—章,将讨论非常重要的并发程序设计。


0 0
原创粉丝点击