So in a Programming Lecture, the lecturer gave us some data on the performance of some Java Collections. He used the data given in this post..
Then my buddies and I decided to test it for ourselves. But our measurements were quite different from those given in the post. We tested 'add', 'contains', 'remove' and 'clear' for the collections listed below. Threw in a few maps in there too.
HashSet TreeSet LinkedHashSet ArrayList LinkedList ArrayDeque PriorityQueue HashMap TreeMap LinkedHashMap
This is our code. We do a 100 test runs and compute the average.
import java.util.*;
public class Testing {
public void initializeAndRun(int testCases){
Set<Integer> hashSet = new HashSet<>();
Set<Integer> treeSet = new TreeSet<>();
Set<Integer> linkedHashSet = new LinkedHashSet<>();
List<Integer> linkedList = new LinkedList<>();
List<Integer> arrayList = new ArrayList<>();
Queue<Integer> priorityQueue = new PriorityQueue<>();
Queue<Integer> arrayDeque = new ArrayDeque<>();
Map<Integer, Integer> hashMap = new HashMap<>();
Map<Integer, Integer> treeMap = new TreeMap<>();
Map<Integer, Integer> linkedHashMap = new LinkedHashMap<>();
testSet(hashSet, "Hash Set","add", testCases);
testSet(treeSet, "Tree Set","add", testCases);
testSet(linkedHashSet, "Linked Hash Set", "add", testCases);
testList(linkedList, "Linked List", "add", testCases);
testList(arrayList, "Array List", "add", testCases);
testQueue(priorityQueue, "Priority Queue", "add", testCases);
testQueue(arrayDeque, "Array Deque", "add", testCases);
testMap(hashMap, "Hash Map", "add", testCases);
testMap(treeMap, "Tree Map", "add", testCases);
testMap(linkedHashMap, "Linked Hash Map", "add", testCases);
System.out.println();
testSet(hashSet, "Hash Set","contains", testCases);
testSet(treeSet, "Tree Set","contains", testCases);
testSet(linkedHashSet, "Linked Hash Set", "contains", testCases);
testList(linkedList, "Linked List", "contains", testCases);
testList(arrayList, "Array List", "contains", testCases);
testQueue(priorityQueue, "Priority Queue", "contains", testCases);
testQueue(arrayDeque, "Array Deque", "contains", testCases);
testMap(hashMap, "Hash Map", "contains", testCases);
testMap(treeMap, "Tree Map", "contains", testCases);
testMap(linkedHashMap, "Linked Hash Map", "contains", testCases);
System.out.println();
testSet(hashSet, "Hash Set","remove", testCases);
testSet(treeSet, "Tree Set","remove", testCases);
testSet(linkedHashSet, "Linked Hash Set", "remove", testCases);
testList(linkedList, "Linked List", "remove", testCases);
testList(arrayList, "Array List", "remove", testCases);
testQueue(priorityQueue, "Priority Queue", "remove", testCases);
testQueue(arrayDeque, "Array Deque", "remove", testCases);
testMap(hashMap, "Hash Map", "remove", testCases);
testMap(treeMap, "Tree Map", "remove", testCases);
testMap(linkedHashMap, "Linked Hash Map", "remove", testCases);
System.out.println();
testSet(hashSet, "Hash Set","clear", testCases);
testSet(treeSet, "Tree Set","clear", testCases);
testSet(linkedHashSet, "Linked Hash Set", "clear", testCases);
testList(linkedList, "Linked List", "clear", testCases);
testList(arrayList, "Array List", "clear", testCases);
testQueue(priorityQueue, "Priority Queue", "clear", testCases);
testQueue(arrayDeque, "Array Deque", "clear", testCases);
testMap(hashMap, "Hash Map", "clear", testCases);
testMap(treeMap, "Tree Map", "clear", testCases);
testMap(linkedHashMap, "Linked Hash Map", "clear", testCases);
System.out.println();
}
private void loadSet(Set<Integer> input){
input.clear();
Random randInt = new Random();
for(int i = 0; i < 100000; i++){
Integer x = randInt.nextInt(100000);
input.add(x);
}
}
private void loadList(List<Integer> input){
input.clear();
Random randInt = new Random();
for(int i = 0; i < 100000; i++){
Integer x = Integer.valueOf(randInt.nextInt(100000));
input.add(x);
}
}
private void loadQueue(Queue<Integer> input){
input.clear();
Random randInt = new Random();
for(int i = 0; i < 100000; i++){
Integer x = Integer.valueOf(randInt.nextInt(100000));
input.add(x);
}
}
private void loadMap(Map<Integer, Integer> input){
input.clear();
Random randInt = new Random();
for(int i = 0; i < 100000; i++){
Integer x = Integer.valueOf(randInt.nextInt(100000));
input.put(x, x);
}
}
private void testSet(Set<Integer> input, String name, String test, int testNo){
long totalTime = 0;
long start = 0;
long end = 0;
Random rand = new Random();
for(int i = 0; i < testNo; i++){
loadSet(input);
Integer x = Integer.valueOf(rand.nextInt(10000));
if(test.equals("add")) {
start = System.nanoTime();
input.add(x);
end = System.nanoTime();
input.remove(x);
}else if(test.equals("contains")){
start = System.nanoTime();
input.contains(x);
end = System.nanoTime();
}else if(test.equals("remove")){
Object[] array = input.toArray();
int index = rand.nextInt(array.length);
int element = (int) array[index];
start = System.nanoTime();
input.remove(element);
end = System.nanoTime();
input.add(element);
}else if(test.equals("clear")){
start = System.nanoTime();
input.clear();
end = System.nanoTime();
}
totalTime += end - start;
}
long averageTime = totalTime/testNo;
printResult(name, test, averageTime);
}
private void testList(List<Integer> input, String name, String test, int testNo){
long totalTime = 0;
long start = 0;
long end = 0;
Random rand = new Random();
for(int i = 0; i < testNo; i++){
loadList(input);
Integer x = Integer.valueOf(rand.nextInt(10000));
if(test.equals("add")) {
start = System.nanoTime();
input.add(x);
end = System.nanoTime();
input.remove(x);
}else if(test.equals("contains")){
start = System.nanoTime();
input.contains(x);
end = System.nanoTime();
}else if(test.equals("remove")){
Object[] array = input.toArray();
int index = rand.nextInt(array.length);
int element = (int) array[index];
start = System.nanoTime();
input.remove(element);
end = System.nanoTime();
input.add(element);
}else if(test.equals("clear")){
start = System.nanoTime();
input.clear();
end = System.nanoTime();
}
totalTime += end - start;
}
long averageTime = totalTime/testNo;
printResult(name, test, averageTime);
}
private void testQueue(Queue<Integer> input, String name, String test, int testNo){
long totalTime = 0;
long start = 0;
long end = 0;
Random rand = new Random();
for(int i = 0; i < testNo; i++){
loadQueue(input);
Integer x = Integer.valueOf(rand.nextInt(10000));
if(test.equals("add")) {
start = System.nanoTime();
input.add(x);
end = System.nanoTime();
input.remove(x);
}else if(test.equals("contains")){
start = System.nanoTime();
input.contains(x);
end = System.nanoTime();
}else if(test.equals("remove")){
Object[] array = input.toArray();
int index = rand.nextInt(array.length);
int element = (int) array[index];
start = System.nanoTime();
input.remove(element);
end = System.nanoTime();
input.add(element);
}else if(test.equals("clear")){
start = System.nanoTime();
input.clear();
end = System.nanoTime();
}
totalTime += end - start;
}
long averageTime = totalTime/testNo;
printResult(name, test, averageTime);
}
private void testMap(Map<Integer, Integer> input, String name, String test, int testNo){
long totalTime = 0;
long start = 0;
long end = 0;
Random rand = new Random();
for(int i = 0; i < testNo; i++){
loadMap(input);
Integer x = Integer.valueOf(rand.nextInt(10000));
if(test.equals("add")) {
start = System.nanoTime();
input.put(x, x);
end = System.nanoTime();
input.remove(x, x);
}else if(test.equals("contains")){
start = System.nanoTime();
input.containsKey(x);
end = System.nanoTime();
}else if(test.equals("remove")){
Set<Integer> keysSet = input.keySet();
Integer[] array = new Integer[keysSet.size()];
keysSet.toArray(array);
int index = rand.nextInt(array.length);
int element = (int) array[index];
start = System.nanoTime();
input.remove(element);
end = System.nanoTime();
input.put(element, element);
}else if(test.equals("clear")){
start = System.nanoTime();
input.clear();
end = System.nanoTime();
}
totalTime += end - start;
}
long averageTime = totalTime/testNo;
printResult(name, test, averageTime);
}
private void printResult(String name, String test, long averageTime){
System.out.printf("%s: %s: %d ns%n", name,test, averageTime);
}
public static void main(String[] args){
Testing test = new Testing();
int testCases = 100;
test.initializeAndRun(testCases);
}
}
And it gave the following output: Hash Set: add: 225 ns
Tree Set: add: 430 ns
Linked Hash Set: add: 46 ns
Linked List: add: 61 ns
Array List: add: 33 ns
Priority Queue: add: 45 ns
Array Deque: add: 55 ns
Hash Map: add: 40 ns
Tree Map: add: 166 ns
Linked Hash Map: add: 43 ns
Hash Set: contains: 235 ns
Tree Set: contains: 456 ns
Linked Hash Set: contains: 57 ns
Linked List: contains: 163147 ns
Array List: contains: 45355 ns
Priority Queue: contains: 35929 ns
Array Deque: contains: 53337 ns
Hash Map: contains: 90 ns
Tree Map: contains: 380 ns
Linked Hash Map: contains: 51 ns
Hash Set: remove: 506 ns
Tree Set: remove: 1236 ns
Linked Hash Set: remove: 150 ns
Linked List: remove: 54833 ns
Array List: remove: 16115 ns
Priority Queue: remove: 31837 ns
Array Deque: remove: 35739 ns
Hash Map: remove: 110 ns
Tree Map: remove: 823 ns
Linked Hash Map: remove: 129 ns
Hash Set: clear: 32998 ns
Tree Set: clear: 322 ns
Linked Hash Set: clear: 33273 ns
Linked List: clear: 213397 ns
Array List: clear: 24334 ns
Priority Queue: clear: 25343 ns
Array Deque: clear: 45598 ns
Hash Map: clear: 33457 ns
Tree Map: clear: 27 ns
Linked Hash Map: clear: 33289 ns
As you can see, results like the 'clear' on TreeSet, when compared to others don't match at all with the values given in the lecture (the ones in the post).
Are we doing something wrong? I mean, we must be! What is it? Any help please?
We tried writing our own code, as shown above. We looked at it for ages but couldn't figure anything out. Please keep in mind that we are beginner Java programmers; only been learning for 3 weeks.
Why? The post you linked to has no code to verify their results. It doesn't even list the JVM and architecture the test was run on.
TreeSet.clear is fast. At least, it is on JDK21; I haven't looked at earlier versions, however, I'm pretty sure it has always worked this way:
TreeSetis nothing - it just creates aTreeMapand relegates every call to the map. The keys in this map are the values in your treeset; the values are irrelevant (PRESENT- a singlenew Object()made just to have some 'value' to map these keys to).ts.clear()just invokes theclear()method of the map that TreeSet is built on.Thus, 'TreeSet.clear is slow' is total horse manure, and the linked-to medium article, which has no code, no details on how that code was written or run, and at least one incredibly dubious result, should be disregarded entirely.
You should direct your professor to this question, and ensure they no longer spread this cruft to future students. Your fellow/future students thank you!