是否有类似ListHashMap的数据结构?

时间:2014-06-11 07:00:31

标签: java data-structures

我一直在努力寻找一种能够:

的数据结构
  1. 让我检查O(1)时间(HashSet)中的重复项,
  2. 保留插入顺序,
  3. 请允许我获取该有序列表的子集。
  4. 我发现最接近的是LinkedHashSet,但是由于没有实现List界面并允许我在其上调用List函数(例如{{} 1}})。有没有理由我找不到这样的结构?我即将实现我自己的subList版本,但使用的是LinkedHashSet(与Linked-list-backed ArrayList相反)。我还从LinkedHashSet库中找到了OrderedHashSet,但由于没有实现所需的subList函数,这也不足......所以我真的很困惑为什么不需要它?或者我只是没想到要搜索的正确名称?

    编辑:我不仅试图找到一个可以满足这个要求的现有结构,但是如果没有一个,我试图弄清楚为什么它不存在。因为我已经知道如何实现它,所以能够回答的人都能得到接受的答案:)

    编辑2:抱歉抱歉,我应该更清楚我的第一个要求,我真的只需要真正有效地检查重复项。对我来说已经很晚了。

4 个答案:

答案 0 :(得分:4)

基本上,您所发现的是提供O(1)点查找但提供有效范围扫描(迭代)的东西。在数据库领域,这种事情有时被称为clustered-index,其中数据使用一些查找结构组织,例如B-Treehash index,但叶子节点或条目index按某种特定顺序排序(在您的情况下,它按插入顺序排序)。下面显示了一个群集B树的示例,其中@Itay Maman的解决方案是群集哈希索引的一个示例。

enter image description here

在Java中,没有这样的类本身满足您的需求,可能是因为它的复杂性 - 很难(或几乎不可能)有一个这样的实现最适合所有工作负载(例如您发出的频率)范围扫描,您发出点查找的频率,是否允许多读者和多个编写者?等等)但是,这里有一些可能的解决方案,取决于您的用例。

  1. 如果在大多数情况下你并不真正关心第3项,那么使用LinkedHashMap,并使用LinkedHashMap提供的常规迭代来完成第3项。

  2. 如果您关心所有项目的性能并且您从不发出删除/更新,那么最简单的方法可能是使用HashMap和ArrayList一起将数据表示为聚簇索引。每个插入都是对HashMap的插入+附加到ArrayList,而HashMap的值是ArrayList的索引。这样可以提供最佳的读取性能,但是如果有的话,则需要解决更新/删除问题,可能是将ArrayList替换为子阵列的链接列表。

  3. 在极端情况下,你确实有删除/更新,想要支持多线程访问,甚至想要持久性,那么最好的方法是使用开源的嵌入式持久键值存储,如作为RocksDBLevelDB,用于快速存储的嵌入式键值存储,如RAM或闪存(它也适用于磁盘工作负载。)虽然它们都是用C ++实现的,但它们确实有Java绑定(例如,Java中的RocksDB的简介page。)

  4. 当然,如果你可以重新实现某些东西,那么定制的LinkedHashMap可能是最简单的。只需添加迭代器的不同构造函数,它允许您在使用O(1)哈希定位的任何特定条目处开始迭代。

答案 1 :(得分:1)

修改

根据更新问题,您只需要一个不重复的集合(而不是键值映射)。这简化了一些事情。基本上,此解决方案使用: - 用于确定重复的(散列)集 - 一个简单的(数组)列表来维护插入顺序

除此之外,还有我们用于子列表的列表的自定义实现。我必须推出自己的(并且不依赖于ArrayList.subList()),因为从ArrayList返回的(子)列表不允许数组列表在创建后更改其大小。幸运的是,在AbstractList的帮助下,这很简单(只需要两种方法来覆盖)。

package p1;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class OrderedSet {
  private final List<String> list = new ArrayList<>();
  private final Set<String> values = new HashSet<>();

  /**
   * Inserts a value if it is not a dup.
   * @return true if "value" was actually inserted (not a dup).
   */
  public boolean put(String value) {
    if (values.contains(value)) 
      return false;

    values.add(value);
    list.add(value);
    return true;
  }

  public boolean contains(String string) {
    return values.contains(string);
  }

  public Iterator<String> iterator() {
    return list.iterator();
  }

  public int size() { return list.size(); }

  public List<String> subList(int begin, int end) {
    return new MyList(begin, end);
  }

  private class MyList extends AbstractList<String> {
    private final int begin;
    private final int end;

    public MyList(int begin, int end) {
      this.begin = begin;
      this.end = end;
    }

    @Override public String get(int index) {
      return list.get(begin + index);
    }

    @Override public int size() { 
      return end - begin;
    }
  }
}

这是一项测试,证明它有效(至少在功能上,对于时间复杂性,您必须相信我或分析实施......):

package p1;

import static org.junit.Assert.*;

import java.util.Iterator;
import java.util.List;

import org.junit.Test;

public class OrderedSetTest {

  @Test
  public void test() {
    OrderedSet om = new OrderedSet();
    assertTrue(om.put("a"));
    assertEquals(1, om.size());
    assertTrue(om.put("b"));
    om.put("c");
    assertEquals(3, om.size());
    assertFalse(om.put("a"));
    assertEquals(3, om.size());
    om.put("d");
    assertEquals(4, om.size());
    om.put("d");
    assertEquals(4, om.size());
    om.put("e");
    assertEquals(5, om.size());


    assertTrue(om.contains("a"));
    assertTrue(om.contains("b"));
    assertTrue(om.contains("c"));
    assertTrue(om.contains("d"));
    assertTrue(om.contains("e"));
    assertFalse(om.contains("a_"));
    assertFalse(om.contains("b_"));
    assertFalse(om.contains("f"));

    Iterator<String> iter = om.iterator();
    assertTrue(iter.hasNext());
    assertEquals("a", iter.next());
    assertTrue(iter.hasNext());
    assertEquals("b", iter.next());
    assertTrue(iter.hasNext());
    assertEquals("c", iter.next());
    assertTrue(iter.hasNext());
    assertEquals("d", iter.next());
    assertTrue(iter.hasNext());
    assertEquals("e", iter.next());
    assertFalse(iter.hasNext());

    List<String> sub = om.subList(2,  4);
    assertArrayEquals(new String[] { "c",  "d" }, sub.toArray(new String[0]));
  }
}

答案 2 :(得分:0)

java.util.TreeSet会有帮助吗?

它不是O(1)(参考您的列表要求,第1点),但支持自然排序(第2点)和子集操作(第3点)。

答案 3 :(得分:-1)

数据结构不可能同时提供这些内容,它们是矛盾的:如果在O(1)时间内插入和访问元素,则意味着它们是随机分布的(带有哈希码),并且如果您想保留订单,那么插入和访问将花费您更多时间。 我相信你所寻找的是HashSet,而不是HashMap。这取决于插入的顺序对于项目的未来是否至关重要,您可以使用HashSet(快速,随机访问和插入),也可以使用ArrayList(较慢,但保持顺序)。 我认为您不可能创建满足您需求的数据结构。