Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/369.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 支持范围查询的可扩展多维数据结构_Java_Data Structures - Fatal编程技术网

Java 支持范围查询的可扩展多维数据结构

Java 支持范围查询的可扩展多维数据结构,java,data-structures,Java,Data Structures,让我首先提出一个问题:考虑到我将进一步描述的情况和需求,哪些数据结构有助于实现非功能性需求 我试图查找几个结构,但到目前为止还不是很成功,这可能是因为我遗漏了一些术语 因为我们将在Java中实现这一点,所以任何答案都应该考虑到这一点(例如,无指针魔术,假设8字节引用等) 形势 我们有一些大的值集,这些值通过一个4维键映射(让我们称这些维度为a、B、C和D)。每个维度可以有不同的大小,因此我们将假设如下: A:100 B:5 C:10000 D:2 这意味着一个完全填充的结构将包含1000万个

让我首先提出一个问题:考虑到我将进一步描述的情况和需求,哪些数据结构有助于实现非功能性需求

我试图查找几个结构,但到目前为止还不是很成功,这可能是因为我遗漏了一些术语

因为我们将在Java中实现这一点,所以任何答案都应该考虑到这一点(例如,无指针魔术,假设8字节引用等)

形势

我们有一些大的值集,这些值通过一个4维键映射(让我们称这些维度为a、B、C和D)。每个维度可以有不同的大小,因此我们将假设如下:

  • A:100
  • B:5
  • C:10000
  • D:2
这意味着一个完全填充的结构将包含1000万个元素。不考虑它们的大小,仅保存引用所需的空间就相当于80兆字节,因此这将被视为内存消耗的下限

我们还可以进一步假设结构不会完全填充,而是相当密集

要求

由于我们经常构建和查询该结构,因此我们有以下要求:

  • 构造结构应该是快速的
  • 对单个元素和范围(例如[A1-A5、B3、任意C、D0])的查询应该是有效的
  • 不需要快速删除元素(不会经常发生)
  • 内存占用应该很低
我们已经考虑过的

kd树

构建这样一棵树需要一些时间,因为它可能会变得很深,我们要么接受较慢的查询,要么采取重新平衡措施。另外,内存占用非常高,因为我们需要在每个节点中保存完整的密钥(尽管可能有方法可以减少)

嵌套地图/地图树

使用嵌套映射,我们可以只存储每个维度的键以及对下一个维度映射或值的引用—有效地从这些映射构建树。为了支持范围查询,我们将保留可能的键的排序集,并在遍历树时访问这些键

构造和查询要比使用kd树快得多,但内存占用要高得多(正如预期的那样)

一张大地图

另一种方法是保留单个可用关键帧的集合,并使用单个大型贴图

构造和查询也很快,但内存消耗甚至更高,因为每个映射节点都更大(它们现在需要保存一个键的所有维度)

我们目前的想法

为维度键构建插入顺序索引映射,即,我们将每个传入键映射到一个新的整数索引。因此,我们可以确保这些指数一次增长一步,没有任何差距(不考虑删除)

通过这些索引,我们可以访问一个由n维数组组成的树(当然可以展平为一维数组)——也称为n元树。该树将根据需要增长,即如果我们需要一个新的数组,那么我们只需创建一个新的块,而不是创建一个更大的数组并复制所有数据。任何需要的非叶节点都将根据需要创建,必要时替换根节点

让我用一个2维A和B的例子来说明这一点。我们将为每个维分配2个元素,从而得到一个2x2矩阵(长度为4的数组)

添加第一个元素A1/B1,我们会得到如下结果:

[A1/B1,null,null,null]
现在我们添加元素A2/B2:

[A1/B1,null,A2/B2,null]
现在我们添加元素A3/B3。由于无法将新元素映射到现有数组,我们将创建一个新元素和一个公共根:

                [x,null,x,null]  
                /        \
[A1/B1,null,A2/B2,null]  [A3/B3,null,null,null]
根据每个数组的大小,密集填充矩阵的内存消耗应该相当低(在一个数组中有4个维度和每个维度4个值,我们将有长度为256的数组,因此在大多数情况下,最大树深度为2-4)

这有意义吗?

如果结构将“非常密集”填充,那么我认为假设它将充满是有意义的。这使事情简化了很多。使用密集填充矩阵的稀疏矩阵表示法并不会节省很多(或任何东西)

我先试试最简单的结构。它可能不是内存效率最高的,但它应该是合理的,并且非常容易使用

首先,一个包含10000000个引用的简单数组。也就是说(请原谅C#,因为我不是真正的Java程序员):

正如你所说,这将消耗80兆字节

接下来是四个不同的字典(我认为是Java中的映射),每个键类型对应一个:

Dictionary<KeyAType, int> ADict;
Dictionary<KeyBType, int> BDict;
Dictionary<KeyCType, int> CDict;
Dictionary<KeyDType, int> DDict;
在.NET中,字典开销大约是每个键24个字节。但是您总共只有11007个密钥,因此字典将消耗大约250 KB的数据

直接查询应该非常快,范围查询应该与单个查找和一些数组操作一样快

我不清楚的一件事是,如果您想要一个键,那么在每个构建中解析为相同的索引。也就是说,如果“foo”在一个构建中映射到索引1,它是否总是映射到索引1

如果是这样,您可能应该静态地构造字典。我想这取决于范围查询是否总是以相同的键顺序进行

无论如何,这是一个非常简单且非常有效的数据结构。如果您可以负担81兆字节作为结构的最大大小(减去实际数据),那么这似乎是一个很好的起点。你可以在几个小时内让它工作


充其量这就是你要做的一切。如果你最终不得不替换它,至少你有一个工作的实现,你可以用它来验证你提出的任何新结构的正确性。

还有其他多维树通常比k更好
Dictionary<KeyAType, int> ADict;
Dictionary<KeyBType, int> BDict;
Dictionary<KeyCType, int> CDict;
Dictionary<KeyDType, int> DDict;
DIndex + 2*(CIndex + 10000*(BIndex + 5*AIndex));