1. Internally an ArrayList uses an Object[] Array.
           private transient Object[] elementData;
         
  2. As you add items to an ArrayList, the list checks to see if the backing array has room left. If there is room, the new item is just added at the next empty space. If there is not room, a new, larger, array is created, and the old array is copied into the new one.
  3. When we actually create an arrayList following piece of code is executed –
      this.elementData=new Object[initial capacity];
         
  4. ArrayList can be created in two ways-

    List<String> myList=new ArrayList<String>();
     
  5. When we create an ArrayList in this way, default constructor is invoked and will internally create an array of Object with default size, which is 10.

    List<String> myList=new ArrayList<String>(5);
     

    When we create an ArrayList in this way, constructor with an integer argument is invoked and will internally create an array of Object with the size, specified in the constructor argument, which happens to be 5 in this case.

  6. Inside add() method Check is made, before adding element into the array it will check what is the current size of filled elements and what is the maximum size of the array. If size of filled elements is greater than maximum size of the array then size of the array must be increased. But we know that the size of the array cannot be increased dynamically.

    So what happens internally is a new Array is created with size 1.5*currentSize and the data from old Array is copied into this new Array.

Internally an ArrayList uses an Object[].

Capacity vs Size

The capacity is how many elements the list can potentially accommodate without reallocating its internal structures.

The size is the number of elements in the list

If you allocate a new array with arr = new Employee[100], the size of that array (arr.length) is going to be 100. It has 100 elements. All the elements are initially null (as this is an array of object references), but still, there are 100 elements.

If you do something like list = new ArrayList (100), and try to check list.size(), you’ll get 0. There are no elements in the list.

Internally, it’s true that the ArrayList allocates enough place to put 100 items before it needs to extend its capacity, but that’s an internal implementation detail, and the list presents its content to you as “no items stored”. Only if you actually do list.add(something), you’ll have items in the list.

So although the list allocates storage in advance, the API with which it communicates with the program tells you there are no items in it. The null items in its internal array are not available to you – you cannot retrieve them or change them.

An ArrayList is just one way to represent an abstract list, and the capacity of an ArrayList is an implementation detail of how the system implements the logical list.

An ArrayList stores the elements of a list by using an actual array “under the covers.” The actual realization of the array in computer memory has a certain size when it is allocated; this size is the ArrayList’s capacity. The ArrayList emulates a variable-sized list by storing the logical length of the list in addition to the fixed-length array. Thus if you have an ArrayList with a capacity 10 which contains 4 logical elements, the ArrayList can be represented as a length and an array

(4) | e1 | e2 | e3 | e4 | __ | __ | __| __ | __ | __ |

where the (4) is the logical length of the list and ‘__’ represent data that is ignored because it is not part of the logical list. If you attempt to access the 5th element of this ArrayList, it will throw an exception because it knows that the fifth element has not been initialized. If we then append an extra element e5 to the list, the ArrayList becomes

(5) | e1 | e2 | e3 | e4 | e5 | __ | __ | __ | __ | __ |

Note that the capacity has not changed, while the logical length has, because the underlying array is still able to handle all the data in the logical list.

If you manage to add more than ten elements to this list, the ArrayList will not break. The ArrayList is an abstraction meant to be compatible with all array operations. Rather, the ArrayList changes its capacity when its logical length exceeds its original capacity. If we were to add the elements (a1, a2, …, a7) to the above list, the resulting ArrayList might look like

(12) | e1 | e2 | e3 | e4 | e5 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | __ | __ | __ | __ | __ | __ | __ | __ |

with a capacity of 20.

Once you have created an ArrayList, you can ignore the capacity in all programming that follows; the logic is unaffected. However, the performance of the system under certain kinds of operations can be affected. Increasing the capacity, for instance, might well involved allocating a larger array, copying the first array into the second and then performing the operations. This can be quite slow compared to, e.g. the same operation on a linked list. Thus it is sensible to choose the capacity of an ArrayList to be bigger than, or at least comparable to, the actual number of elements expected in the real runtime environment.

Is there a Way to Create ArrayList of Fixed size in Java

 List<String> fixedSizeList = Arrays.asList(new String[100]);

You cannot insert new Strings to the fixedSizeList (it already has 100 elements). You can only set its values like this:

fixedSizeList.set(7, "new value");

What would be the Output of the Following Code

List<Employee> employees = new ArrayList<>(100);
int size = employes.size();

size will be 0 while the initial capacity is 100.

Return Types in ArrayList add Method

Test.java

public class Test 
{
  public static void main(String[] args) 
  {		
    List arraList = new ArrayList<String>();
    System.out.println(arraList.add("Mugil"));
  }
}

Output

True

But the below add method is of void return type

public class Test 
{
  public static void main(String[] args) 
  {		
    List arraList = new ArrayList<String>();
    System.out.println(arraList.add(1, "Mugil"));
  }
}

Why it is So?
Collection.add is a pretty generic method (widely applicable). As such, they wanted a return value that would apply generally.

Some classes (like ArrayList) always accept elements irrespective of element already in the list(duplicate element), and so will always return true. In these cases, a return type of void is more then enough.

If a collection refuses to add a particular element for any reason other than that it already contains the element say Set, it must throw an exception (rather than returning false).

So if it returns true, the element was added, if it returns false the element was already there (such as in a Set) and in other cases an exception needs to be thrown (for example if a Collection would limit its size and not block).

Differences is that the contract for add(E) is defined in Collection, whereas add(int index, E e) is defined in the List interface (and doesn’t need to return anything). It could return true as well, but it would be useless. The other method has to return true, because otherwise it would break the contract for Collection.

The way the Set Collection could have been is

if (!set.contains(item)) 
{
    set.add(item);
    itemWasAdded(item);
}

The extra checking of Contains is skipped in below actual java code design

if (set.add(item)) 
{
    itemWasAdded(item);
}

But this check-then-act behavior isn’t thread safe, which can be crucial in multithreaded applications. For instance, it could be that another thread added an equal item between you checking set.contains(item) and the set.add(item) in the first code snippet. In a multithreaded scenario, those two actions really need to be a single, atomic action; returning boolean from the method makes that possible.

modcount of the list lets you know if there has been a structural modification made that might cause the current operation to give incorrect results.

  List<String> arrNames = new ArrayList<String>();
  arrNames.add("Mugil");
  arrNames.add("Vinu");
  arrNames.add("Bala");
  arrNames.add("Madhu");		
  arrNames.remove(0);		
  arrNames.add("Swathi");		
  arrNames.remove(1);

When the control reaches
Line 6 The Size and Modcount of the arrNames would be 4
Line 7 The Size and Modcount would be 5 and size would be 3
Line 7 The Size and Modcount would be 6 and size would be 4

Fine the Screenshots below for further Details

The below code Generate java.util.ConcurrentModificationException

 List<Fruits> arrFruits = new ArrayList<Fruits>();

 Fruits objApple  = new Apple();
 Fruits objOrange = new Orange();
 Fruits objMango  = new Mango();

 arrFruits.add(objApple);
 arrFruits.add(objOrange);
 arrFruits.add(objMango);

 List<Fruits> arrNewFruits = arrFruits.subList(0, 1);
 arrFruits.retainAll(arrNewFruits); 

In your above code examples you I have a big list and the sub-list in reverse order.
When I invoke retainAll() on the sub-list, no modifications will occur.
This is because each element in the sub-list is in the big list.
If no modification occurs, no ConcurrentModificationException will be thrown.


If I reverse the order and invoke retainAll() on the big list, it will get mutated.
This is because not every item in the big list is in the sub-list.
When I remove an element from the big list, a ConcurrentModificationException is thrown.
This is because I cannot mutate a list while iterating over it.

The iteration takes place in the retainAll() method.
In my code, the list argument happens to reference the same list that’s being modified.
This is because of the way List.subList() works:

arrNewFruits.retainAll(arrFruits) //This Works

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.

I should create a new list from the sub-list if there is a chance that either list will get modified while one of the lists is being iterated over.

I can create a new list from the sub-list like below

List<Foo> freshList = new ArrayList<Foo>(bigList.subList(0,2));

Now I can iterate and mutate.

  • List maintains elements in Particular Sequence.
  • Two types of List 1.ArrayList and 2.LinkedList
  • ArrayList is faster while accessing elements but slower while Insertion and Deletion
  • LinkedList is slow in random access but faster during insertion and deletion

Operations in ArrayList

class Fruits
{	
  String Name = "Fruit";
}

class Apple extends Fruits
{	
 public Apple()
 {
   Name = "Apple";
 }
}

class Orange extends Fruits
{	
 public Orange()
 {
  Name = "Orange";
 }
}

class Mango extends Fruits
{	
 public Mango()
 {
  Name = "Mango";
 }
}

Adding Element – List.add()

List<Fruits> arrFruits = new ArrayList<Fruits>();
Fruits objApple  = new Apple();
Fruits objMango  = new Mango();
arrFruits.add(objApple);
arrFruits.add(objMango);

Removing Element – List.remove()

arrFruits.remove(objApple);

Removing by Index – List.remove()

arrFruits.remove(1);

Index of Element – List.indexOf()

arrFruits.indexOf(objMango);

Index of Element – List.indexOf()

arrFruits.indexOf(objMango);

SubList – List.subList(StartPOS, EndPOS)

List<Fruits> arrNewFruits = arrFruits.subList(0,2);

containsAll – NewList.containsAll(OrgList)

 arrNewFruits.containsAll(arrFruits);

set Value at Index

 arrNewFruits.set(1, new Orange());

removeAll

 arrFruits.removeAll(arrFruits);

addAll

 arrFruits.addAll(arrNewFruits);

retainAll – set Intersection Operation
retainAll performs set Interscetion Operation by taking Two ArrayList. It Retains Boolean true if intersection finds element false otherwise.

List<Integer> arrNums1 = new ArrayList<Integer>();
arrNums1.add(1);
arrNums1.add(2);
arrNums1.add(3);
		
List<Integer> arrNums2 = new ArrayList<Integer>();
arrNums2.add(1);

System.out.println(arrNums1.retainAll(arrNums2));
System.out.println(arrNums1);