有没有更好的方法让ArrayList变得更大?

huangapple go评论69阅读模式
英文:

Are there better ways to make an ArrayList bigger?

问题

我正在尝试使用ArrayList来按索引值存储和检索项目。我的代码类似于这样:

ArrayList<Object> items = new ArrayList<>();

public void store(int index, Object item) {
    while (items.size() < index) items.add(null);
    items.set(index, item);
}

这个循环看起来不够优雅,我想使用items.setMinimumSize(index + 1),但这个方法不存在。我也可以使用items.addAll(Arrays.asList(new Object[index - items.size()])),但这会分配内存,似乎过于复杂,只是为了扩展数组的大小。大多数情况下,循环不会迭代任何次数,所以我更喜欢简单性而不是速度。

索引值可能不会超过200,所以我可以这样做:

Object[] items = new Object[200];

public void store(int index, Object item) {
     items[index] = item;
}

但如果有一天需要超过200个值,这种方法就会出问题。

这真的是我唯一的选择吗?看起来我被迫使用比本应简单的东西更复杂的方法。

英文:

I am trying to use an ArrayList to store and retrieve items by an index value. My code is similar to this:

ArrayList&lt;Object&gt; items = new ArrayList&lt;&gt;();

public void store (int index, Object item)
{
    while(items.size() &lt; index) items.add(null);
    items.set(index, item);
}

The loop seems ugly and I would like to use items.setMinimumSize(index + 1) but it does not exist. I could use items.addAll(Arrays.asList(new Object[index - items.size()])) but that allocates memory and seems overly complex as a way to just make an array bigger. Most of the time the loop will iterate zero times, so I prefer simplicity over speed.

The index values probably won't exceed 200, so I could use:

Object[] items = new Object[200];

public void store (int index, Object item)
{
     items[index] = item;
}

but this would break if it ever needs over 200 values.

Are these really my only options? It seems that I am forced into something more complex than it should be.

答案1

得分: 3

我会考虑使用 Map 而不是 List 结构。为什么不试试这样:

// 根据你的需求,你可能想要使用另一个 Map 实现,只需阅读文档...
Map<Integer, Object> items = new HashMap<>();

public void store(int index, Object item)
{
    items.put(index, item);
}

这样你就可以避免那个无意义的 for 循环。

英文:

I would consider using a Map instead of a List construct. Why not this :

//depending on your requirements, you might want to use another Map
//implementation, just read through the docs...
Map&lt;Integer, Object&gt; items = new HashMap&lt;&gt;();

public void store (int index, Object item)
{
    items.put(index, item);
}

This way you can avoid that senseless for loop.

答案2

得分: 0

问题是,你想要的并不是一个数组列表。你似乎想要的是一个 Map<Integer, T>,而不是一个 ArrayList<T>,因为你明显想要将索引映射到一个值;你想要这个操作:

向这个列表中添加一个值,使得 list.get(250) 返回这个值。

虽然使用数组列表也可以实现这一点,但这实际上并不是它的主要用途,当你使用不符合预期的方式使用某些东西时,通常会写出一堆奇怪的代码,然后产生疑问:“真的吗?这样对吗?”- 就像在这种情况下一样。

鉴于你说索引不太可能超过 200,你所做的事情并没有特别大的问题,但一般来说,我建议不要通过使用不恰当的方式引入语义上的混乱。如果必要,可以创建一个完全新的类型,专门封装了稀疏列表的概念。如果必要,可以在底层使用数组列表来实现,这也是可以的。

或者,你可以使用已经存在的东西。

一个映射(map)

从核心库中来看,为什么不使用 new TreeMap<Integer, T>() 呢?树映射会自行排序(因此,如果你遍历其键,你会按顺序获得它们;如果你依次添加 5、200 和 20,你会得到按顺序的键 '5, 20, 200',就如你所期望的那样)。性能为 O(1) 或 O(log n),但会有更高的启动时间(如果你的集合中大约有 200 项,这几乎不可能成为一个问题。当你添加一百万项时再叫醒我,那时性能可能是可测量的,甚至是可察觉的)- 如果你愿意,甚至可以向它投入几百万的 '键',没有问题。在这里,最坏情况下的表现要好得多,你基本上不可能引发问题,而对于稀疏的、由数组支持的列表来说,如果我向它投入一个 30 亿的索引,那么你将会有几个 GB 的空白内存被分配;你肯定会注意到这一点!

一个稀疏列表

Java 本身没有稀疏列表,但其他库有。在网上搜索一些你喜欢的内容,将其添加为依赖,然后继续前进。

英文:

The problem is, what you want isn't really an arraylist. It feels like what you want is a Map&lt;Integer, T&gt; instead of an ArrayList&lt;T&gt;, as you clearly want to map an index to a value; you want this operation:

Add a value to this list such that list.get(250) returns it.

And that is possible with arraylist, but not really what it is for, and when you use things in a way that wasn't intended, what usually ends up happening is that you write a bunch of weird code and go: "really? Is this right?" - and so it is here.

There's nothing particularly wrong with doing what you're doing, given that you said the indices aren't going to go much beyond 200, but, generally, I advise not creating semantic noise by using things for what they aren't. If you must, create an entirely new type that encapsulates exactly this notion of a sparse list. If you must, implement it by using an arraylist under the hood, that'd be fine.

Alternatively, use something that already exists.

a map

From the core library, why not.. new TreeMap&lt;Integer, T&gt;()? treemaps keep themselves sorted (so if you loop through its keys, you get them in order; if you add 5, then 200, then 20, you get the keys in order '5, 20, 200' as you'd expect. The performance is O(1) or O(log n), but with a higher runup (this is extremely unlikely to matter one iota if you have a collection of ~200 items in it. Wake me up when you add a million, then performance might be even measurable, let alone noticable) - and you can toss a 'key' of a few million at it if you want, no problem. The 'worst case scenario' is far better here, you basically cannot cause this to be a problem, whereas with a sparse, array-backed list, if I tossed an index of 3 billion at it, you would then have a few GB worth of blank memory allocated; you'd definitely notice that!

A sparse list

java itself doesn't have sparse lists, but other libraries do. Search the web for something you like, add it as a dependency, and keep on going.

答案3

得分: 0

> 循环看起来不太优雅,我想使用 items.setMinimumSize(index + 1) 但这个方法不存在。

一个 List 包含一系列的引用,没有间隙(但可能包含空元素)。索引与列表中的序列相关联,列表的大小定义为它所包含的元素数量。您不能操作索引大于等于列表当前大小的元素。

这个大小不应与 ArrayList容量 混淆,容量是指 ArrayList 目前能够在不获取额外存储的情况下容纳的元素数量。在大多数情况下,您可以并且应该忽略 ArrayList 的容量。与容量打交道会使您的代码特定于 ArrayList,而不是通用于 List,这主要是一个优化问题。

因此,如果您想增加 ArrayList(或大多数其他类型的 List)的大小,以确保某个索引对其有效,则唯一的选择是添加元素,使其至少达到那个大小。但您不需要在循环中逐个添加元素。我实际上喜欢您的这个方法:

> items.addAll(Arrays.asList(new Object[index - items.size()]))

,但您需要再添加一个元素。大小至少需要达到 index + 1,以便在索引 index 处进行 set() 操作。

或者,您可以使用

items.addAll(Collections.nCopies(1 + index - items.size(), null));

这甚至可能更便宜。

英文:

> The loop seems ugly and I would like to use items.setMinimumSize(index + 1) but it does not exist.

A List contains a sequence of references, without gaps (but possibly with null elements). Indexes into a list correlate with that sequence, and the list size is defined as the number of elements it contains. You cannot manipulate elements with indexes greater than or equal to the list's current size.

The size is not to be confused with the capacity of an ArrayList, which is the number of elements it presently is able to accommodate without acquiring additional storage. To a first approximation, you can and should ignore ArrayList capacity. Working with that makes your code specific to ArrayList, rather than general to Lists, and it's mostly an issue of optimization.

Thus, if you want to increase the size of an ArrayList (or most other kinds of List) so as to ensure that a certain index is valid for it, then the only alternative is to add elements to make it at least that large. But you don't need to add them one at a time in a loop. I actually like your

> items.addAll(Arrays.asList(new Object[index - items.size()]))

, but you need one more element. The size needs to be at least index + 1 in order to set() the element at index index.

Alternatively, you could use

items.addAll(Collections.nCopies(1 + index - items.size(), null));

That might even be cheaper, too.

huangapple
  • 本文由 发表于 2020年9月23日 22:18:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/64030002.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定