Ленивый неизменяемый список в Google Collections - PullRequest
8 голосов
/ 23 апреля 2010

Я искал достойную реализацию универсальной реализации отложенного немодифицируемого списка, чтобы обернуть мои записи результатов поиска.Немодифицируемая часть задачи проста, поскольку она может быть достигнута Collections.unmodifiableList(), поэтому мне нужно только разобраться с ленивой частью.

Удивительно, но google-collection не имеетчто-нибудь предложить;в то время как LazyList из коллекций Apache Commons не поддерживает дженерики.

Я обнаружил попытку построить что-то поверх google-коллекций, но она кажется неполной (Например, не поддерживает size()), устарел (не компилируется с 1.0 final) и требует некоторых внешних классов, но может быть использован в качестве хорошей отправной точки для создания моего собственного класса.

Кто-нибудь знает о каких-либохорошая реализация LazyList?Если нет, какой вариант, по вашему мнению, лучше:

  • написать свою собственную реализацию, основанную на google-collection ForwardingList, аналогично тому, что сделал Питер Маас;
  • написать свою собственную оболочку вокругКоллекции Commons LazyList (обертка будет добавлять только дженерики, поэтому мне не придется разыгрывать повсюду, а только в самой обертке);
  • просто напишите что-нибудь поверх java.util.AbstractList;

Любые другие предложения приветствуются.

РЕДАКТИРОВАТЬ: объяснение, почему мне нужен ленивый список.

У меня есть результат поиска Lucene (TopDocs), который в основном представляет собой набор указателей на документы Lucene,Мой класс результатов поиска будет принимать эти указатели в качестве входных данных и возвращать список объектов, которые сделаны из извлеченных и обработанных иным образом документов Lucene.Оборачивая все в ленивый список, я хочу убедиться, что не выполняю дорогостоящую обработку, когда в этом нет необходимости.

Ответы [ 4 ]

5 голосов
/ 23 апреля 2010

Google-коллекций и метод 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();
    }
  };
}

Да, вы можете смеяться.И вы, вероятно, должны.Я ... не очень рекомендую это.Тем не менее может быть меньше кода, чем то, что вы делаете в настоящее время.И я только что проверил это .. это работает.

4 голосов
/ 23 апреля 2010

Существует проект, который добавил функции Generics в общие коллекции Apache:

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

(Коллекции общих с Generics)

4 голосов
/ 23 апреля 2010

Я на самом деле решил это по-другому. Вместо того, чтобы идти ленивым и неизменяемым, я просто реализовал java.lang.Iterable<T>. Реализация выбрасывает UnsupportedOperationException на remove().

Мне пришлось немного изменить некоторые другие части кода, отказаться от чего-то, но я считаю, что это был лучший выбор. Iterable позволяет поместить его в цикл foreach.

Извините, что разочарован, если это не будет приемлемым выбором для кого-то в аналогичной ситуации, и большое спасибо за идеи.

2 голосов
/ 23 апреля 2010

Решение Питера Мааса, которое вы связываете, выглядит хорошо для меня - я настоятельно рекомендую вам поработать над этим, вместо того, чтобы тратить время на переизобретение этого. Просто замените Factory<T> на Supplier<T> (входит в коллекции Google). Его реализация subList также довольно умна, хотя и имеет некоторые специфические последствия: если вы получите subList() и попытаетесь добавить элемент за пределы подсписка, вы не получите IndexOutOfBoundsException (как правильный подсписок). должен сделать), но вы будете вставлять дополнительные элементы в список. Скорее всего, вам не понадобятся подсписки, поэтому самым безопасным было бы реализовать этот метод, выдав 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) вам не нравятся нулевые значения, которые следует рассматривать как элементы (хорошо!), И ( 2) что ваша структура может быть разреженной, тогда как фактический ArrayList будет расточительным.

...