我有一堆商店:
public class Shop {
private final String shopName;
private boolean shopProperty1;
private boolean shopProperty2;
}
现在有时我需要通过shopName检索Shop,有时我需要对所有现有商店执行操作。
使用ArrayList
List<Shop> shops = new ArrayList<>();
Shop shop1 = new Shop("Megastore", false, true);
Shop shop2 = new Shop("PC-shop", true, true);
Shop shop3 = new Shop("Jim's junkyard", false, false);
shops.add(shop1);
shops.add(shop2);
shops.add(shop3);
迭代:
for (Shop shop : shops) {
doOperation(shop);
}
通过shopName检索Megastore:
Shop retrieved;
for (Shop shop : shops) {
if ("Megastore".equals(shop.getShopName())) {
retrieved = shop;
break;
}
}
我对使用这种方法的担忧:
按名称检索似乎相当慢,ArrayList和HashMap在那里会好得多。
使用HashMap
Map<String, Shop> shops = new HashMap<>();
Shop shop1 = new Shop("Megastore", false, true);
Shop shop2 = new Shop("PC-shop", true, true);
Shop shop3 = new Shop("Jim's junkyard", false, false);
shops.put(shop1.getShopName(), shop1);
shops.put(shop2.getShopName(), shop2);
shops.put(shop3.getShopName(), shop3);
迭代:
for (Shop shop : shops.values()) {
doOperation(shop);
}
通过shopName检索Megastore:
Shop retrieved = shops.get("Megastore");
我对使用这种方法的担忧:
当shopName已经是Shop的一个字段时,将shopName作为键是多余的。另外,我不知道HashMap的设计是如何被迭代完成的。
所以问题是:哪种方法更好的设计实践还是更好的方法?程序员通常如何处理这种情况?
不是When to use HashMap over LinkedList or ArrayList and vice-versa的副本,因为这解释了这些方法的潜在问题。但在codereview中可能更好。
答案 0 :(得分:3)
使用HashMap
- 这显然是您需要的抽象,因此它是最佳选择。 HashMap
上的迭代按每个元素的O(1)顺序,O(n)对整个映射进行总迭代(注意n
是HashMap
的容量,而不是ArrayList
的容量。它的大小!)。您也可以使用LinkedHashMap(由Peter Lawrey建议),但请注意:
由于维护链表的额外费用,性能可能略低于HashMap的性能,但有一个例外:对LinkedHashMap的集合视图进行迭代需要与地图大小成比例的时间,无论如何它的能力。对HashMap的迭代可能更昂贵,需要与其容量成比例的时间。
简而言之 - 它使迭代速度稍微快一些,同时使其他操作稍慢。更多的是IMO过早的操作。
但是,如果你需要的每一点速度,那么数据就是静态的(即只创建一次[元素添加]集合,并且多次使用[迭代,检查包含] ),你不介意使用大约2倍的内存 - 你可以使用两者,添加两者,并使用数组/ HashMap
进行迭代,使用ArrayList
进行查找。我不建议将它用于休闲用途,因为它使代码更难以阅读和保持,因为它很可能违反Single Responsibility Principle。如果您的目标是使用它,IMO最好编写一个合成类,将Map
的迭代器与{{1}}接口的方法并行显示。
至于在对象中存储名称及其冗余 - 您只存储对密钥的引用,而不是密钥本身。因此,您的“浪费”(在大多数情况下不是真正的浪费,请注意我)每个收集项目大约4个字节。除非您打算拥有数十亿元素的集合,否则这不是问题。 OTOH,问问自己为什么要在商店实例中存储商店的名称?如果您希望能够在键(商店名称)和商店[能够通过名称获得商店并知道每个商店的名称]之间存在双向关系 - 您必须将名称存储在对象,或使用第二个地图。在大多数情况下,前者比后者更好(这里更多的是适当抽象而不是内存/ CPU)。因此,将密钥复制到对象中通常是处理它的最简单和最明显的方法。