2011-03-23 27 views
1

举个简单的例子,考虑一个Place类:(Java)使用Maps时,键和值是否有相同的类型?

public class Place { 

    //fields 
    private String name; 

    private String state; 

    private int population; 

    private int squareMileage; 

    private int elevation; 

    //constructors 
    public Place() {  
    } 

    public Place(String name, String state) { 
     this.name = name; 
     this.state = state; 
    } 

    public Place(String name, String state, int population, int squareMileage, 
        int elevation) { 
     this.name = name; 
     this.state = state; 
     this.population = population; 
     this.squareMileage = squareMileage; 
     this.elevation = elevation; 
    } 

    //getters and setters 
    public String getName() { 
     return this.name; 
    } 

    public String getState() { 
     return this.state; 
    } 
    //... (other getters and setters omitted) 

    //do stuff 
}

而一个Places类(PlaceHashMap一个对象):

import java.util.*; 

public class Places { 
    private Map searchablePlaces; 

    public Places() { 
     searchablePlaces = new HashMap(); 
    } 

    public void add(Place value) { 
     Place key = new Place(value.getName(), value.getState()); 
     searchablePlaces.put(key, value); 
    } 

    public Place find(Place key) { 
     return searchablePlaces.get(key); 
    } 

    //override hashCode, equals 

    //do stuff 
}

从本质上讲,我的问题是:

  1. 会有效率吗?,而不是直接搜索value,比方说,排序的ArrayList
  2. 如果key类型String等于name + state(或String[2]类型)会怎么样?
+1

地图的参数在'add'和'find'方法中是不同的。将''存储在'add'中,但尝试将'Place'作为''获取到'find'中。 – adarshr 2011-03-23 23:10:18

+0

@adarshr我用2个字符串来构建一个'Place'对象,然后添加'Place'和一个作为arg传递给'add'方法的对象,所以它应该是''。 – TheBlackKeys 2011-03-23 23:51:24

回答

4

它令人困惑,恕我直言。我将定义一个PlaceKey类,只保存一个名称和一个状态并定义hashCode和equals。一个地方可以容纳一个PlaceKey和其他属性。我会使用Map<PlaceKey, Place>

请注意,在您的示例中,Place类需要equals和hashCode,而不是Places类。

现在回答你两个问题:

  1. 哈希映射为O(1),和一个列表为O(n)。因此地图通常会更快(除非是非常小的n值)
  2. 连接几乎总是一个坏主意。你可能有name1 = aa,state1 = a,name2 = a和state2 = aa,这会导致冲突。一个数组在内容方面不会覆盖equals和hashCode,并且不是一个合适的映射关键类。
+0

那么,根据实现(如TreeMap),地图也可以是O(log n):) – Thomas 2011-03-23 23:20:04

+0

我非常喜欢这个想法。感谢您的答复。 – TheBlackKeys 2011-03-23 23:22:58

+0

@Thomas:你说得对。我编辑答案来讨论哈希映射而不是简单的映射。 – 2011-03-23 23:28:08

3

会不会有在寻找关键的HashMap中获得的任何效率,直接而不是寻找价值,比方说,一个有序的ArrayList?

是的。比较字符串比比较整数要慢(而且比较字符串也很少)。

如果您使用自定义对象的键,您应该谨慎实施hashCode与最常用的可能值。您还应该精简一下equals

自定义类对name + state的好处是您可以考虑可能的值。

+2

此外,它执行直接查找和可能的短序列搜索更快,而不是迭代到列表中的许多对象,即HashMap查找具有O(1)成本,而ArrayList(或任何其他列表)查找具有O(n)成本。 – Thomas 2011-03-23 23:17:46

+0

这并不像比较哪种类型更快。在一般情况下,散列总是比线性或二分搜索更快,无论哪种类型进行比较(在合理范围内)。当然,如果你决定通过做一些愚蠢的事情来搜索地图中的一个键,比如迭代keySet,那么你就完全失去了使用哈希表的好处。 – aroth 2011-03-23 23:22:21

+0

@aroth除非在非常不可能的情况下产生完全相同的'hashCode',否则它会更快。 - 这可能是由于不小心编写的自定义密钥对象(例如,将hashCode放在状态的前几个字母上),在这种情况下会出现大量相同的代码。 – vbence 2011-03-23 23:29:53

0

通过ArrayList访问对象比通过HashMap的键访问对象要快得多,但指向该对象的指针必须存储在内存中的连续块中,这会使删除对象更慢并将其放入ArrayList 。因此,决定哪种类型更好取决于您在执行程序期间要执行的操作。

例如,如果您需要放置和/或删除地方很多次,使用HashMap会更好,因为ArrayList不够高效。如果你的位置或多或少是固定的,你必须很少移动它们,那么ArrayList会给你更多的性能。

如果您定义了使用HashMap,则使用唯一字符串作为键会更快。

+1

OP“直接在... ArrayList中搜索值” - 这将涉及大量基于“equals”的比较。元素的访问成本并不是这里最大的问题。 – vbence 2011-03-23 23:20:58

+0

访问对象可能会更快,但在列表中找不到对象。 – Thomas 2011-03-23 23:21:29

0

使用另一个(未完全填充的)Place对象作为关键字至少会浪费内存。通常你用一个唯一的标识符搜索。有几种可能性获得一个:

  1. 如果要使用它们进行搜索,请使用(名称+ SEPARATOR +状态)作为键。
  2. 使用几个地图,每个(可搜索)属性 - 如果它不是唯一的,你会连接一个列表。
  3. 使用对象的(cryptogrphic)哈希关键 ...
0
  1. 这取决于你如何搜索HashMap。例如,如果使用containsKey(),那么地图的搜索速度将比搜索列表快一个数量级。在典型情况下,检查散列表中的密钥是在一定的时间内发生的(O(1))。如果使用二进制搜索,则可以在O(log n)时间内为排序的数组搜索密钥。但是,如果您通过迭代密钥集(不保证按任何顺序排序)来搜索密钥,那么您将执行一个O(n)操作,这比搜索排序列表要慢。

    短版?使用containsKey()将提供最佳性能。

  2. 你可以这样做,但为什么使用Map?为什么不是ListSetMap的要点是将密钥与值关联起来。如果你没有这样做(并且示例代码并非如此),那么你的任务使用了错误的数据结构。

+0

谢谢你的提示,那么我肯定会使用'containsKey'来提高效率。关于#2,我有点困惑。我喜欢@JB Nizet提出的将'PlaceKey'类定义为键类型的建议,但是(尽管效率可能更低),但使用'String'(仅包含相关搜索项)作为' Map'。你介意澄清一下吗?这不会比搜索“List”或“Set”更有效吗? – TheBlackKeys 2011-03-24 00:15:18

相关问题