【问题标题】:Java data structure that has efficient add, delete, and random高效增删改查的Java数据结构
【发布时间】:2023-03-19 13:55:02
【问题描述】:

我需要一个可以有效地添加、删除和访问随机对象的 Java 数据结构。

这是行不通的:

ArrayList 具有高效的添加(恒定时间)和随机访问(只需使用随机整数“获取”),但删除可能需要线性时间,因为它必须潜在地搜索整个列表。

TreeSet 或 HashSet 具有高效的添加和删除功能,但我不知道如何获取随机对象。

有什么想法吗?

理论上,如果我可以自己用随机的左或右遍历树,B 树就可以工作,但我认为标准的 Java 类没有给我这种能力。

如果标准 Java 类中的任何内容都不起作用,我愿意使用第三方库。

我不需要支持重复或空值,也不需要是线程安全的。

谢谢。

【问题讨论】:

  • ArrayList.remove(int index) 在摊销的常数时间内运行。据我所知,这意味着单个调用是线性时间,但一系列调用的平均时间接近恒定时间。
  • 感谢 Aarowaim,但我不知道要删除的对象的索引。我想我可以将这些信息单独存储在 HashMap 或其他东西中,但是一旦我从 ArrayList 中删除了一个对象,列表中的其他对象就会更改索引,这意味着 HashMap 中的某些值是错误的,我需要线性时间来修复它们。
  • @Magmatic 你需要允许重复元素吗?
  • 我不需要支持重复或空值,也不需要线程安全。
  • 你说的元素有多少?数百?数千?百万?它们是简单的数字、字符串还是更复杂的对象?

标签: java search data-structures random


【解决方案1】:

我建议您使用带有 Integer 键的 Map。这样您就可以为删除生成一个随机数。

留给读者的问题:在随后的删除(或任何操作)中会有间隙。

【讨论】:

  • 但是如果你不知道它的键,你怎么能删除一个值呢?您仍然需要进行低效的线性搜索才能找到它。
【解决方案2】:

如果您进行列表删除无序,您可以使用 ArrayList/HashMap 对(其中地图将存储列表中对象的索引)来获取此信息。删除时,您只需移动最后一个元素以填充已删除项目的空间,而不是将列表中的所有后续元素向下移动一个位置。那么在移除的过程中最多有两个不同的元素需要被触动。一个简单的例子(我没有测试过,希望我没有搞砸):

class SpecialSet<E> extends AbstractSet<E> implements Set<E> {
    private final List<E> list = new ArrayList<>();
    private final Map<E,Integer> indexMap = new HashMap<>();

    @Override
    public boolean add(E e) {
        if (contains(e)) return false;
        indexMap.put(e, list.size());
        list.add(e);
        return true;
    }

    @Override
    public boolean remove(Object o) {
        Integer indexBoxed = indexMap.remove(o);
        if (indexBoxed == null) return false;
        int index = indexBoxed;
        int last = list.size() - 1;
        E element = list.remove(last);
        if (index != last) {
            indexMap.put(element, index);
            list.set(index, element);
        }
        return true;
    }

    public E removeRandom() {
        E element = list.get((int)(Math.random() * size()));
        remove(element);
        return element;
    }

    @Override
    public boolean contains(Object o) {
        return indexMap.containsKey(o);
    }

    @Override
    public Iterator<E> iterator() {
        return list.iterator();
    }

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

根据您想要的对象相等性测试行为,您也许可以将 HashMap 换成 IdentityHashMap 以获得更好的速度。

【讨论】:

  • 哇,感谢代码!我只是把它扔到我的项目中。 (我只将“removeRandom”更改为“getRandom”并取出删除它的行)。它工作得很好!我一直在使用具有低效删除的 ArrayList,但是当我更改为此代码时,处理时间从 1900 毫秒下降到 628 毫秒。哇哇哇!谢谢,谢谢,谢谢!
  • 我看不到HashMap 的重要性。这里最重要的trick是:removal不是直接移除元素,而是移除最后一个元素,将最后一个元素放在需要移除的位置。在这种情况下,只需一个 ArrayList 作为内部结构就会有所帮助
  • @AdrianShum 你必须先找到一个元素才能删除它;这就是 HashMap 快速实现的部分。
  • @Boann 哎呀,你说得对,我忽略了删除部分(我以为 OP 要求按索引删除)
【解决方案3】:

请注意,如果您预先分配到您需要的最大大小,一个简单的数组就可以完成这项工作。保留“top”索引值并通过递增“top”并存储到该数组元素中进行插入。删除时,将“top”值向下复制到已删除的元素中并递减“top”。

如果您不知道您的最大大小,您仍然可以分配数组数组并使用相同的基本算法,只需在访问元素之前首先计算主要和次要数组索引值。

【讨论】:

    猜你喜欢
    • 2012-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-13
    • 2011-08-12
    • 2012-04-20
    相关资源
    最近更新 更多