类:自定义集合实现

许多程序员永远不需要实现自己的Collection s 类。您可以使用本章前面各节中描述的实现进行深入研究。但是,有一天您可能想编写自己的实现。借助于 Java 平台提供的抽象实现,这样做非常容易。在讨论如何编写实现之前,让我们先讨论为什么要编写一个实现。

编写实现的原因

以下列表说明了您可能要实现的自定义Collection的种类。它并非旨在详尽无遗:

  • 持久 :所有内置的Collection实现都驻留在主内存中,并且在程序退出时消失。如果您希望在下次程序启动时仍然存在一个集合,则可以通过在外部数据库上构建单板来实现它。这样的集合可以被多个程序同时访问。

  • 特定于应用 :这是一个非常广泛的类别。一个示例是包含实时遥测数据的不可修改的Map。键可以表示位置,并且可以响应get操作从这些位置的传感器读取值。

  • 高性能,专用 :许多数据结构利用受限的使用来提供比通用实现更好的性能。例如,考虑一个List包含 Long 期相同元素值。这样的列表(在文本处理中经常出现)可以游程 Long 度编码-游程可以表示为包含重复元素和连续重复次数的单个对象。该示例很有趣,因为它在性能的两个方面进行了权衡:与ArrayList相比,它需要更少的空间,但需要更多的时间。

  • 高性能,通用 :Java Collections Framework 的设计人员试图为每个interface提供最佳的通用实现,但是本可以使用很多很多数据结构,并且每天都在发明新的数据结构。也许您可以更快地提出一些建议!

  • 增强的功能 :假设您需要一个有效的 bag 实现(也称为* multiset *):一个Collection,它提供恒定时间的容纳检查,同时允许重复的元素。在HashMap上实现这样的集合是相当直接的。

  • 便利 :您可能需要其他实现,这些实现除了 Java 平台提供的便利之外,还提供其他便利。例如,您可能经常需要List个实例,它们代表Integer s 的连续范围。

  • 适配器 :假设您使用的是具有自己的临时集合 API 的旧版 API。您可以编写适配器实现,以允许这些集合在 Java Collections Framework 中运行。适配器实现*是一种薄木皮,它通过将后一种类型的操作转换为前一种类型的操作来包装一种类型的对象,并使它们的行为类似于另一种类型的对象。

如何编写自定义实现

编写自定义实现非常容易。 Java Collections Framework 提供了专门设计用于促进自定义实现的抽象实现。我们将从以下实现Arrays.asList的示例开始。

public static <T> List<T> asList(T[] a) {
    return new MyArrayList<T>(a);
}

private static class MyArrayList<T> extends AbstractList<T> {

    private final T[] a;

    MyArrayList(T[] array) {
        a = array;
    }

    public T get(int index) {
        return a[index];
    }

    public T set(int index, T element) {
        T oldValue = a[index];
        a[index] = element;
        return oldValue;
    }

    public int size() {
        return a.length;
    }
}

信不信由你,这与java.util.Arrays中包含的实现非常接近。就这么简单!您提供了一个构造函数,并提供了getsetsize方法,其余的全部由AbstractList完成。您可以免费获得ListIterator,批量操作,搜索操作,哈希码计算,比较和字符串 表示。

假设您想使实现更快一些。用于抽象实现的 API 文档精确描述了每种方法的实现方式,因此您将知道要覆盖哪些方法才能获得所需的性能。前面的实现的性能很好,但是可以改进一点。特别地,toArray方法在List上迭代,一次复制一个元素。给定内部表示形式,仅克隆阵列就更快,更明智。

public Object[] toArray() {
    return (Object[]) a.clone();
}

加上此覆盖和更多类似的覆盖,此实现正是在java.util.Arrays中找到的实现。为了完全公开,使用其他抽象实现会有些困难,因为您将不得不编写自己的迭代器,但仍然没有那么困难。

以下列表总结了抽象的实现:

  • AbstractCollection —既不是Set也不是ListCollection。至少必须提供iteratorsize方法。

  • AbstractSet-a Set;用法与AbstractCollection相同。

  • AbstractListList由诸如数组之类的随机访问数据存储备份。至少必须提供positional access方法(get,以及可选的setremoveadd)和size方法。抽象类负责listIterator(和iterator)。

  • AbstractSequentialListList由 Sequences 访问数据存储(例如链表)备份。至少必须提供listIteratorsize方法。抽象类负责位置访问方法。 (这与AbstractList相反.)

  • AbstractQueue —至少必须提供offerpeekpollsize方法以及iterator支持remove

  • AbstractMapMap。至少必须提供entrySet视图。这通常是通过AbstractSet类实现的。如果Map是可修改的,则还必须提供put方法。

编写自定义实现的过程如下:

  • 从前面的列表中选择适当的抽象实现类。

  • 提供该类的所有抽象方法的实现。如果您的自定义集合是可修改的,则您还必须覆盖一种或多种具体方法。抽象实现类的 API 文档将告诉您要覆盖的方法。

  • 测试并在必要时调试实现。您现在有了一个有效的自定义集合实现。

  • 如果您担心性能,请阅读抽象实现类的 API 文档,以了解要继承其实现的所有方法。如果看似太慢,请覆盖它们。如果覆盖任何方法,请确保在覆盖前后测量该方法的性能。您需要花多少精力来调整性能,这取决于实现将获得多少使用以及其使用对性能的重要性。 (通常最好省略此步骤.)