7

Wondering whether there is an efficient way to add an item to Java's ArrayList at a bigger position than its current size:

Scenario:

   ArrayList<Item> items = new ArrayList<Item>;
   ... let's say I add three elements

Now I would like to add an item at position 10 (leaving items from 3 to 10 to null)

  items.add(10,newItem);  // item.size() == 3 

Is there an efficient way resizing/filling an ArrayList with nulls?

Java's implementation makes size field private :-(..

10
  • You should probably use HashMap or SortedMap instead. Commented Jan 19, 2012 at 11:24
  • Ok, using a Map is not a solution for memory raisons, we know at the end the structure is full -> TIntObjectHashMap (trove) maybe Commented Jan 19, 2012 at 11:26
  • A HashMap does not support order. A SortedMap is a better option. Commented Jan 19, 2012 at 11:27
  • What memory reasons? Why would you assume that a Map is using significantly more memory than a list? Commented Jan 19, 2012 at 11:28
  • 1
    One way would be to call add(null) 8 times. Commented Jan 19, 2012 at 11:30

10 Answers 10

6

imho the best thing you can do is items.addAll(Collections.nCopies(6, null)) and hope, that ArrayList implements some behaviour to internally fasten this up

Sign up to request clarification or add additional context in comments.

6 Comments

This is nice but a bit scary from a performance point of view
well nCopies only produces a list-Wrapper for an Array and this way your ArrayList is able to use System.arraycopy to fill in the null's depending on how smart it is implemented
public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacity(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; }
Voila, but your solution it's the best one of the proposition so far :-)
so where exactly is this "from a performance point of view" scary, comparing to calling add(null) many times (if we suppose the numbers arent fix)?
|
2

How about this?

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

items.add(new Item(0));
items.add(new Item(1));
items.add(new Item(2));

items.addAll(Collections.<Item>nCopies(7, null));
items.add(10,new Item(10));

System.out.println(items);

prints

[0, 1, 2, null, null, null, null, null, null, null, 10]

1 Comment

Use 10 - items.size instead of 7 as a more robust option.
2

This is an older question, but you can now use SparseArray as an (almost) direct drop-in replacement for ArrayList. It allows non-contiguous integer key values, and returns null if a value has not been set for a key. Performance-wise it was designed exactly for your needs. Instead of add you use append, which is more descriptive in that you are appending to the end of whatever the max key is, even if there is gaps. You can also set at any key value you'd like, even if it is beyond the maximum key.

2 Comments

There is no class called SparseArray in standard Java.
Yes, sorry, it is Android-specific.
1

Use TreeMap instead. Here is simple example to check memony consuption. Run first and second test separatly and use jvisualvm to check heap size. Remember to Perform GC several times.

    public class Test {


            public static void main(String[] args) throws InterruptedException {
                String s = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque metus.";


                //Test 1
                ArrayList<String> l = new ArrayList<String>();

                for (int i = 0; i < 1000000; i++) {
                    l.add(s + " " + i);
                    l.addAll(Collections.nCopies(i % 10, (String)null)); //Add some nulls
                }
                //Heap is > 5MB

                //Test 2 uncomment and comment test 1
    //          SortedMap<Integer, String> map = new TreeMap<Integer, String>();
    //          for (int i = 0; i < 1000000; i++) {
    //              map.put(i,s + " " + i);
    //          }
                //Heap is < 5MB

                Thread.sleep(100000);

            }
    }

It looks like TreeMap version is even less memory consuming than ArrayList version. Check yourself.

5 Comments

Peter, ArrayList is an array and little more. How is possible a full array takes more size than any other structure ? ... I think your example has a problem (I'm certain)
The array is (s - string, n- null): s n s n n s n n n s n n n n s n n n n n s n n n n n n s n n n n n n n s ... etc There is lot of memory reserved for references but set to null. TreeMap doesn't have this problem. As you said it's an array. Even array of 1mio nulls still costs memory. It's all about how "dense" is your list.
If it's a full array, whre is the problem? Put some nulls using Collection.nCopies method.
I think you misunderstood the problem, once the filling is finished there are no anymore nulls. To check the size is more like :
for (int i = 0; i < 1000000; i++) { l.add(s + " " + i); }
0

No, you can't:

http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html#add(int, E)

Throws: IndexOutOfBoundsException - if the index is out of range (index < 0 || index > size())

1 Comment

Unable to edit. Correct link is : docs.oracle.com/javase/6/docs/api/java/util/…
0

No, you can't do this, But if you wish to do that, then add empty object in remaining index such as..

    ArrayList<Object> items = new ArrayList<Object>();
    items.add(new Object());
    items.add(new Object());
    items.add(new Object());
    items.add(3,new Object());

1 Comment

actually I was lucking for something a bit more elegant :-)
0

I would consider using a SortedMap instead of a List here. This will allow for indexes to not exist:

SorteMap<Integer, Item> myMap = new TreeMap<Integer, Map>();
int i=0;
myMap.put(i++, first);
myMap.put(i++, second);
myMap.put(i++, third);
myMap.put(10, other);

If a Map truly will not work, as you stated. I would then suggest creating a Decorator around ArrayList. In the insert method, add nulls to fill the empty locations. I would suggest using Guava's ForwardingList to ease the creation of the class. This way you would only have to implement one method.

3 Comments

TreeMap with 1mio Object is large.
ArrayList with 1mio Objects and nulls in the middle is much smaller?
The ArrayList is mainly full.
0

If memory and index is so important that use a normal array.

When it becomes to small use System.arraycopy thats the way ArrayList does it internal.

--

Even if you use the ArrayList and have a Million Objects it is advisable to use the ArrayList(int initialCapacity)-Constructor to avoid a lot of copy-operations

Comments

0

@icCube- you said, that list should be about 90% full. My idea for this solution is:

  • If you exactly know target size - use plain array
  • If you are aware of target size - use ArrayList with initial capacity as close as possible to target size. Put nulls with l.addAll(Collections.nCopies(n, (String)null)); as people said.
  • If you don't know target size - your ArrayList will be resized many times. Resizing means copying whole underlying array (it uses Arrays.copyOf). You can imagine what happens if array is copied around - GC has lots of work. Use TreeMap then.

Comments

-2

Use constructor ArrayList(int initialCapacity). This way you can set an initial capacity.

2 Comments

InitialCapacity is not the size -> Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 9, Size: 0
Try : public static void main(String[] args) { ArrayList list = new ArrayList(10); list.add(9,3); }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.