Наиболее эффективный способ поиска коллекции всех идентификаторов в коллекции объектов

У меня есть сущность:

public class Entity
{
    private long id;    
    private String data;

    public long getId() {
        return id;
    }

    public String getData() {
        return data;
    }
}

и набор объектов:

Collection<Entity> entities= ...

Каков наиболее эффективный способ найти Collection<Long> всех идентификаторов в entities?

Ответ 1

Предполагая, что у вас

class Entity {
    final long id;
    final String data;

    public long getId() {
        return id;
    }

    public String getData() {
        return data;
    }

    Entity(long id, String data) {
        this.id = id;
        this.data = data;
    }
}

В Java 8 вы можете написать

Collection<Entity> entities = Arrays.asList(new Entity(1, "one"), 
                  new Entity(11, "eleven"), new Entity(100, "one hundred"));
// get a collection of all the ids.
List<Long> ids = entities.stream()
                         .map(Entity::getId).collect(Collectors.toList());

System.out.println(ids);

печатает

[1, 10, 100]

Как вы можете себе представить, это довольно уродливо на Java 7 или меньше. Обратите внимание, что Entity.getId, когда применяется к map(), означает вызов этого метода для каждого элемента.

Теперь, реальная интересная часть, вы можете это сделать.

List<Long> ids = entities.parallelStream()
                         .map(Entity::getId).collect(Collectors.toList());

В большинстве случаев использование параллельного потока может повредить производительность, но оно делает попытку и видит удивительно легкую (возможно, слишком легкую;)


Самый эффективный способ - создать или создать карту.

Map<Long, Entity> entitiesMap = ...
// get all ids
Collection<Long> addIds = entitiesMap.keySet();

// look up entities by id.
List<Long> ids = ...
List<Entity> matching = new ArrayList<>();
for(Long id: ids)
    matching.add(entitiesMap.get(id));

Ответ 2

Вы не получите ничего короче, чем:

Collection<Long> ids = new ArrayList<>();
for (Entity e : entities) ids.add(e.getId());

Я предполагаю, что все пути будут перебирать по коллекции

Не обязательно. Это создает коллекцию, которая напрямую поддерживается базой объектов (будущие изменения в коллекции объектов отображаются в коллекции идентификаторов):

Collection<Long> ids = new AbstractCollection<Long>() {
    @Override
    public int size() {
        return entities.size();
    }

    @Override
    public Iterator<Long> iterator() {
        return new Iterator<Long>() {
            private Iterator<Entity> base = entities.iterator();
            @Override public boolean hasNext() { return base.hasNext(); }
            @Override public Long next() { return base.next().getId(); }
            @Override public void remove() { base.remove(); }
        };
    }
};

Ответ 3

Самый эффективный? В основном просто перебирайте и добавляйте в список. Вы должны посмотреть на каждый предмет.

Collection<Long> ids = new LinkedList<Long>();
for (Entity e : entities) {
    ids.add(e.id);
}

Или, если вы можете использовать Java 1.8, вы можете сделать что-то вроде:

entities.forEach((e) -> ids.add(e.id));

Ответ 4

Я не знаю, является ли это наиболее эффективным, но для pre-Java 8 я стал использовать интерфейсы свойств, как описано здесь: http://blog.cgdecker.com/2010/06/property-interfaces-and-guava.html

Как описано в сообщении в блоге, у вас будет простой интерфейс с именем HasId:

public interface HasId {
    long getId();
}

Класс Entity будет выглядеть следующим образом:

public class Entity implements HasId {
    private long id;    
    private String data;

    public long getId() {
        return id;
    }

    public String getData() {
        return data;
    }
}

и у вас будет простая функция вроде этого где-то:

public class ToId implements Function<HasId, Long> {
    public Long apply(HasId hasId) {
        return hasId.getId();
    }
}

Наконец, чтобы использовать его:

Collection<Long> ids = Collections2.transform(entities, new ToId());

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

Ответ 5

Вместо преобразования списка в поток, а затем обратно в список

Я буду рекомендовать ниже

Идентификаторы коллекции = new LinkedList(); entity.forEach((e) → ids.add(e.id));