java - 为恒定时间 contains() 创建一个 HashMap 和一个 ArrayList 是一个有效的策略吗?

标签 java data-structures time-complexity

我有一个 ArrayList,它的长度可以是 0 到 5000 个项目(也是相当大的对象)。

有一次我将它与另一个 ArrayList 进行比较,以找到它们的交集。我知道这是 O(n^2)。

在这个 ArrayList 旁边创建一个 HashMap 来实现恒定时间查找,这是一个有效的策略,以便将复杂性降低到 O(n)?还是另一种数据结构的开销根本不值得?我相信它不会占用额外的空间(除了引用文献)。

(我知道,我确定“这取决于我在做什么”,但我很想知道是否有任何缺点让它变得毫无意义,或者它是否真的是一种常见的使用策略。是的,我知道关于过早优化的引述。我只是从理论上的角度好奇)。

最佳答案

首先,一个简短的旁注:

And yes, I'm aware of the quote about prematurely optimizing.

你在这里问的是不是“过早的优化”!

您不是在谈论用一些奇怪的按位运算替换乘法,“因为它们更快(在 90 年代的 PC 上,在 C 程序中)”。您正在为您的应用程序模式考虑正确的数据结构。您正在考虑应用案例(尽管您没有告诉我们有关它们的很多细节)。您正在考虑选择某种数据结构对您算法的渐近 运行时间的影响。这是规划,或者可能是工程,但不是“过早的优化”。


话虽这么说,但要告诉您您已经知道的内容:这取决于情况。

详细说明一下:这取决于您对这些集合执行的实际操作(方法)、您执行的频率、它们的时间关键程度以及应用程序对内存的敏感程度。

(对于 5000 个元素,后者应该不是问题,因为仅存储引用 - 请参阅评论中的讨论)

一般来说,如果它们总是应该包含相同的元素。这种措辞是有意为之的:您应该始终了解这两个集合之间的差异。主要是:Set 只能包含每个元素一次,而 List 可能多次包含相同的元素。

对于所有提示、建议和注意事项,应牢记这一点。

但是,即使在您的情况下列表总是只包含一次元素是理所当然的,那么您仍然必须确保两个集合都得到适当的维护。如果你真的只是存储它们,你很容易导致细微的错误:

private Set<T> set = new HashSet<T>();
private List<T> list = new ArrayList<T>();

// Fine
void add(T element)
{
    set.add(element);
    list.add(element);
}

// Fine
void remove(T element)
{
    set.remove(element);
    list.remove(element); // May be expensive, but ... well
}

// Added later, 100 lines below the other methods:
void removeAll(Collection<T> elements)
{
    set.removeAll(elements);
    // Ooops - something's missing here...
}

为了避免这种情况,人们甚至可以考虑创建一个专用的集合类——类似于 FastContainsList,它结合了 SetList ,并将 contains 调用转发给 Set。但是您很快就会注意到,要违反 CollectionList 接口(interface)的约定是很困难的(或者可能是不可能的)一个集合,除非“你不能添加元素两次”的条款成为契约(Contract)的一部分......


因此,所有这一切都取决于你想用这些方法做什么,以及你真正需要哪个接口(interface)。如果您不需要 List 的索引访问,那很简单。否则,引用你的例子:

At one point I compare it against another ArrayList, to find their intersection. I know this is O(n^2).

您可以通过本地创建集来避免这种情况:

static <T> List<T> computeIntersection(List<T> list0, List<T> list1)
{
    Set<T> set0 = new LinkedHashSet<T>(list0);
    Set<T> set1 = new LinkedHashSet<T>(list1);
    set0.retainAll(set1);
    return new ArrayList<T>(set0);
}

这将有 O(n) 的运行时间。当然,如果您经常这样做,但很少更改列表的内容,则可以选择避免复制,但由于上述原因,维护所需的数据结构可能会变得棘手。

关于java - 为恒定时间 contains() 创建一个 HashMap 和一个 ArrayList 是一个有效的策略吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31704898/

相关文章:

java - 将用户输入的字符串与文本文件中的字符串进行比较

algorithm - 采访 : Renaming all files in a directory using a data structure

java - 用 Java 写回单词

java - 这个java leetcode解决方案在最坏的情况下使用二次时间吗?

java - 五秒停顿

java - 使用 Java 查找 AWS ElastiCache 终端节点

java - 如何找到此函数的增长顺序?

algorithm - 如果树是平衡的,在二叉搜索树中搜索的时间复杂度是多少?

java - CardLayout 带有更改卡片的按钮

c++ - 制表符分隔的文件数据要存储到数据结构中