/*
Code example used in blog-article: "Java Battle Royal: LinkedList vs ArrayList vs DynamicIntArray"
http://k...content-available-to-author-only...s.com/2012/08/08/java-galore-linkedlist-vs-arraylist-vs-dynamicintarray/
Comparing ArrayList, DynamicIntArray, LinkedList
Scope of code:
[Number crunching example that should favmour the linked list]
[...] filter a collection of elements, removing the values that are above or below a threshold. This "should" be perfect for an linked list ... but it is shown here that it can with just a barely better approach then the naive approach will still beat the LinkedList
See the article for explanation
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Random;
import java.util.List;
public class Main {
class DynamicIntArray {
private int[] storage;
private int size;
private final int INITIAL_CAPACITY = 8;
private final int GROW_FACTOR = 2;
private void rangeCheck(int index){
if(index < 0 || index >= size)
}
// ref: http://w...content-available-to-author-only...t.net/Data_structures/Dynamic_array/Capacity_management
private void ensureCapacity(int size_wanted) {
int max_capacity = storage.length;
if (size_wanted > max_capacity) {
max_capacity = max_capacity * GROW_FACTOR +1;
// http://d...content-available-to-author-only...e.com/javase/6/docs/api/java/util/Arrays.html#copyOf%28int[],%20int%29
storage
= Arrays.
copyOf(storage, max_capacity
); // increases array size + copy contents }
}
public DynamicIntArray() {
storage = new int[INITIAL_CAPACITY];
size = 0;
}
public DynamicIntArray(int capacity) {
storage = new int[capacity];
size = 0;
}
public int size() {return size;}
public boolean equals(List<Integer> list)
{
boolean success = (list.size() == size());
if(success) {
for(int idx = 0; success && (idx < size()); ++idx) {
success = success && (get(idx) == list.get(idx).intValue());
}
}
return success;
}
public int get(int position){
rangeCheck(position);
return storage[position];
}
public void set(int index, int value){
rangeCheck(index);
storage[index] = value;
}
public void add(int value) {
insertAt(size, value); // same as c++ std::vector push_back
}
// Insert the value at the given index and shift 'tail' to give space.
// The value can either be last position (size) or within the range 0 -> size.
public void insertAt(int index, int value) {
if(index < 0 || index > size) // allows for push_back to last index also
ensureCapacity(size+1);
int move_count = size - index; // number of elements to shift
if( move_count > 0)
System.
arraycopy(storage, index, storage, index
+1, move_count
);
storage[index] = value;
size++;
}
public void removeAt(int index) {
rangeCheck(index);
int move_count = size - index - 1;
if (move_count > 0)
System.
arraycopy(storage, index
+ 1, storage, index, move_count
);
size--;
}
public void printAll() {
for (int idx = 0; idx < size; idx++)
System.
out.
printf(" [%d]=%d", idx, get
(idx
));
}
} // DynamicIntArray
// Measuring filtering/remove of too large values
//Get the jvm heap size.
//long heapSize = Runtime.getRuntime().totalMemory();
//Print the jvm heap size.
//System.out.println("Heap Size = " + heapSize);
Main obj = new Main();
System.
out.
println("NO WARMUP: Repeating the same 'number-of-elements' changes the timing result"); System.
out.
println(" just a nice 'present' for using java\n\n");
System.
out.
println("\n\n**************************"); obj.performanceOfFilter(10);
obj.performanceOfFilter(1000);
obj.performanceOfFilter(100000);
}
public void performanceOfFilter(int numbers) {
System.
out.
println("Filter containers with random " + numbers
+ " numbers"); System.
out.
println("Performance is measured in microseconds [us]\n");
DynamicIntArray dynamicArray = new DynamicIntArray();
for (int i = 0; i < array.length; i++)
{
int value
= random.
nextInt(Integer.
MAX_VALUE); array[i] = value;
dynamicArray.add(value);
}
LinkedList
<Integer
> linkedList
= new LinkedList
<Integer
>(Arrays.
asList(array
)); ArrayList
<Integer
> arrayListNaive
= new ArrayList
<Integer
>(Arrays.
asList(array
)); ArrayList
<Integer
> arrayListLessNaive
= new ArrayList
<Integer
>(Arrays.
asList(array
)); ArrayList
<Integer
> arrayListSmart
= new ArrayList
<Integer
>(Arrays.
asList(array
)); ArrayList
<Integer
> arrayListSmartReplace
= new ArrayList
<Integer
>(Arrays.
asList(array
));
/** ArrayList : Naive. Working against the natural direction of the array.
Lots of unnecessary shifting */
long before
= System.
nanoTime(); for (int i = 0; i < arrayListNaive.size(); i++)
if (arrayListNaive.
get(i
) > Integer.
MAX_VALUE / 2) arrayListNaive.remove(i--);
System.
out.
println("Naive ArrayList \t\t" +(System.
nanoTime() - before
) / 1000);
/** ArrayList : Less naive. Working with filtering in the 'direction of growth'
i.e. some 'shuffling' can be avoided */
for (int i = arrayListLessNaive.size() -1; i >= 0; i--)
if (arrayListLessNaive.
get(i
) > Integer.
MAX_VALUE / 2) arrayListLessNaive.remove(i);
System.
out.
println("Less naive ArrayList \t\t" +(System.
nanoTime() - before
) / 1000);
/** ArrayList: From David, suggesting a smart algorithm where set (replace) is used before trimming.
The small enough items are set after each other at the front.
Larger items or 'copies' at the end are removed by trimming */
int insert = 0;
for (int i = 0; i < arrayListSmartReplace.size(); i++){
if (arrayListSmartReplace.
get(i
) <= Integer.
MAX_VALUE / 2){ // small enough items are kept; arrayListSmartReplace.set(insert, arrayListSmartReplace.get(i)); // replace
insert++;
}
}
while (arrayListSmartReplace.size() > insert) // trim/filter according to count
arrayListSmartReplace.remove(arrayListSmartReplace.size() - 1);
System.
out.
println("Davids 'set/replace' ArrayList \t" + (System.
nanoTime() - before
) / 1000);
/** Linked-List: Very fast in this scenario. It is cache unfriendly, but avoids the shuffling intensive
operations that the 'array' structures use */
for (Iterator<Integer> it = linkedList.iterator(); it.hasNext(); )
if (it.
next() > Integer.
MAX_VALUE / 2) it.remove();
System.
out.
println("LinkedList \t\t\t"+ (System.
nanoTime() - before
) / 1000);
/** ArrayList : Smart. Avoiding unnecessary shuffling by using a "work copy" */
ArrayList arraylist_workcopy
= new ArrayList(arrayListSmart.
size()); // pre-allocating work copy makes sense for (int i = 0; i < arrayListSmart.size(); i++)
if (arrayListSmart.
get(i
) <= Integer.
MAX_VALUE / 2) arraylist_workcopy.add(arrayListSmart.get(i));
System.
out.
println("Smart ArrayList\t\t\t" +(System.
nanoTime() - before
) / 1000);
/** DynamicIntArray "smarter" (cache friendly) and uses a pre-allocated work copy */
DynamicIntArray dynamic_workcopy= new DynamicIntArray(dynamicArray.size());
for (int i = 0; i < dynamicArray.size(); i++){
if (dynamicArray.
get(i
) <= (Integer.
MAX_VALUE / 2)) dynamic_workcopy.add(dynamicArray.get(i));
}
System.
out.
println("Smart DynamicIntArray \t\t" + (System.
nanoTime() - before
) / 1000);
boolean all_equal = arrayListNaive.equals(linkedList) &&
arrayListLessNaive.equals(arrayListNaive) &&
arrayListSmartReplace.equals(arrayListLessNaive) &&
arraylist_workcopy.equals(arrayListSmartReplace) &&
dynamic_workcopy.equals(arrayListNaive);
System.
out.
println("All containers filtered equally: " + all_equal
); System.
out.
println("**************************************\n\n\n"); }
}