【问题标题】:Implementing efficient data structure using Arrays only仅使用数组实现高效的数据结构
【发布时间】:2015-12-09 22:29:07
【问题描述】:

作为我编程课程的一部分,我得到了一个练习来实现我自己的字符串集合。我计划使用 ArrayList 集合或类似的集合,但其中一个限制是我们不允许使用任何 Java API 来实现它,因此只允许使用数组。我本可以使用数组来实现这一点,但是效率以及测试此代码的数据量非常重要。有人建议我使用哈希表或有序树,因为它们比数组更有效。在做了一些研究之后,我决定使用哈希表,因为它们看起来很容易理解和实现,但是一旦我开始编写代码,我意识到它并不像我想象的那么简单。

以下是我提出的问题,并希望得到一些建议,了解什么是再次解决这些问题的最佳方法,同时考虑到效率:

  1. 实际大小:如果我理解正确,哈希表没有排序(索引),这意味着项目之间会有间隙,因为哈希函数给出了不同的索引。那么我如何知道数组何时已满,我需要调整它的大小?
  2. 调整大小:我需要使用数组创建动态数据结构的困难之一。因此,如果我有一个数组 String[100],一旦它变满,我将需要调整它的大小,我决定每次将其增加 100,所以一旦我这样做,我需要更改所有现有值的位置,因为它们哈希键在计算时会有所不同:
int position = "orange".hashCode() % currentArraySize;

因此,如果我尝试找到某个值,它的哈希键将与数组较小时的不同。

  1. HASH FUNCTION:我还想知道 String 类中的内置 hashCode() 方法是否高效且适合我要实现的功能,或者创建自己的方法是否更好。
  2. 处理多次出现:要求之一是能够添加多个相同的单词,因为我需要能够计算该单词在我的集合中存储的次数。由于它们将具有相同的哈希码,因此我计划在下一个索引处添加下一个匹配项,希望会有一个间隙。我不知道这是否是最好的解决方案,但这里我是如何实现它的:
public int count(String word) {
    int count = 0;
    while (collection[(word.hashCode() % size) + count] != null && collection[(word.hashCode() % size) + count].equals(word))
        count++;
    return count;
}

提前感谢您的建议。请问有什么需要澄清的。

附:字长不固定,变化很大。

更新感谢您的建议,我知道我确实犯了一些愚蠢的错误,我会努力做得更好。所以我采纳了你的所有建议并迅速提出了以下结构,它并不优雅,但我希望它是你大致的意思。我确实不得不做出一些判断,例如桶大小,现在我将元素的大小减半,但是有没有办法计算或一些通用值?另一个不确定因素是通过什么因素来增加我的数组,我应该乘以某个 n 数还是添加固定数也适用?另外我想知道一般效率,因为我实际上是在创建类的实例,但是 String 是一个类,所以我猜性能差异应该不会太大?

【问题讨论】:

标签: java arrays hash


【解决方案1】:

实际大小:内置 Java HashMap 仅在元素总数超过桶数乘以称为负载因子的数字(默认为 0.75)时调整大小。它确实考虑有多少桶实际上已满。您也不必这样做。

调整大小:是的,当调整表大小时,您必须重新散列所有内容,其中包括重新计算其散列。

因此,如果我尝试找到某个值,它的哈希键将与数组较小时的不同。

是的。

哈希函数:是的,您应该使用内置的hashCode() 函数。对于基本用途来说已经足够了。

处理多个事件:这很复杂。一个简单的解决方案就是让给定字符串的哈希条目也记录该字符串出现的次数。也就是说,不要在哈希表中保留同一字符串的多个副本,而是保留一个 int 以及每个 String 计算其出现次数。

【讨论】:

  • 感谢您的回答!但是确定桶大小的方法是什么?
  • 你不是在确定桶的大小,你只是在确定桶的数量,这只是你在哈希表中使用的数组的大小。
【解决方案2】:

那么我怎么知道数组何时满了,我需要调整它的大小?

您会跟踪大小,而 HashMap 会。当size used > capacity * load factor 时,您会扩展底层数组,无论是整体还是部分。

int position = "orange".hashCode() % currentArraySize;

需要考虑的一些事项。

  • 负值的%是负值。
  • Math.abs 可以返回负值。
  • & 与位掩码一起使用会更快,但您需要的大小是 2 的幂。

我还想知道 String 类中的内置 hashCode() 方法是否高效且适合我正在尝试实现的内容,或者创建自己的方法是否更好。

内置的hashCode是缓存的,所以速度很快。然而,它不是一个很好的哈希码,低位随机性很差,短字符串的随机性高。您可能想要实现自己的散列策略,可能是 64 位的。

处理多个事件:

这通常通过每个键的计数器来完成。这样,您可以说相同键/元素的 32767 个重复项(如果您使用 short)或 20 亿个(如果您使用 int)重复项。

【讨论】:

  • "您可能想要实现自己的散列策略,可能是 64 位的。"真的吗?这家伙不知道如何判断数组已满......
  • @Dima 是的,他/她只是“意识到这并不像我想象的那么简单。”并且甚至没有询问线性寻址与链接列表或值树,但会学习并且理解这意味着什么的人可能会阅读答案。
  • @PeterLawrey 感谢您的回答。我认为有些事情超出了本练习的范围和我目前的知识范围,但我会在未来研究这些主题。 HashMap 是否也为每个元素保留一个计数器?因此,如果我删除一个出现 3 次的元素,我只需减少计数器,直到它只有 1 才能完全删除它?
  • @AlenEviLL HashMap 为每个键保留一个计数器。它不允许重复,因此您需要为键的实例数保留一个计数器。在任何情况下,它都会计算唯一键的数量。
猜你喜欢
  • 2015-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多