扩展HashMap以获取具有指定哈希码的所有对象?

时间:2016-04-13 21:58:43

标签: java hashmap hashcode

根据我的理解,当两个对象放在具有相同哈希码的HashMap中时,它们被放入具有相同哈希码的对象的LinkedList(我认为)中。我想知道是否有办法扩展HashMap或操纵现有方法返回共享哈希码的对象列表或数组,而不是进入等于查看它们是否是同一个对象。

原因是我正在尝试优化代码的一部分,当前这只是一个while循环,它找到带有该哈希码的第一个对象并存储/删除它。如果我可以一次性返回完整列表,这将会快得多。

以下是我要替换的代码:

    while (WorkingMap.containsKey(toSearch)) { 
        Occurences++;
        Possibles.add(WorkingMap.get(toSearch));
        WorkingMap.remove(toSearch);
    }

键是Chunk对象,值是字符串。以下是Chunk类的hashcode()和equals()函数:

 /**
 * Returns a string representation of the ArrayList of words
 * thereby storing chunks with the same words but with different
 * locations and next words in the same has bucket, triggering the
 * use of equals() when searching and adding
 */
public int hashCode() {
    return (Words.toString()).hashCode();
}

@Override
/**
 * result depends on the value of location. A location of -1 is obviously
 * not valid and therefore indicates that we are searching for a match rather
 * than adding to the map. This allows multiples of keys with matching hashcodes 
 * to be considered unequal when adding to the hashmap but equal when searching
 * it, which is integral to the MakeMap() and GetOptions() methods of the 
 * RandomTextGenerator class.
 * 
 */
public boolean equals(Object obj) {
    Chunk tempChunk = (Chunk)obj;
    if (LocationInText == -1 && Words.size() == tempChunk.GetText().size())
    {
        for (int i = 0; i < Words.size(); i++) {
            if (!Words.get(i).equals(tempChunk.GetText().get(i))) {
                return false;
            }
        }
        return true;
    }
    else {
        if (tempChunk.GetLocation() == LocationInText) {
            return true;
        }
        return false;
    }
}

谢谢!

3 个答案:

答案 0 :(得分:3)

HashMap没有任何方式可以做到这一点,但我认为你首先误解了HashMap的工作方式。

您需要知道的第一件事是,如果每个对象具有完全相同的哈希码,HashMap仍然有效。它永远不会“混淆”键。如果您致电get(key),则只会返回与key相关联的值。

这样做的原因是HashMap仅使用hashCode作为第一个分组,但是然后它会根据密钥检查您传递给get的对象使用.equals方法存储在地图中。

从外部无法告诉HashMap使用链接列表。 (事实上​​,在更新版本的Java中,它总是使用链表。)实现没有提供任何查看哈希码的方法,以找出哈希码是如何分组的或者沿着这些方向的任何事情。

while (WorkingMap.containsKey(toSearch)) { 
    Occurences++;
    Possibles.add(WorkingMap.get(toSearch));
    WorkingMap.remove(toSearch);
}

此代码不“找到具有该哈希码的第一个对象并存储/删除它”。它根据toSearch找到唯一的对象等于 .equals,存储并删除它。 (Map中只能有一个这样的对象。)

答案 1 :(得分:0)

你的时间不是真的。如果WorkingMap是普通的Java HashMap,它最多转一圈。 .get(key)返回HashMap上保存的'key'中最后保存的对象。如果它与toSearch匹配,则与之前相匹配。

我不确定这里有多少未解决的问题。但如果您需要那个,而您的更远的代码就是理解

class Possibles是什么类型的? ArrayList

 // this one should make the same as your while
        if(WorkingMap.containsKey(toSearch)) {
           Possibles.add(WorkingMap.get(toSearch));
           WorkingMap.remove(toSearch);
        }

// farher: expand your Possibles to get that LinkedList what you want to have. 
    public class possibilities {

            // List<LinkedList<String>> container = new ArrayList<LinkedList<String>>();
            public Map<Chunk, LinkedList<String>> container2 = new HashMap<Chunk, LinkedList<String>>();

            public void put(Chunk key, String value) {
                if(!this.container2.containsKey(key)) {
                    this.container2.put(key, new LinkedList<String>());
                }
                this.container2.get(key).add(value);
            }

        }
    // this one works with updated Possibles
        if(WorkingMap.containsKey(toSearch)) {
           Possibles.put(toSearch, WorkingMap.get(toSearch));
           WorkingMap.remove(toSearch);
        }
//---

但是,它可以这样,但键不应该是一个复杂的对象。

注意:LinkedLists占用内存,块有多大?检查内存使用情况

Possibles.(get)container2.keySet();

好看的

答案 2 :(得分:0)

  

根据我的理解,当两个对象放在具有相同哈希码的HashMap中时,它们被置于具有相同哈希码的对象的LinkedList中(我认为)。

是的,但它比这复杂得多。它通常需要将对象放在链表中,即使它们具有不同的哈希码,因为它只使用哈希码的某些位来选择存储对象的桶;它使用的位数取决于内部哈希表的当前大小,这大致取决于映射中的事物数量。当一个存储桶需要包含多个对象时,如果可能的话,它也会尝试使用像TreeMap这样的二叉树(如果对象是互相Comparable),而不是链接列表。

反正.....

  

我想知道是否有办法扩展HashMap或操纵现有方法来返回共享哈希码的对象列表或数组,而不是进入等于查看它们是否是同一个对象。

没有

HashMap根据equals方法比较相等的键。根据{{​​1}}方法的等同是唯一有效的方式来设置,替换或检索与特定键相关联的值。

是的,它还使用equals作为在结构中排列对象的方法,该结构允许更快地定位可能相等的对象。不过,匹配键的the contract是根据hashCode定义的,而不是equals

请注意,将每个hashCode方法实现为hashCode是完全合法的,并且地图仍然可以正常工作(但速度非常慢)。因此,任何涉及获取共享哈希码的对象列表的想法都是不可能的或无意义的,或者两者兼而有之。

我不能100%确定您使用return 0;变量在equals方法中执行的操作,但它看起来很危险,因为它违反了{{}的合同1}}方法。 必需 equals method是对称的,传递的和一致的:

  
      
  • 对称:对于任何非空引用值LocationInTextequals,当{且仅x返回true时,y应返回true
  •   
  • 传递:对于任何非空参考值x.equals(y)y.equals(x)x,如果y返回true且{{1返回true,然后z应返回true。
  •   
  • 一致:对于任何非空引用值x.equals(y)y.equals(z)x.equals(z)的多次调用始终返回true或始终返回false,前提是没有信息用于x对象的比较被修改。
  •   

hashCode method必须始终同意y关于同等对象:

  
      
  • 如果两个对象根据x.equals(y)方法相等,则对两个对象中的每一个调用equals方法必须生成相同的整数结果。
  •   

equals变量正在严重破坏这些规则,可能会破坏事物。如果不是今天,那么有一天。 摆脱它!

  

以下是我要替换的代码:

equals(Object)

跳出来的东西是你只需要进行一次密钥查找,而不是做三次,因为hashCode返回删除的值,如果密钥不存在则返回null:

LocationInText

无论哪种方式,循环仍然有问题,因为地图应该不可能包含多个等于 while (WorkingMap.containsKey(toSearch)) { Occurences++; Possibles.add(WorkingMap.get(toSearch)); WorkingMap.remove(toSearch); } 的密钥。我不能夸大Map.remove变量,因为你使用它并不是一个好主意。

我同意其他评论者看起来你正在寻找列表地图结构。像Guava这样的Java库为此提供Multimap,但您可以非常轻松地手动完成。我认为你想要的声明是:

for (;;) {
    String s = WorkingMap.remove(toSearch);
    if (s == null) break;
    Occurences++;
    Possibles.add(s);
}

要向地图添加新的块 - 字符串对,请执行以下操作:

toSearch

如果块是新的,那么该方法在地图中放置一个新的LocationInText,或者如果该块有一个,则取出现有的Map<Chunk,List<String>> map = new HashMap<>(); 。然后它将字符串添加到它获取或创建的列表中。

要检索特定块值的所有字符串列表,只需void add(Chunk chunk, String string) { map.computeIfAbsent(chunk, k -> new ArrayList<>()).add(string); } 即可,您可以将ArrayList列表添加为ArrayList

我指出的其他潜在优化:

  • map.get(chunkToSearch)方法中,考虑缓存哈希码,而不是每次调用方法时重新计算哈希码。如果Possibles是可变的(这对于地图密钥来说不是一个好主意,但只要你小心谨慎地允许),那么只有在Possibles.addAll(map.get(chunkToSearch));&#39;之后才重新计算哈希码。 s值已经改变。此外,如果Chunk.hashCode似乎是Chunk,则使用其哈希码可能会比将其转换为字符串并使用字符串的哈希码更快,但是我不确定。

  • Chunk方法中,如果实例相同(通常是实例),则可以立即返回Words。另外,如果List返回数据副本,则不要调用它;您可以访问其他Chunk.equals的私人true列表,因为您在同一个班级,最后,您可以按照GetText方法进行操作:

    Words

    简单!快!