在Java中,我正在创建一组对象(相同类型),每个对象都包含一个名为name
的字符串字段。集合,对象及其name
都是在构造函数中创建的,永远不会被更改。我希望能够轻松找到具有给定name
的对象。
class Program {
final Collection<Foo> foos;
Program() {
foos = new HashSet<>(); // Note: I'm willing to use another type of collection
foos.add(new Foo("First", 7));
foos.add(new Foo("Qwerty", 4));
}
get(String name) {
// how?
}
}
class Foo {
final String name;
int size;
Foo(String name, int size) {
this.name = name;
this.size = size;
}
}
我可以想到用给定name
获取Foo的几种方法。我可以创建一个Map<String,Foo>
,但这似乎使用了比实际需要更多的内存(每个String必须在内存中重复,并且有一个全新的数据结构)。或者,我可以做一个简单的foreach循环,但这是O(n)效率,我正在寻找O(1)或接近它。
答案 0 :(得分:2)
首先,正如评论the implementation of HashSet
actually uses a HashMap
中所指出的那样,所以不要指望该集合具有更高的内存效率。
其次,您声称在使用地图时必须复制String
个实例。这不是真的。 Java通过引用而不是通过值传递非基本类型的实例,因此如果您创建这样的映射:
map.put(foo.getName(), foo);
String
name
Foo
字段指向的map.put("alice", new Foo("alice"));
字段将与地图的键指向的完全相同的实例。你不是在浪费任何记忆。另请注意the JVM will reuse string literals,所以即使是这样的事情:
Collection
不会导致字符串在内存中重复。一般来说,你应该小心做出这样的太多假设,因为不仅JVM,而且编译器可能会在你背后做出很多优化。专注于功能和可读性,如果发现瓶颈,则稍后进行优化。 *插入关于过早优化的陈词滥调。*
最后,get
接口不提供Set
方法,因为它没有任何意义。集合就是一个对象的集合。它对它们一无所知,只是“收集”它们。从任意集合中检索对象与要求某人打开未知物品的抽屉并取出其中的蓝色物体是相同的。所述人必须查看每个项目才能找到蓝色项目,但可能会有多个蓝色内容,因此不仅需要线性时间才能找到蓝色内容,因此可能还会有重复项(不在{{1}中)但是,如果是这样,你可能不知道你得到了什么蓝色。
现在,另一方面Map
提供了一种将某种唯一标识符与其“收集”的每个项目相关联的方法(请注意Map
不是但是Collection
。这正是您尝试在Program
课程中手动实现的内容。您需要一组Foo
个对象,并且您希望按名称唯一标识它们。这就是地图的用途。
请注意,如果您想支持重复项,只需使用给定名称某些 Foo
,那么您正在寻找 multimap ,您可以制作通过将名称映射到Foo
个对象的列表来实现非常粗略的实现(请注意,您必须自己进行必要的空检查并列出实例化):
Map<String,List<Foo>> map = ...;
如果您坚持使用Collection
,则必须迭代它以检索对象(因为Collection
接口不保证迭代顺序)。但是,一般情况下,在需要成员资格测试的情况下使用集合(我有这个Foo
,它是否存在于我的集合中?),以及需要检索对象的情况的映射给定一个唯一的标识符。