Back OCAJP
After studing String, string pool and StringBuilder, we should have some faith with java community now. Whenever there is a common requirement, the community will bring up a solution and there will be a library class for reusing the solution. String is immutable, cost too much memory to instantiate new objects, string pool came to rescue: caching String objects allows reuse String objects and reduces memory footage. When string content needs to change frequently, java.util.StringBuilder class came to rescue: StringBuilder's content is mutable, thus developer can avoid creating lots of short lived interim String objects.
Now we are facing another common problem: array can not change size at run time. Remember how we initialize array? You have to specify the array size either explicitly or implicitly, otherwise compiler will complain "array dimension missing".
OCAJP>cat test.java
import java.util.*;
class test {
public static void main(String... args) {
int[] a = new int[7];
int[] b = new int[]{1,2,3};
//int[] c = new int[]; //error: array dimension missing
int[] d = {1,2,3};
}
}
OCAJP>javac test.java
What if we need an ordered list of primitive or objects that can change size at run time? For example, we want to keep a list of contact information as we meet new friends. Sure enough, the community have brought up a solution for such a common requirement, and the solution is in one of the java library class: java.util.ArrayList. We just have to read the source code then import the class to solve our problem.
Here is the class comment of java.util.ArrayList<E>
Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null. In addition to implementing the List interface, this class provides methods to manipulate the size of the array that is used internally to store the list. (This class is roughly equivalent to Vector, except that it is unsynchronized.)
The size, isEmpty, get, set, iterator, and listIterator operations run in constant time. The add operation runs in amortized constant time, that is, adding n elements requires O(n) time. All of the other operations run in linear time (roughly speaking). The constant factor is low compared to that for the LinkedList implementation.
Each ArrayList instance has a capacity. The capacity is the size of the array used to store the elements in the list. It is always at least as large as the list size. As elements are added to an ArrayList, its capacity grows automatically. The details of the growth policy are not specified beyond the fact that adding an element has constant amortized time cost.
An application can increase the capacity of an ArrayList instance before adding a large number of elements using the ensureCapacity operation. This may reduce the amount of incremental reallocation.
Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the list. If no such object exists, the list should be "wrapped" using the Collections.synchronizedList method. This is best done at creation time, to prevent accidental unsynchronized access to the list:
List list = Collections.synchronizedList(new ArrayList(...));
The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.
This class is a member of the Java Collections Framework.
Since:
1.2
Author:
Josh Bloch
Neal Gafter
See Also:
That's a hell lot of reading before writing a single line of code, isn't it? Don't worry, salt is salt, sugar is sugar, ArrayList just do what it supposed to do. Although ArrayList is very powerful, our requirement is just a common one, we are not doing multi-threading, not strict about speed and memory usage, we just need plain salt and sugar. Let's look at OCA oriented code example.
OCAJP>cat test.java
import java.util.*;
class test {
public static void main(String... args) {
//a, b, c is declared the old way. compiler will warning: [unchecked] unchecked call to ArrayList(Collection<? extends E>) as a member of the raw type ArrayList
ArrayList a = new ArrayList(); //default initial capacity (10), element have type Object
ArrayList b = new ArrayList(20); //specify initial capacity 20
ArrayList c = new ArrayList(a); //copy size and content of a
//java 5 or above, new way
ArrayList<String> d = new ArrayList<String>(); //Generics, element have type String
ArrayList<String> e = new ArrayList<>(); //same as above, java 7 syntax
List<String> f = new ArrayList<String>(); //List interface
List<String> g = new ArrayList<>(); //List interface, java 7 syntax
System.out.println(a.add("string1")); //true, String is stored as Object
System.out.println(a.add(1L)); //autoboxing, convert 1L to Long, then store as Object
System.out.println(a.size());
//a.set(2, "something"); //java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
System.out.println("set(0, true)");
a.set(0, true); //replace "String1" Object with Boolean.true Object
System.out.println("a="+a);
for(Object o: a){
//System.out.println((String)o); //java.lang.ClassCastException: java.lang.Boolean cannot be cast to java.lang.String
System.out.println(o.toString());
}
System.out.println("remove(0)");
System.out.println(a.remove(0));
System.out.println("a="+a);
a.add("string1");
Object obj = new Integer(1);
a.add(obj);
a.add(2);
System.out.println("a="+a);
System.out.println("contains() string1, true, 2, obj?");
System.out.println(a.contains("string1"));
System.out.println(a.contains(true));
System.out.println(a.contains(2));
System.out.println(a.contains(obj));
System.out.println("clear()");
a.clear();
System.out.println(a.size());
System.out.println("a="+a);
System.out.println("b="+b);
System.out.println(a==b);
System.out.println(a.equals(b));
}
}
OCAJP>javac test.java
Note: test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
OCAJP>java test
true
true
2
set(0, true)
a=[true, 1]
true
1
remove(0)
true
a=[1]
a=[1, string1, 1, 2]
contains() string1, true, 2, obj?
true
false
true
true
clear()
0
a=[]
b=[]
false
true
Let's create the ArrayList with generic way and exercise on ArrayList methods again.
OCAJP>cat test.java
import java.util.*;
class test {
public static void main(String... args) {
//java 5 or above, new way
ArrayList<String> d = new ArrayList<String>(); //Generics, element have type String
ArrayList<String> e = new ArrayList<>(); //same as above, java 7 syntax
List<String> f = new ArrayList<String>(); //List interface
List<String> g = new ArrayList<>(); //List interface, java 7 syntax
List<String> h = new ArrayList<>(20); //initial capacity 20
g.add("Tom");
g.add("Peter");
g.add("Jerry");
g.add("David");
for(String s: g) System.out.print(s + " ");
System.out.println();
System.out.println(g.size());
g.remove(2);
System.out.println("g="+g);
System.out.println(g.contains("Jerry"));
System.out.println(g.contains("Tom"));
g.set(0, "Tomy");
System.out.println("g="+g);
System.out.println(d.isEmpty());
d = new ArrayList<>(g); //copy size and content of g
System.out.println("d="+d);
System.out.println(d.equals(g)); //unlike array, ArrayList override the Object class's equals method
System.out.println(g==d);
d.clear();
System.out.println("d="+d);
System.out.println(d.isEmpty());
System.out.println(d.size());
}
}
OCAJP>javac test.java
OCAJP>java test
Tom Peter Jerry David
4
g=[Tom, Peter, David]
false
true
g=[Tomy, Peter, David]
true
d=[Tomy, Peter, David]
true
false
d=[]
true
0
Array and List can covert to each other.
Arrays.asArray() method returns the array's backup List, the List is not the ArrayList, it is a fixed size list and change the content of the List also changes the Array.
ArrayList's toArray() method returns an array which has the same size of the ArrayList.
Static method Arrays.sort() method sorts an array; static method Collections.sort() sorts an List.
OCAJP>cat test.java
import java.util.*;
public class test{
public static void main(String...args) {
Integer[] arr = new Integer[]{1, 2, 3, 0};
List<Integer> list = Arrays.asList(arr); //backup list, not ArrayList
//ArrayList<Integer> list2 = Arrays.asList(arr); //error: incompatible types: no instance(s) of type variable(s) T exist so that List<T> conforms to ArrayList<Integer>
Collections.sort(list);
for(int i: arr) System.out.print(i + ",");
System.out.println();
List<String> list2 = Arrays.asList("a", "b", "c"); //asList() also accept varargs as parameters
ArrayList<Integer> l = new ArrayList<>();
l.add(20);
l.add(3);
l.add(5);
Integer arr1[] = l.toArray(new Integer[4]); //suggest an initial size
//Integer arr2[] = l.toArray(); //error: incompatible types: Object[] cannot be converted to Integer[]
Integer arr3[] = l.toArray(new Integer[0]); //suggest an initial size
Integer array[] = l.toArray(new Integer[3]);
Arrays.sort(array);
for(int i: array) System.out.print(i + ",");
System.out.println();
}
}
OCAJP>javac test.java
OCAJP>java test
0,1,2,3,
3,5,20,
Back OCAJP