Hashmap或ArrayList如果需要迭代和随机访问元素?

时间:2015-07-27 17:41:54

标签: java arraylist hashmap

我有一堆商店:

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中可能更好。

1 个答案:

答案 0 :(得分:3)

使用HashMap - 这显然是您需要的抽象,因此它是最佳选择。 HashMap上的迭代按每个元素的O(1)顺序,O(n)对整个映射进行总迭代(注意nHashMap的容量,而不是ArrayList的容量。它的大小!)。您也可以使用LinkedHashMap(由Peter Lawrey建议),但请注意:

  

由于维护链表的额外费用,性能可能略低于HashMap的性能,但有一个例外:对LinkedHashMap的集合视图进行迭代需要与地图大小成比例的时间,无论如何它的能力。对HashMap的迭代可能更昂贵,需要与其容量成比例的时间。

简而言之 - 它使迭代速度稍微快一些,同时使其他操作稍慢。更多的是IMO过早的操作。

但是,如果你需要的每一点速度,那么数据就是静态的(即只创建一次[元素添加]集合,并且多次使用[迭代,检查包含] ),你不介意使用大约2倍的内存 - 你可以使用两者,添加两者,并使用数组/ HashMap进行迭代,使用ArrayList进行查找。我不建议将它用于休闲用途,因为它使代码更难以阅读和保持,因为它很可能违反Single Responsibility Principle。如果您的目标是使用它,IMO最好编写一个合成类,将Map的迭代器与{{1}}接口的方法并行显示。

至于在对象中存储名称及其冗余 - 您只存储对密钥的引用,而不是密钥本身。因此,您的“浪费”(在大多数情况下不是真正的浪费,请注意我)每个收集项目大约4个字节。除非您打算拥有数十亿元素的集合,否则这不是问题。 OTOH,问问自己为什么要在商店实例中存储商店的名称?如果您希望能够在键(商店名称)和商店[能够通过名称获得商店并知道每个商店的名称]之间存在双向关系 - 您必须将名称存储在对象,或使用第二个地图。在大多数情况下,前者比后者更好(这里更多的是适当抽象而不是内存/ CPU)。因此,将密钥复制到对象中通常是处理它的最简单和最明显的方法。