ArrayList's add(index,object) method throw IndexOutOfBoundException - java

On creating an arrayList using the below mentioned code snippet:
List arrayList = new ArrayList(16);
The internal implementation of ArrayList creates an array elementData of size 16 and assigns nullat every location. On doing something like arrayList.add(2,"HelloWorld") gives an IndexOutOfBoundException as the index at which the element is being added (i.e 2) is greater than the size attribute of the arrayList .
As clear from the javaDocs, the size attribute of the arrayList is initialized to 0 when the arrayList is initialized and is incremented by 1 everytime a new element is added to the arrayList
Can someone please explain, why the ArrayList dataStructure was designed this way in the first place. Even tho' the internal dataStructure elementData was initialized with 16 null values at the creation of arrayList, still it does not allow to add value at indeces > size; (assuming index <16 in this case). What was the idea to implement the add(index,object)
funtionality to be governed by the size attribute of the arrayList?

The purpose of having an internal array with a size greater then List.size() is to avoid re-allocating the array unnecessarily. If the internal array always had the same size as the List, then every time a new element is added, the internal array would have to be re-allocated, causing a performance penalty.

In fact, the default constructor of ArrayList constructs a list with an initial capacity of 10.
public ArrayList() {
this(10);
}
But why we need such an allocation? As you understand, if you indicate the size of ArrayList in advance, you can provide efficient for the list. Otherwise, after the number of elements exceeds the initial capacity of ArrayList, a new reallocation operation is performed for each element.
The documentation says:
public void add(int index, E element)
Throws:
IndexOutOfBoundsException -
if the index is out of range
(index < 0 || index > size())
As you can see, it throws IndexOutOfBoundsException if (index > size()).
Since ArrayList's "public int size()" returns elements which are not equal to null, your size equals to 0 (not 16 as you said in your example). In other words, if null values were counted as well, the size of each ArrayList that was created with default constructor would be 10.
Consequently, "arrayList.add(2, "HelloWorld")" throws IndexOutOfBoundsException since index = 2 but size() = 0.
Edit:
I think when you mount your argument, you use this as base:
String[] arr = new String[5];
arr[3] = "hello";
System.out.println(arr[3]);
Then, you think why you can give a value in an array element directly but why you cannot do the same thing while using add(int index, E element) method of ArrayList. Actually, it is true but there is no condition to implement ArrayList as complete counterpart of Array. In other words, this method is conditioned with that rule since it is nature of ArrayList. As we all know, when you create an array, you specify its size in square brackets. The constructor of ArrayList which takes int as parameter does not do the same thing. It performs just an imaginary allocation. Yes, it could specify its initial size with this allocation or after add(int index, E element) is called, size could be increased by one. However, ArrayList is implemented to provide an array-like structure which has continuity with respect to index number but has no fixed size. Thence, there are some other higher level of abstraction examples do this task. To exemplify, LinkedHashMap structure.

You can not add object at a specific index till it contains null. You just need to add object using add method and then you can update values on a index.
For Example.
ArrayList<Integer> arrlist = new ArrayList<Integer>(5);
// use add() method to add elements in the list
arrlist.add(15);
arrlist.add(22);
arrlist.add(30);
arrlist.add(40);
// adding element 25 at third position
arrlist.add(2,25);

You rarely need to specify the capacity of an ArrayList, it can improve the performance only if you know how many elements your ArrayList will hold.
ArrayList is simply a List that can automatically be grown or shrink. Using List, you never need to add an element in the place n if the list is empty, you simply link it to the previous node (unless it's the head of course) - that's the idea of ArrayList except the fact that it can be grown/shrink automatically.

See the java doc of ArrayList's add(int index,
E element) method. Here you can find the ArrayIndexOutOfBound exception occurred when if the index is out of range (index < 0 || index > size()).
You declared an ArrayList with an initial capacity 16. It doesn't mean that each of the 16 indexed position of the ArrayList contains element. It just mention the initial capacity and when it necessary it will grow it's size dynamically.
See the source code the constructor from the ArrayList class -
/**
* Constructs an empty list with the specified initial capacity.
*
* #param initialCapacity the initial capacity of the list
* #exception IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
Here we can not find anything that told us - ArrayList will initiated with null value.
Update: Based on your comment I have done some experiment, since I'm not sure about whether an array of reference/non-primitive type will initialized with null. See the following code below. Run the the code by uncommenting each of line at per execution -
import java.util.List;
import java.util.ArrayList;
public class ArrayListTest{
public static void main(String[] args){
List<String> list1 = new ArrayList<String>(); //default with initial capacity 10
List<String> list2 = new ArrayList<String>(5);
List<String> list3 = new ArrayList<String>(5);
list2.add(null);
list2.add(null);
list2.add(null);
list3.add("zero");
list3.add("one");
list3.add("two");
//System.out.println(list1.get(4)); //IndexOutOfBoundException
//System.out.println(list2.get(0)); //null
//System.out.println(list2.get(2)); //null;
//System.out.println(list2.get(3)); //IndexOutOfBoundException
//System.out.println(list3.get(0)); //zero
//System.out.println(list3.get(2)); //two;
//System.out.println(list3.get(3)); //IndexOutOfBoundException
//list3.add(4, "four"); //IndexOutOfBoundException
}
}
Here you can see list2.get(0) and list2.get(2) gives you null. Because we put null at these index. But list2.get(3) doesn't give null since we didn't put null at index 3. So it seems array of reference/non-primitive type won't initialize with null. list2.get(3) gives IndexOutOfBoundException.
You found the same scenario for the list3. where I didn't put any null in this list. Even when we are trying to add some value at index 4 of list3 it gives IndexOutOfBoundException. Since the index 4 is not available for the list3. But you can add some value at index 2 of list2. Cause at index 2 of this list I have inserted null manually.
So in long story short (I think) - new ArrayList<SomeType>(givenSize) wouldn't initialize an array with givenSize with all element setting to null.
Hope it will Help.
Thanks.

Related

Why are ArrayList created with empty elements array but HashSet with null table?

Maybe a bit of a philosophical question.
Looking at java's ArrayList implementation I noticed that when creating a new instance, the internal "elementData" array (that holds the items) is created as new empty array:
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
However, a HashSet (that is based on a HashMap) is created with the table and entreySet are just left null;
transient Node<K,V>[] table;
transient Set<Map.Entry<K,V>> entrySet;
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
This got me thinking so I went and looked up C#'s List and HashSet:
https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,61f6a8d9f0c40f6e
https://referencesource.microsoft.com/#System.Core/System/Collections/Generic/HashSet.cs,2d265edc718b158b
List:
static readonly T[] _emptyArray = new T[0];
public List() {
_items = _emptyArray;
}
HashSet:
private int[] m_buckets;
public HashSet()
: this(EqualityComparer<T>.Default) { }
public HashSet(IEqualityComparer<T> comparer) {
if (comparer == null) {
comparer = EqualityComparer<T>.Default;
}
this.m_comparer = comparer;
m_lastIndex = 0;
m_count = 0;
m_freeList = -1;
m_version = 0;
}
So, is there a good reason why both languages picked empty for list and null for set/map?
They both used the "single instance" for the empty array trick, which is nice, but why not just have a null array?
Answering from a C# perspective.
For an empty ArrayList, you'll find that all the logic (get, add, grow, ...) works "as-is" if you have an empty array as backing store. No need for additional code to handle the uninitialized case, this makes the whole implementation neater. And since the empty array is cached, this does not result in an additional heap allocation, so you get the cleaner code at no extra cost.
For HashSet this is not possible, as accessing a bucket is done through the formula hashCode % m_buckets.Length. Trying to compute % 0 is considered as a division by 0, and therefore invalid. This means you need to handle specifically the "not initialized" case, so you gain nothing from pre-assigning the field with an empty array.
Initializing elementData to an empty array in ArrayList allows to avoid a null check in the grow(int minCapacity) method, which calls:
elementData = Arrays.copyOf(elementData, newCapacity);
to increase the capacity of the backing array. When that method is first called, that statement will "copy" the empty array to the start of the new array (actually it will copy nothing).
In HashMap a similar strategy wouldn't be useful, since when you re-size the array of buckets, you don't copy the original array to the start of the new array, you have to go over all the entries and find the new bucket of each entry. Therefore initialing the buckets array to an empty array instead of keeping it null will require you to check if the array's length == 0 instead of checking whether it's null. Replacing one condition with another wouldn't be useful.

Deleting random elements of a list while going through the list

From what I read, it is safe to remove elements while iterating through a list. (instead of using the simple for each loop).
Suppose I have a list of elements, and I want to visit all of the elements, but while I visit each element, I will visit its neighbours (note that I have a map for each element which will give me its neighbours), thus I will
have to remove other elements from the original list. Thus I cannot use
iterator.remove().
What is a good way to do this, that is to say, remove elements from the list that I am going through without being at the position with the iterator?
One idea I had was the following, have my elements in a map, with value as
a boolean, (true for visited, and false otherwise).
Thus, I go through my list, and for each element i set it visited true, and if one if its neighbours I also see while visiting that element, I will also set them as true.
Use a for loop instead of a foreach to iterate over the items. Then remove as you see fit. Here is an example of removing even elements from the List
import java.util.List;
import java.util.ArrayList;
class Test {
public static void main(String[] args){
final List<Integer> ints = new ArrayList<Integer>();
ints.add(100);
ints.add(1);
ints.add(15);
ints.add(42);
ints.add(187);
System.out.println("Original List");
for(Integer i: ints){
System.out.println(i);
}
/* Remove any even elements from the list */
for(int i=0; i < ints.size(); i++){
if(ints.get(i) % 2 == 0){
ints.remove(i);
}
}
System.out.println("\nModified List");
for(Integer i: ints){
System.out.println(i);
}
}
}
Lets assume that you are talking about an input list that is an ArrayList.
The following approach will give you O(N) behaviour (for at least OpenJDK Java 6 through Java 8):
Create a HashSet.
Iterate over the elements of your input list:
Visit the element and add it to the set if it should be removed
Visit the neighbours of the element and add to the set any one that should be removed.
Call list.removeAll(set).
The removeAll method for ArrayList calls an internal batchRemove method (see here). This method performs a single pass over the ArrayList's backing array, removing elements and filling the holes. It tests each element to see if it should be removed by calling set.contains(elem). For a HashSet that test is O(1), and hence the removeAll(set) call is O(N) where N is the list size.
It is interesting to note that arrayList.removeAll(hashSet) will be O(N) where N is the list length, but removing the same elements like this:
for (Iterator it = arrayList.iterator; it.hasNext(); ) {
if (hashSet.contains(it.next())) {
it.remove();
}
}
will be O(NM) where N is the list length and M is the set size.

Use optional index with ArrayList - is it possible?

I am new in Android and Java developing...so maybe my question will be confused or not normal :)
Is it possible to make an ArrayList or any collection where I give the index value?
Example:
ArrayList<String> list = new ArrayList<String>();
list.add(3, "Item1");
list.add(10, "Item2");
When I try to use this I got an exception message: IndexOutOfBounds...
I want to add my ArrayList (collection) to an Android ArrayAdapter...
So! Is it possible to make a collection with optional index and it is able to add android ArrayAdapter?
Thanks the help!
You can add an element to a specific location in the ArrayList, but only if the size of the ArrayList prior to that operation is higher or equal to the index you are specifying. This means that the ArrayList can have no gaps. You can't add an elements to the index number 3 if indices 0,1,2 are not already occupied.
So, list.size() should be at least 3 in order for list.add(3, "Item1"); to be valid. Adding elements to the ArrayList in this manner increments the index of all the elements of the list starting in the given index.
Use HashMap, it should solve your problem
http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html
You can create your own List implementation which will support this functionality:
public class SparseArrayList<E> extends ArrayList<E> {
#Override
public E set(int index, E element) {
while (size() <= index) add(null);
return super.set(index, element);
}
}

Pass An Array as Argument Having a Different Start Index

How can I pass arrays in Java where I can indicate a different start index (0th index) for the array argument. In C++, you can pass arrays like this
myMethed(myArray + 3); // passing method where begin index is 4th element
...
void myMethod(int* arr) {
int val = arr[0]; // refers to the 4th element
int val2 = arr[1]; // 5th element
}
How can I do something similar in Java. I tried copying a subset of the array using Arrays.copyOfRange but I don't want a separate copy but the same array with a different start index.
Of course I can do this:
myMethod(int[] arr, int startIndex) {
int val = arr[startIndex];
int val2 = arr[startIndex + 1];
}
But are there other ways without declaring a separate parameter for start index?
Thanks a lot.
but the same array with a different start index.
In Java, an array is an object whose size is defined at the point it is initialized. The starting index is always zero and the size is fixed. These two cannot be altered after memory has been allocated. In Java same array means same object and not a memory space.
Java doesn't allow you to slice the allocated memory as a new array. So, you will always need to create another array, or explicitly pass the starting index to the method you are calling.
In general, prefer collections over arrays. Your particular need can be solved by using Lists instead:
myMethod(List<Integer> list) {
int val = list.get(0);
int val2 = list.get(1);
}
...
List<Integer> myList = ...;
myMethod(myList.subList(3, myList.size()));
From the List.subList documentation:
Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive. (If fromIndex and toIndex are equal, the returned list is empty.) The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa. The returned list supports all of the optional list operations supported by this list.
(emphasis mine)
You can create an iterator, move it to the required position and pass it as a method argument.
what about this ?
Integer myarr[] = new Integer[] { 1, 2, 3, 4 };
System.out.println(Arrays.toString(Arrays.asList(myarr).subList(2, myarr.length).toArray(new Integer[2])));

Why is my ArrayList.remove(id) call not working?

I have an ArrayList, which includes a number of items I want to remove. I have the ids of the items to remove stored in another list. Figured the following code should work trivially, but for some reason, the remove() calls are returning a false value:
ArrayList<Integer> toRemove = new ArrayList<Integer>();
ArrayList<JCheckBox> al = new ArrayList<JCheckBox>();
/* Code that adds a bunch of items to al, and a few integers to toRemove */
System.out.println("Size before removing: " + al.size());
for (int i = toRemove.size() - 1; i >= 0; i--) {
System.out.println("Removing id: " + toRemove.get(i) + ": ");
System.out.println(al.get(toRemove.get(i)));
System.out.println(al.remove(toRemove.get(i)));
}
System.out.println("Size after removing: " + al.size());
I'd get it if the get() call also returned a false value, but it does actually return the object in question. What am I missing here?
The output of the code above:
Size before removing: 3
Removing id: 2:
javax.swing.JCheckBox[...]
false
Size after removing: 3
My guess is you are having a problem with the fact that remove() is overloaded with both int and Object, while get() only takes an int. Try remove(toRemove.get(i).intValue()).
remove(Object) from AbstractCollection will search through the list and remove the given object, which won't be there because you are sending it an Integer and the list only has JCheckBoxs. You are trying to call remove(int), but because you are giving it an Integer, the Object overload is called instead. By converting the Integer to an int, you avoid this problem
Also, can you always be sure the Id in toRemove always equals the index? If toRemove is not in greatest to least order, it won't be.
There are two problems with your code.
First, the wrong "toRemove" method is getting invoked. When you call "toRemove.get(i)", the return value is autoboxed into a java.lang.Integer, instead of an int. Therefore, java.util.List#remove(Object) is called instead of java.util.List#remove(int). It's trying to remove an Integer object, and returns false.
If you cast the Integer to an int, the desired method will get invoked.
Second problem: each time you remove an element of the list, the indexes of all subsequent elements change, as those elements are "shifted" down. There are several ways to get around this. One is to sort your list of indexes in descending order. Another would be to use a set of indexes, create a new array, and copy into the new array only those elements whose index is not in the set.
javax.swing.JCheckBox[...]?
Also, try remove elements by Objects value, like here:
http://www.easywayserver.com/blog/java-remove-element-in-arraylist/

Categories

Resources