Google Collections中的懒惰不可修改列表

时间:2010-04-22 21:07:45

标签: java list collections guava

我一直在寻找一个通用的懒惰不可修改列表实现的合适实现来包装我的搜索结果条目。任务的不可修改部分很容易,因为它可以通过Collections.unmodifiableList()实现,所以我只需要理清懒惰的部分。

令人惊讶的是,google-collections没有提供任何东西;虽然LazyList from Apache Commons Collections不支持泛型。

我发现an attempt可以在google-collections之上构建一些东西,但它似乎不完整(例如不支持size()),过时(不能用1.0最终编译)并且需要一些外部类,但可以作为构建我自己的类的良好起点。

是否有人知道LazyList的任何良好实现?如果没有,您认为哪个选项更好:

  • 编写我自己的实现,基于google-collections ForwardingList,类似于Peter Maas所做的;
  • 在Commons Collections LazyList周围编写我自己的包装器(包装器只会添加泛型,所以我不必在任何地方进行转换,只能在包装器本身中进行转换);
  • 只需在java.util.AbstractList;
  • 之上写一些内容

欢迎任何其他建议。

编辑:解释为什么我需要一个懒惰的列表。

我有一个Lucene搜索结果(TopDocs),它基本上是Lucene文档的一堆指针。我的搜索结果类将这些指针作为输入并返回由提取的和以其他方式处理的Lucene文档组成的对象列表。通过将所有内容包装到一个惰性列表中,我希望确保在不必要时不进行昂贵的处理。

4 个答案:

答案 0 :(得分:5)

Google-collections和Guava的Lists.transform方法为您提供了所寻求的懒惰。坚持Iterables.transform应该同样好。

但是,如果您还担心结果应该在首次创建时缓存,那么......现在,这是我想出的最好的结果,并且它不会非常令人欣慰:

List<Supplier<ExpensiveResult>> suppliers =
    ImmutableList.copyOf(Lists.transform(keys,
        new Function<Key, Supplier<ExpensiveResult>>() {
          public Supplier<ExpensiveResult> apply(Key key) {
            return Suppliers.memoize(Suppliers.compose(
                myExpensiveFunction(),
                Suppliers.ofInstance(key)));
          }
        }));

return Lists.transform(suppliers, ThisClass.<ExpensiveResult>supplyFunction());

 . . . 

private static <T> Function<Supplier<T>, T> supplyFunction() {
  return new Function<Supplier<T>, T>() {
    public T apply(Supplier<T> supplier) {
      return supplier.get();
    }
  };
}

是的,你可以笑。你可能应该这样做。我......真的不推荐这个。代码可能仍然少于您目前所做的代码。我只是测试了它..它的工作原理。

答案 1 :(得分:4)

有一个项目在Apache commons-collections中添加了Generics功能:

http://sourceforge.net/projects/collections/

(Commons-collection with Generics)

答案 2 :(得分:4)

我实际上已经以不同的方式解决了这个问题。我简单地实现了java.lang.Iterable<T>,而不是通过懒惰和不可修改的方式。该实现会在UnsupportedOperationException上抛出remove()

我不得不稍微修改一些其他代码部分,放弃一些东西,但我相信这是最好的选择。 Iterable允许将它放在foreach循环上。

很抱歉让人失望,如果对于类似情况的人来说这不是一个可行的选择,并且非常感谢这些想法。

答案 3 :(得分:2)

您链接的Peter Maas的解决方案对我来说很好 - 我强烈建议您使用它,而不是花时间重新发明这一点。只需将Factory<T>替换为Supplier<T>(包含在Google收藏中)。他对subList的实现也很聪明,虽然它有一些特殊的含义:如果你得到一个subList(),并尝试从subList的边界中添加一个元素,你就不会得到IndexOutOfBoundsException(作为一个正确的subList应该做的,但你会在列表中插入额外的元素。您可能不需要子列表,因此最安全的方法是通过抛出UnsupportedOperationException来实现该方法(或构造一个具有额外标志的LazyList,该标志是否允许通过get()调用来增长超出它的大小:如果它是由subList创建的,那么它不是。)

size() 支持(由ForwardingList本身自动支持)。

更新:请注意,正如凯文所说,你没有解释为什么这样的东西真的是你需要的。此外,也许您可​​能想要考虑这样的事情是否适用:

final Supplier<T> supplier = ...;
Map<Integer, T> graphs = new MapMaker()
   .makeComputingMap(
       new Function<Integer, T>() {
         public T apply(Integer index) {
           return supplier.get();
         }
       });

由于List<T>Map<Integer, T>或多或少代表相同的抽象数据类型,并且因为它从您的注释中看起来(1)您不喜欢将null视为元素(好! ),以及(2)你的结构可能是稀疏的,实际的ArrayList会浪费。