在一个实体集合中找到所有ID集合的最有效方法

36

我有一个实体:

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

    public long getId() {
        return id;
    }

    public String getData() {
        return data;
    }
}

和一组实体:

Collection<Entity> entities= ...

如何最有效地找到entities中所有id的Collection<Long>


请澄清一下,“最有效率”是指“最快”还是“最短”? - Boann
@Boann,因为我认为所有的方法都会遍历集合...在这种情况下,我指的是“最短”。 - rapt
6个回答

83

假设您已经拥有

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));

当然,但是在GuavaApache Commons中是否有一个能够为我完成这个任务的工具? - rapt
@rapt 如果没有闭包,使用库会比编写简单的循环更需要代码。你能使用Java 8吗? - Peter Lawrey
1
在这种情况下,忘记Guava和Apache Commons,直接使用流会更容易。你有一个Id的getter吗? - Peter Lawrey
2
在Java 7中,您将使用循环。仅由于Java 8的语法糖,它才有些意义。请注意:循环仍然是更短的代码。 - Peter Lawrey
1
等价于 .map(e -> e.getId()).collect(..)。 - Míra
显示剩余4条评论

5

最有效的方法?基本上只需要迭代并将其添加到列表中。您必须查看每个项目。

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

或者,如果您可以使用Java 1.8,您可以这样做:

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

1
最好使用 ArrayList 并指定其大小(即 entities.size())。 - arshajii
3
entities.forEach((e) -> ids.add(e))ids.addAll(entries) 相同。 - Peter Lawrey
抱歉,我的意思是ids.add(e.id)。 - kmera

3

您不可能得到比这更短的东西了:

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

我假设所有的方式都会迭代整个集合。
但事实并非如此。这会创建一个直接由底层实体集合支持的集合(未来对实体集合的更改将出现在ids集合中)。
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(); }
        };
    }
};

1
我不知道这是否是最有效的方法,但在Java 8之前,我喜欢使用属性接口,如此处所述:http://blog.cgdecker.com/2010/06/property-interfaces-and-guava.html 正如博客文章中所述,您可以拥有一个简单的接口,命名为HasId:
public interface HasId {
    long getId();
}

您的实体类将如下所示:

Your Entity class would look like this:

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或其他接口的对象,我发现使用它非常愉快。


0

不要将列表转换为流再转回列表

我建议使用以下代码:

Collection ids = new LinkedList(); entities.forEach((e) -> ids.add(e.id));


0

我们也可以尝试这种方法

List<Long> ids = new ArrayList<>();
entities.Stream().map(entity->ids.add(entity.getId()));

您正在回复一个旧问题,该问题已经有了一个被接受的答案。 - Stefano Groenland

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接