Несколько списков или список и getSublist() (Java)

У меня есть абстрактный класс «сущность» и объекты (которые расширяют «сущность»), которые реализуют разные интерфейсы. У меня также есть ArrayList, который содержит все эти разные объекты.

Теперь, если мне нужен доступ ко всем сущностям, реализующим определенный интерфейс (для использования его методов), я использую следующий метод (который возвращает отфильтрованный список «сущностей», реализующих интерфейс «IDirectFire»):

public ArrayList<IDirectFire> getDirectFireSublist() {//direct fire sublist
    ArrayList<IDirectFire> sublist = new ArrayList();
    entities.stream().filter((it) -> (it instanceof IDirectFire)).forEach((it) -> {
        sublist.add((IDirectFire) it);
    });
    return sublist;
}

Теперь к моему вопросу: следует ли мне дальше работать с этим методом или создать новый ArrayList, который существует помимо «сущностей» и который мне нужно будет вручную обновлять каждый раз при изменении «сущностей»?

Мне нужно много обновлять «сущности», поэтому я не уверен, что более эффективно хранить несколько подсписков и обновлять их каждый раз при изменении «сущностей» или мне следует продолжать использовать методы для фильтрации «сущностей» и применять методы к этим подсписки. Имейте в виду, что эти подсписки также будут использоваться в цикле в других методах, например:

private void resetFirestatusIDF() {//reset firestatus (IDirectFire)
    getDirectFireSublist().stream().forEach((it) -> {
        it.dfHasFired(false);
    });}

Это жизнеспособно? Заранее спасибо!


person paxikek    schedule 17.07.2016    source источник
comment
В общем, следует стараться избегать использования instanceof, где это возможно, так как это считается дизайнерским запахом.   -  person wakjah    schedule 17.07.2016


Ответы (4)


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

public Stream<IDirectFire> getDirectFire() {
    return entities.stream().filter((it) -> (it instanceof IDirectFire));
}

Вы также можете использовать Guava и вместо этого вернуть отфильтрованное Iterable:

public Iterable<IDirectFire> getDirectFire() {
    return FluentIterable.from(entities).filter(IDirectFire.class);
}

Затем, чтобы перебрать элементы в другом месте:

private void resetFirestatusIDF() {
    getDirectFire().forEach((it) -> it.dfHasFired(false));
}
person Sam    schedule 17.07.2016
comment
Спасибо, хотя в настоящее время я не использую Guava. Посмотрим на это! - person paxikek; 17.07.2016
comment
Метод filter(Class<?>) в Guava отлично подходит для того, что вы делаете, но в остальном метод Stream так же хорош. У них обоих есть метод forEach, что означает, что пример цикла идентичен для обоих. - person Sam; 17.07.2016

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

Второе, что я заметил, это использование потока для фрагмента кода 1. Я бы рекомендовал вам и альтернативный подход к нему:

> public ArrayList<IDirectFire> getDirectFireSublist() {
>      return entities.stream().filter((it) -> (it instanceof IDirectFire)).collect(Collectors.toList());
> }
person Alexander Petrov    schedule 17.07.2016
comment
Большое спасибо! Я не был уверен, насколько сильно это повлияет на производительность. - person paxikek; 17.07.2016

Теперь к моему вопросу: следует ли мне дальше работать с этим методом или создать новый ArrayList, который существует помимо «сущностей» и который мне нужно будет вручную обновлять каждый раз при изменении «сущностей»?

По какой причине вы хотите дублировать данные «entites»?

1) Вы можете поместить их только в специальный список. В этом случае вам больше не нужен getDirectFireSublist().

2) Вы можете поделиться ими между двумя списками, не дублируя их. В этом случае необходимо обновить добавленный и удаленный элемент сущности, поскольку будут обновлены только измененные элементы. Но это довольно просто реализовать.

person davidxxx    schedule 17.07.2016
comment
Новый список будет не дубликатом, а отфильтрованными «сущностями» в виде отдельного списка, так что да, я имел в виду выделенный список. Спасибо! - person paxikek; 17.07.2016

wakjah упоминает в комментарии, что instanceof немного пахнет дизайном. Имея это в виду, одним из альтернативных решений может быть использование шаблона «Посетитель».

public abstract class Entity {
    public abstract void acceptVisitor(EntityVisitor visitor);
    ...
}

public interface IDirectFire {
    default acceptVisitor(EntityVisitor visitor) {
        visitor.visit(this);
    }
    ...
}

public class ResetFireStatusVisitor implements EntityVisitor {
    public void visit(IDirectFire directFireEntity) {
        directFireEntity.dfHasFired(false);
    }
}

Затем, чтобы перебрать элементы:

entities.forEach(entity -> entity.acceptVisitor(new ResetFireStatusVisitor()));

ResetFireStatusVisitor вызывает dfHasFired(false) для всего, что реализует IDirectFire. В EntityVisitor вы можете указать реализации no-op по умолчанию для других подтипов Entity.

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

person Sam    schedule 17.07.2016
comment
Могу я спросить, почему instanceof - это запах дизайна? Однозначно вижу полезность посетителя, просто любопытно. Экземпляр больше подвержен ошибкам? Большое спасибо! - person paxikek; 17.07.2016
comment
Слишком большая предметная область, чтобы охватить ее в комментарии, но там много дискуссий. Вот главный результат поиска: stackoverflow.com/questions/20589590/ - person Sam; 17.07.2016
comment
Да, извините, очевидно, что это большая тема, я поищу. Спасибо за ссылку и вашу помощь! - person paxikek; 17.07.2016