我有两个课程Foo
和Bar
。
class Foo
{
Set<Integer> bars; // Foo objects have collection of bars.
Set<Integer> adjacents; // Adjacency list of Foos.
}
class Bar
{
int foo; // ID of foo of which this object belongs to
Ipsum ipsum; // This an arbitrary class. But it must be present
Map<Integer, Float> adjacents; // Adjacency list of Bars
}
预定义Bar
的数量(最多1000个)。因此,我可以使用数组。
但Foo
的数量未定义(最多#ofBars/4
)。
当你考虑添加,删除和get()
时,我需要一个速度更快,占用空间更少的(因为我将使用序列化)。
以下是我的选择(据我所知)
选项1:不要为Foo
定义一个类。相反,使用List<Set<Integer>> foo;
和另一张地图&gt; fooAdjacencies;
选项2 :如果我想获得Map<Integer, Set<Integer> foo
栏,请使用i
。我只需写foo.get(i)
。
选项3:不要定义类。相反,使用选项2和Bar
类:
Map<Integer, Ipsum> bar;
Map<Integer, Map<Integer, Floar>> barAdjacencies;
我应该在空间和时间效率方面选择哪个选项?
答案 0 :(得分:1)
我觉得这个问题描述有点难以理解,但我认为你只是在寻找一般的馆藏/数据结构建议。
列表(例如,数组列表)可以轻松地添加和迭代元素。当它扩展到超出底层数组的大小时,执行一次性昂贵的调整大小操作以增加更多空间;但这很好,因为它很少发生,摊销的时间也不错。搜索列表中的特定元素很慢,因为您需要按顺序遍历它;大多数列表中没有隐含的排序。删除元素取决于基础列表实现。在这方面,数组列表可能很慢;但我猜他们只是通过将底层元素标记为已删除并在迭代期间跳过它来优化它。使用列表时,还必须考虑添加元素的位置。链接列表的迭代速度较慢,但可以在任何位置轻松添加和删除元素。除了结尾之外,数组列表不能轻易地添加元素。
根据您的要求,如果您需要执行“获取”或查找元素,那么您需要某种搜索功能来加快速度。这样可以使地图更好,因为您可以在log(n)时间内找到元素,而不是在搜索无序列表时查找线性时间。添加和删除列表中的元素也相对较快,因此这可能是您的最佳选择。
最重要的是,实施它不止一种方式并自己进行分析以了解更多信息:)虽然需要进行搜索,但列表很少是一个不错的选择。
答案 1 :(得分:1)
这听起来对您(特别是数据结构部分)非常有帮助:http://bigocheatsheet.com/
你说
我需要在添加,删除和查找元素时使我的结构高效。没有其他行为。
问题是Lists
和Maps
通常用于完全不同的情况。他们的名字相当好地描述了他们的用例 - 如果你需要列出某些东西(可能是按照某些顺序)你使用List
,而如果你需要Map
您需要映射输入到输出。通过将Map
映射到您的元素,您可以将List
用作Integers
,但这会使事情过于复杂。但是,即使在List
和Map
内,您也可以在渐近性能方面有不同的实现。
除了少数例外,数据结构将占用O(n)
空间,这是有道理的。如果内存服务,除ArrayList
之外的任何东西(或仅由原始数组支持的其他集合)在使用其他对象时都会有相当大的空间开销(例如Nodes
用于LinkedLists
以及Entry
的{{1}}个对象来组织底层结构。我不会过分担心这种开销,除非空间确实非常重要。
为了获得最佳性能添加,删除和搜索,您需要了解数据结构的实现方式。
Maps
- 样式实现会为您LinkedList
添加和删除(并且还有一个很好的常量因素!),但是O(1)
会有{get()
{1}}时间,因为每次想要获取内容时都必须遍历列表。但是,Java的O(n)
实现在LinkedList
时间内被删除;实际的删除行为是O(n)
,只有当你有一个你要删除的实际节点的引用时才会这样做。因为您没有,因此Java O(1)
中的删除LinkedList
- O(n)
用于搜索要删除的节点,O(n)
用于删除。
使用普通数组支持的数据结构将具有O(1)
O(1)
,因为它是一个数组,但需要get()
来添加和删除,因为除了O(n)
之外的任何添加/删除在最后一个元素需要洗牌所有其他元素(至少在Java的实现中)。使用对象而不是索引搜索某些内容是在O(n)
时间内完成的,因为您必须遍历数组才能找到该对象。
以下两种结构通常是Maps
,因此通常要求您实施equals()
(和hashCode()
HashMaps
):
由树支持的数据结构(例如TreeMap
)将分摊(我认为)O(lg n)
添加/删除,因为一个好的实现应该是自我平衡的,最坏情况下添加/删除最多只需要通过树的高度。 get()
次操作为O(lg n)
。使用树需要您的元素在某种程度上可以排序/比较,这可能是奖励或阻碍,具体取决于您的使用情况。
基于散列的数据结构已经摊销(平均)O(1)
所有内容,尽管由于散列的开销而导致常数因子稍高(如果散列扩散很差,则跟随任何链)。如果你编写了一个糟糕的HashMaps
函数,hashCode()
可能会开始吸吮,所以你要小心,尽管Java HashMap
的实现者确实在幕后做了一些魔术尝试至少部分地否定了不良hashCode()
实施的影响。
希望破旧有所帮助。如果你弄清楚你的程序是如何构建的,我或许可以提出建议。在那之前,我能做的最好的就是向你展示选项并让你选择。