6

我一直在寻找一种享元模式的实现,并在达到谷歌搜索的第 20 页后放弃了。虽然那里有无数愚蠢的例子,但似乎没有人发布过 Java 中可重用的实现。

对我来说,flyweight 只有在你必须保留许多这样的实例时才有意义,所以它必须作为一个集合来实现。我想要的是一个工厂,它采用字节/短/整数/长映射器实现,并返回一个列表、集合或映射,它看起来像一个普通的对象集合,但在内部将它的数据存储为一个原始数组,从而节省大量内存。映射器将获取一个 X 类型的对象,并将其映射到一个基元,或者以相反的方式进行。

在某处有类似的东西吗?

[编辑] 我正在寻找一个支持这种模式的Collection库,而不仅仅是任何示例,其中有数百个。

4

6 回答 6

3

如果要替换 List,可以使用 TByteArrayList 代替。如果你想替换 List where MyClass { int a; T 对象;您可以改用 TIntObjectHashMap。

如果您想用两个必须排序的字段或三个或更多字段替换某些内容,您需要实现自己的类来包装数组以保存数据。这是使用基于列的表模型。

例如

class MyClass {
    byte b;
    int i;
    String s;
}

class MyClassList {
    int size = 0;
    int capacity;
    byte[] bytes;
    int[] ints;
    String[] strings;

    MyClassList(int capacity) {
        this.capacity = capacity;
    }

    public void add(MyClass myClass) {
        if (size == capacity) resize();
        bytes[size] = myClass.b;
        ints[size] = myClass.i;
        strings[size] = myClass.s;
        size++;
    }

    public void get(MyClass myClass, int index) {
        if (index > size) throw new IndexOutOfBoundsException();
        myClass.b = bytes[index]; 
        myClass.i = ints[index];
        myClass.s = strings[index];
    }
}

从 Java 5.0 开始,自动装箱缓存是享元的示例。

Integer i1 = 1;
Integer i2 = 1;
System.out.println(i1 == i2); // true, they are the same object.

Integer i3 = -200;
Integer i4 = -200;
System.out.println(i3 == i4); // false, they are not the same object.

如果您想阅读代码,请查看您的 IDE 中的 Integer.valueOf(int) 或 http://www.docjar.com/html/api/java/lang/Integer.java.html第 638 行

编辑: Integer 的自动装箱使用 IntegerCache 这是一个集合。ArrayList 是一个包装数组并具有大小的类...

private static class IntegerCache {
    static final int high;
    static final Integer cache[];
于 2011-07-27T08:35:22.723 回答
2

我认为, Integer.valueOf(String s) 是相当轻量级的。因为据我所知,它在内部保留了一些创建的整数,因此,当您传递之前传递的字符串时,它会返回一个现有实例。

于 2011-07-27T08:34:47.930 回答
1

你见过四人帮——设计模式吗?如果您愿意,我将重写他们的实现(尽管它是在 C++ 中),但稍后会。

这是一本你应该拥有的书——永远不知道它什么时候会派上用场。

于 2011-07-27T07:55:13.257 回答
1

对于您的要求,我认为您应该尝试TroveColt。这些库支持原始集合。

于 2011-07-27T08:13:27.177 回答
1

GNU Trove对包做了类似的事情gnu.trove.decorator(仅限 Map 和 Set,而不是 List)。

这种事情虽然效率很低,但我怀疑在很多情况下权衡是值得的。

为什么不只使用适当的原始集合?

于 2011-07-27T08:16:13.807 回答
0

我制作了一个测试程序来看看 Flyweight 在 Java 中的工作情况。为了记录,我将在这里描述我的结果。YMMV

1) 如果您的对象中有多个字段,您可以通过将它们混合在一个 int 或 long 中来节省一些 CPU。这对编程很痛苦,而且容易出错,但速度要快几个百分点,因为多数组访问比位操作更昂贵。随着字段数量的增长,更是如此。

2) 对于小型实例(4 字节状态),它的运行速度会比直接存储实例慢 25%。但是,仅当您没有在每个 get 上创建一个新实例时。这就是真正的问题所在。每次获取都必须创建一个新实例非常昂贵;在这种情况下,它不是慢 25%,而是慢 500%!

3) 我看到了两种在 get 上保存实例创建的方法:

A)您的 get 方法填充一个预先存在的实例,而不是创建一个新实例。换句话说,您将结果对象作为输入传递给您的 get 方法。

B) 你使用不可变的实例,缓存它们,然后从 get 中返回一个缓存的实例。仅当您的列表索引很重要并且您希望在列表中多次重复使用相同的值时,这才有用。如果您这样做,那么您也可以直接在集合中存储对缓存实例的引用,而不是某些状态,因为无论如何您只需为每个实例支付 4 个字节的引用。在这种情况下,您的状态必须为 2 个字节或更少,才能存储状态而不是引用。

因此,作为最终答案,Flyweight 没有通用库的原因是它只在特定条件下付费,否则它并不值得。

于 2011-07-27T19:52:55.753 回答