java - просмотр в реальном времени коллекции, содержащейся в коллекции, содержащейся внутри ... и т. д. - PullRequest
2 голосов
/ 17 мая 2011

У меня есть класс A, который может содержать много экземпляров класса B, который, в свою очередь, может содержать много экземпляров класса C, который может содержать много экземпляров класса D

Теперь в классе A у меня есть метод getAllD. В настоящее время каждый раз, когда это вызывается, происходит много итераций, и недавно создается и возвращается довольно большой список. Это не может быть очень эффективным.

Мне было интересно, как я могу сделать это лучше. Этот вопрос Объединить несколько коллекций в одну логическую коллекцию? , кажется, затрагивает похожую тему, но я не совсем уверен, как можно применить ее к моей ситуации.

Все комментарии очень ценятся!

Ответы [ 4 ]

4 голосов
/ 17 мая 2011

Я бы скомбинировал Iterables.concat с Iterables.transform , чтобы получить живое представление Ds:

public class A {
    private Collection<B> bs;

    /**
     * @return a live concatenated view of the Ds contained in the Cs
     *         contained in the Bs contained in this A.
     */
    public Iterable<D> getDs() {
        Iterable<C> cs = Iterables.concat(Iterables.transform(bs, BToCsFunction.INSTANCE));
        Iterable<D> ds = Iterables.concat(Iterables.transform(cs, CToDsFunction.INSTANCE));
        return ds;
    }

    private enum BToCsFunction implements Function<B, Collection<C>> {
        INSTANCE;

        @Override
        public Collection<C> apply(B b) {
            return b.getCs();
        }
    }

    private enum CToDsFunction implements Function<C, Collection<D>> {
        INSTANCE;

        @Override
        public Collection<D> apply(C c) {
            return c.getDs();
        }
    }
}


public class B {
    private Collection<C> cs;

    public Collection<C> getCs() {
        return cs;
    }
}

public class C {
    private Collection<D> ds;

    public Collection<D> getDs() {
        return ds;
    }
}

Это хорошо работает, если ваша цель простоперебирать Ds, и вам не нужно представление collection .Это позволяет избежать создания большой временной коллекции.

1 голос
/ 17 мая 2011

Ответ на ваш вопрос будет зависеть от специфики вашей ситуации. Эти коллекции статичны или динамичны? Насколько велика ваша коллекция B в A? Собираетесь ли вы получить доступ к D только из A, или вам иногда захочется оказаться ниже в дереве или вернуть B или C? Как часто вы хотите получить доступ к одному и тому же набору D из определенного A? Может ли D (или C или B) ассоциироваться с более чем 1 A?

Если все динамично, то лучший шанс улучшить производительность - это иметь родительские ссылки от C до A, а затем обновлять родительский элемент всякий раз, когда список D в C изменяется. Таким образом, вы можете сохранить коллекцию D в своем объекте A и обновлять A всякий раз, когда один из C получает новый или удаляет его.

Если все статично и существует некоторое повторное использование коллекций D из каждого A, то кэширование может быть хорошим выбором, особенно если имеется много B. A будет иметь карту с ключом B и значением коллекции Ds. Метод getAllDs () сначала проверит, есть ли на карте ключ для B, и если да, вернет свою коллекцию D. Если нет, то он сгенерирует коллекцию, сохранит ее в карте кэша и вернет коллекцию.

Вы также можете использовать дерево для хранения объектов, особенно если они довольно простые. Например, вы можете создать объект XML DOM и использовать выражения XPath для извлечения нужного вам набора D. Это позволило бы гораздо более динамичный доступ к интересующим вас наборам объектов.

Каждое из этих решений имеет различные компромиссы с точки зрения затрат на настройку, затрат на обслуживание, своевременности результатов, гибкости использования и стоимости получения результатов. То, что вы должны выбрать, будет зависеть от вашего контекста.

0 голосов
/ 17 мая 2011

Это не может быть очень эффективным.

Итерирование в памяти чертовски быстро.Кроме того, эффективность создания ArrayList из 10 тыс. Элементов по сравнению с 10 ArrayList с 1 тыс. Элементов каждый не будет так сильно отличаться.Итак, в заключение, вы, вероятно, должны сначала просто перейти к самой простой итерации.Скорее всего, это работает просто отлично.

Даже если у вас есть несколько элементов gazillion, вероятно, целесообразно в любом случае реализовать прямую итерацию для сравнения.В противном случае вы не знаете, сможете ли вы оптимизировать или замедлите это, делая что-то умное.

Сказав, что, если вы хотите оптимизировать последовательный доступ для чтения всех D, я бы поддерживал "индекс" снаружи.Индекс может быть LinkedList, ArrayList, TreeList и т. Д. В зависимости от вашей ситуации.Например, если вы не уверены в длине индекса, возможно, стоит избегать ArrayList.Если вы хотите эффективно удалить случайные элементы, используя ссылку на этот элемент, OrderedSet может быть намного лучше, чем список и т. Д.

Когда вы делаете это, вы должны беспокоиться о согласованности индекса и фактических ссылокв ваших классах.Т.е. больше сложности = больше места, чтобы скрывать ошибки.Так что, если вы не сочтете это необходимым в ходе тестирования производительности, на самом деле не стоит пытаться оптимизировать.

(кстати, избегание создания экземпляров новых объектов коллекции вряд ли сделает вещи намного быстрее, если вы не говорите о EXTREME high-Выполнение кода. Создание экземпляров объектов в современных JVM занимает всего несколько десятков наносекунд или что-то в этом роде. Кроме того, вы можете по ошибке использовать ArrayList, имеющий небольшую начальную длину, или что-то еще, что еще хуже)

0 голосов
/ 17 мая 2011

На самом деле, я думаю, Iterables.concat (или IteratorChain от Apache Commons) подойдет для вашего случая:

class A {
    Collection<B> children;
    Iterator<D> getAllD() {
        Iterator<Iterator<D>> iters = new ArrayList<Iterator<D>>();
        for (B child : children) {
            iters.add(child.getAllD());
        }
        Iterator<D> iter = Iterables.concat(iters);
        return iter;
    }
}
class B {
    Collection<C> children;
    Iterator<D> getAllD() {
        Iterator<Iterator<D>> iters = new ArrayList<Iterator<D>>();
        for (C child : children) {
            iters.add(child.getAllD());
        }
        Iterator<D> iter = Iterables.concat(iters);
        return iter;
    }
}
class C {
    Collection<D> children;
    Iterator<D> getAllD() {
        Iterator<D> iter = children.iterator();
        return iter;
    }
}
...