5

I know that I cannot store a value at an index of an ArrayList that hasn't been used yet, i.e. is less than the size. In other words, if myArrayList.size() is 5, then if I try to do

myArrayList.set(10, "Hello World") 

I will get an out of bounds error. But my app needs this. Other than a loop of storing null in each of the intermediate slots, is there a more elegant way?

It looks to me like:

  • This behavior is the same in Vector
  • If I need to be able to randomly access (i.e. element at pos X) then my choices are Vector and ArrayList.
  • I could use a HashMap and use the index as a key but that's really inefficient.

So what is the elegant solution to what looks like a common case. I must be missing something...

Bhushan
  • 16,055
  • 24
  • 96
  • 132
pitosalas
  • 8,411
  • 10
  • 51
  • 97

7 Answers7

4

I could use a HashMap and use the index as a key but that's really inefficient.

Depends. If the indices you use are very sparse, it might be a lot better to use a Map. If the indices tend to be closely together, I think there is no better way than to fill it with nulls. Just write a utility function for it that you can use over and over instead of repeating the loop everywhere you need it, something like this:

private void padTo(List<?> list, int size) {
    for (int i=list.size(); i<size; i++)
        list.add(null);
}
Steven
  • 2,347
  • 5
  • 29
  • 32
  • 3
    Consider to call `list.ensureCapacity(size)` before loop to avoid unnecessary memory reallocations. – Mersenne Feb 10 '12 at 16:48
  • Note that ensureCapacity(size) doesn't actually pad the array - http://stackoverflow.com/questions/7688151/java-arraylist-ensurecapacity-not-working – Aram Kocharyan Feb 09 '16 at 23:18
3

You can use a Map<Integer, MyClass> instead. Specifically, if you use HashMap, it will also be O(1) - though it will be slower than ArrayList.

Michael Myers
  • 178,094
  • 41
  • 278
  • 290
amit
  • 166,614
  • 24
  • 210
  • 314
3

You can use TreeMap<key, value>, which is sorted in natural order by the value.

Here you can keep value as the index. You can insert any value, it need not be in order. This seems the simplest solution.

Bhushan
  • 16,055
  • 24
  • 96
  • 132
1

Sounds like you want a regular array:

  • You want random access
  • You want to specify some large size
Bohemian
  • 365,064
  • 84
  • 522
  • 658
  • You can specify the size of an ArrayList – blank Feb 10 '12 at 16:24
  • 4
    Don't think so. It's the initial allocated capacity but the size is zero at the start. – pitosalas Feb 10 '12 at 16:32
  • Regular array is not an option if the items stored in the list are generic. – Mark Jeronimus May 23 '17 at 15:33
  • @MarkJeronimus it is an option, if you are ok with disregarding the unchecked assignment warning, and fully control what goes into that array and how it's read (i.e. you have to cast it) – Populus Mar 03 '20 at 00:57
  • @MarkJeronimus huh? java arrays have a type, although not implemented via generics, still typed. eg a `String[]` is different from a `Integer[]` and you'll get a compile error if you try the assign an object of the wrong type to an array element. There's no casting needed or desired, unless you use a `Object[]`, but that is obvious. – Bohemian Mar 03 '20 at 01:14
  • You can do it if you specify an array directly, but not when you have a generic container class and need the items in the array to be generic (i.e. of the generic parameter). You can do `new Arraylist` but not `new T[]`. Note that the way `ArrayList` is implemented is by storing `Object[]` and lots of ugly casting. They can get away with unchecked casts because the array shouldn't leak to the outside. (however you can hack ArrayList and cause ClassCastExceptions using reflection) – Mark Jeronimus Mar 05 '20 at 14:45
1

If you definitely have to use the list and not a map, then it's best you override the arraylist's add and set methods to first put a null in indexes before. No other better way IMO

sethu
  • 7,407
  • 5
  • 35
  • 61
1

HashMap is probably much less inefficient than you think, try it. Otherwise, I can think of no way of doing it more elegantly than looping and filling with null. If you want elegance of exposition at least, then you could always subclass ArrayList and add an expandingSet(position, value) method to hide all the looping and such, or something. Perhaps this isn't an option though? If not just have a utility method somewhere else for it, but this is not as nice imho, though it will work also with other types of list too I guess...

Perhaps a wrapper class would be the best of both worlds, or perhaps it would just incur unnecessary overhead...

Elias Vasylenko
  • 1,467
  • 11
  • 21
0

If you are looking for a sparse array (where most of the indices will be empty), a Map of some sort (probably a HashMap) will be your best bet. Any array-esque solution will be forced to reserve space for all the empty indices, which is not very space-efficient, and a HashMap is fast enough for most normal purposes.

If you will eventually fill up the array up to some n, you will need to add nulls in a loop to get to the index you want. You can make this somewhat more efficient by giving it an initial capacity of the number of elements you will eventually want to store (this prevents the ArrayList from needing to resize itself). new ArrayList(n) will work fine. Unfortunately, there is no simple way of making it a certain size to begin with except adding things in a loop when you make it.

Retief
  • 3,111
  • 15
  • 16