From d37b5ada37391207595338b412ddae8359a04ff2 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 31 Oct 2013 00:35:40 -0500 Subject: [PATCH 1/9] Implement Collections#sort This is really a verbatim translation of Arrays#sort. Signed-off-by: Johannes Schindelin --- classpath/java/util/Collections.java | 115 +++++++++++++++++++++++++++ test/Collections.java | 42 ++++++++++ 2 files changed, 157 insertions(+) diff --git a/classpath/java/util/Collections.java b/classpath/java/util/Collections.java index 91fad0964b..88df95a95f 100644 --- a/classpath/java/util/Collections.java +++ b/classpath/java/util/Collections.java @@ -33,6 +33,121 @@ public class Collections { shuffle(list, new Random()); } + public static void sort(List list) { + sort(list, new Comparator() { + public int compare(Object a, Object b) { + return ((Comparable) a).compareTo(b); + } + }); + } + + private final static int SORT_SIZE_THRESHOLD = 16; + + public static void sort(List list, Comparator comparator) { + int size = list.size(); + introSort(list, comparator, 0, size, size); + insertionSort(list, comparator); + } + + private static void introSort(List list, + Comparator comparator, int begin, int end, int limit) + { + while (end - begin > SORT_SIZE_THRESHOLD) { + if (limit == 0) { + heapSort(list, comparator, begin, end); + return; + } + limit >>= 1; + + // median of three + T a = list.get(begin); + T b = list.get(begin + (end - begin) / 2 + 1); + T c = list.get(end - 1); + T median; + if (comparator.compare(a, b) < 0) { + median = comparator.compare(b, c) < 0 ? + b : (comparator.compare(a, c) < 0 ? c : a); + } else { + median = comparator.compare(b, c) > 0 ? + b : (comparator.compare(a, c) > 0 ? c : a); + } + + // partition + int pivot, i = begin, j = end; + for (;;) { + while (comparator.compare(list.get(i), median) < 0) { + ++i; + } + --j; + while (comparator.compare(median, list.get(j)) < 0) { + --j; + } + if (i >= j) { + pivot = i; + break; + } + T swap = list.get(i); + list.set(i, list.get(j)); + list.set(j, swap); + ++i; + } + + introSort(list, comparator, pivot, end, limit); + end = pivot; + } + } + + private static void heapSort(List list, Comparator comparator, + int begin, int end) + { + int count = end - begin; + for (int i = count / 2 - 1; i >= 0; --i) { + siftDown(list, comparator, i, count, begin); + } + for (int i = count - 1; i > 0; --i) { + // swap begin and begin + i + T swap = list.get(begin + i); + list.set(begin + i, list.get(begin)); + list.set(begin, swap); + + siftDown(list, comparator, 0, i, begin); + } + } + + private static void siftDown(List list, Comparator comparator, + int i, int count, int offset) + { + T value = list.get(offset + i); + while (i < count / 2) { + int child = 2 * i + 1; + if (child + 1 < count && + comparator.compare(list.get(child), list.get(child + 1)) < 0) { + ++child; + } + if (comparator.compare(value, list.get(child)) >= 0) { + break; + } + list.set(offset + i, list.get(offset + child)); + i = child; + } + list.set(offset + i, value); + } + + private static void insertionSort(List list, + Comparator comparator) + { + int size = list.size(); + for (int j = 1; j < size; ++j) { + T t = list.get(j); + int i = j - 1; + while (i >= 0 && comparator.compare(list.get(i), t) > 0) { + list.set(i + 1, list.get(i)); + --i; + } + list.set(i + 1, t); + } + } + static T[] toArray(Collection collection, T[] array) { Class c = array.getClass().getComponentType(); diff --git a/test/Collections.java b/test/Collections.java index d175318a4f..d778267dde 100644 --- a/test/Collections.java +++ b/test/Collections.java @@ -1,9 +1,12 @@ +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; public class Collections { public static void main(String[] args) { testValues(); + testSort(); } @SuppressWarnings("rawtypes") @@ -23,4 +26,43 @@ public class Collections { // expected } } + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static > void expectSorted(List list) { + for (int i = 1; i < list.size(); ++i) { + expect(list.get(i - 1).compareTo(list.get(i)) <= 0); + } + } + + private static int pseudoRandom(int seed) { + return 3170425 * seed + 132102; + } + + private static > int shuffle(List list, int seed) { + for (int i = list.size(); i > 1; --i) { + int i2 = (seed < 0 ? -seed : seed) % i; + T value = list.get(i - 1); + list.set(i - 1, list.get(i2)); + list.set(i2, value); + seed = pseudoRandom(seed); + } + return seed; + } + + public static void testSort() { + List list = new ArrayList(); + for (int i = 0; i < 64; ++i) { + list.add(Integer.valueOf(i + 1)); + } + ; + int random = 12345; + for (int i = 0; i < 32; ++i) { + random = shuffle(list, random); + java.util.Collections.sort(list); + expectSorted(list); + } + } } From a61bcf824fee551ee20652a68995314bc7dda9aa Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 31 Oct 2013 01:44:31 -0500 Subject: [PATCH 2/9] Implement Collections#binarySearch Signed-off-by: Johannes Schindelin --- classpath/java/util/Collections.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/classpath/java/util/Collections.java b/classpath/java/util/Collections.java index 88df95a95f..f6af29ae9b 100644 --- a/classpath/java/util/Collections.java +++ b/classpath/java/util/Collections.java @@ -148,6 +148,22 @@ public class Collections { } } + public static int binarySearch(List list, T needle) { + int left = -1, right = list.size(); + while (left + 1 < right) { + int middle = (left + right) >> 1; + int result = ((Comparable)needle).compareTo(list.get(middle)); + if (result < 0) { + right = middle; + } else if (result > 0) { + left = middle; + } else { + return middle; + } + } + return -1 - right; + } + static T[] toArray(Collection collection, T[] array) { Class c = array.getClass().getComponentType(); From eab3b8e448400229dc30eaca7e14a8ffbea5e95b Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 1 Nov 2013 20:18:33 -0500 Subject: [PATCH 3/9] Implement Collections#reverse Signed-off-by: Johannes Schindelin --- classpath/java/util/Collections.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/classpath/java/util/Collections.java b/classpath/java/util/Collections.java index f6af29ae9b..23db1872ac 100644 --- a/classpath/java/util/Collections.java +++ b/classpath/java/util/Collections.java @@ -164,6 +164,15 @@ public class Collections { return -1 - right; } + public static void reverse(List list) { + int ascending = 0, descending = list.size() - 1; + while (ascending < descending) { + T tmp = list.get(ascending); + list.set(ascending++, list.get(descending)); + list.set(descending--, tmp); + } + } + static T[] toArray(Collection collection, T[] array) { Class c = array.getClass().getComponentType(); From 46a55bd024c1c8edd8f7aadaf9630f98e709b123 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 1 Nov 2013 20:33:27 -0500 Subject: [PATCH 4/9] Implement ArrayList#ensureCapacity Signed-off-by: Johannes Schindelin --- classpath/java/util/ArrayList.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/classpath/java/util/ArrayList.java b/classpath/java/util/ArrayList.java index bcc3810c82..61cd5edbbf 100644 --- a/classpath/java/util/ArrayList.java +++ b/classpath/java/util/ArrayList.java @@ -65,6 +65,10 @@ public class ArrayList extends AbstractList implements java.io.Serializabl return size; } + public void ensureCapacity(int capacity) { + grow(capacity); + } + public boolean contains(Object element) { for (int i = 0; i < size; ++i) { if (equal(element, array[i])) { From 6a81623690ccc587cf8681ea42b42e2ee0fbec56 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 2 Nov 2013 00:17:50 -0500 Subject: [PATCH 5/9] Implement the Arrays#copyOf family ... as introduced in Java 6. Signed-off-by: Johannes Schindelin --- classpath/java/util/Arrays.java | 74 +++++++++++++++++++++++++++++++++ test/Arrays.java | 7 ++++ 2 files changed, 81 insertions(+) diff --git a/classpath/java/util/Arrays.java b/classpath/java/util/Arrays.java index 73a978d4bc..e640acec33 100644 --- a/classpath/java/util/Arrays.java +++ b/classpath/java/util/Arrays.java @@ -10,6 +10,8 @@ package java.util; +import java.lang.reflect.Array; + public class Arrays { private Arrays() { } @@ -348,4 +350,76 @@ public class Arrays { } } + public static boolean[] copyOf(boolean[] array, int newLength) { + boolean[] result = new boolean[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static byte[] copyOf(byte[] array, int newLength) { + byte[] result = new byte[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static char[] copyOf(char[] array, int newLength) { + char[] result = new char[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static double[] copyOf(double[] array, int newLength) { + double[] result = new double[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static float[] copyOf(float[] array, int newLength) { + float[] result = new float[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static int[] copyOf(int[] array, int newLength) { + int[] result = new int[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static long[] copyOf(long[] array, int newLength) { + long[] result = new long[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static short[] copyOf(short[] array, int newLength) { + short[] result = new short[newLength]; + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static T[] copyOf(T[] array, int newLength) { + Class clazz = array.getClass().getComponentType(); + T[] result = (T[])Array.newInstance(clazz, newLength); + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } + + public static T[] copyOf(U[] array, int newLength, + Class newType) + { + T[] result = (T[])Array.newInstance(newType.getComponentType(), newLength); + int length = array.length > newLength ? newLength : array.length; + System.arraycopy(array, 0, result, 0, length); + return result; + } } diff --git a/test/Arrays.java b/test/Arrays.java index 5a2ad8bf0a..36df4a5dc5 100644 --- a/test/Arrays.java +++ b/test/Arrays.java @@ -130,6 +130,13 @@ public class Arrays { java.util.Arrays.hashCode((Object[])null); } + { String[] list = new String[] { "Hello", "World", "!" }; + Object[] result = java.util.Arrays.copyOf(list, 2, Object[].class); + expect(list[1] == result[1]); + expect(result.length == 2); + expect(result.getClass().getComponentType() == Object.class); + } + testSort(); } } From 1ed90c38ab7e07315d1de3b1ff6f267c863be4c4 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 2 Nov 2013 00:21:03 -0500 Subject: [PATCH 6/9] Implement rest of the Arrays#fill family Signed-off-by: Johannes Schindelin --- classpath/java/util/Arrays.java | 38 ++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/classpath/java/util/Arrays.java b/classpath/java/util/Arrays.java index e640acec33..9f2ef702c5 100644 --- a/classpath/java/util/Arrays.java +++ b/classpath/java/util/Arrays.java @@ -343,7 +343,43 @@ public class Arrays { array[i] = value; } } - + + public static void fill(short[] array, short value) { + for (int i=0;i void fill(T[] array, T value) { for (int i=0;i Date: Fri, 1 Nov 2013 20:20:05 -0500 Subject: [PATCH 7/9] Implement TreeSet#descendingIterator If need be, this functionality can be sped up by implementing a descending iterator on the tree without copying it into an ArrayList. Signed-off-by: Johannes Schindelin --- classpath/java/util/TreeSet.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/classpath/java/util/TreeSet.java b/classpath/java/util/TreeSet.java index 772b6deefb..0a61748b9a 100644 --- a/classpath/java/util/TreeSet.java +++ b/classpath/java/util/TreeSet.java @@ -56,6 +56,12 @@ public class TreeSet extends AbstractSet implements Collection { return new MyIterator(set.first()); } + public Iterator descendingIterator() { + ArrayList iterable = new ArrayList(this); + Collections.reverse(iterable); + return iterable.iterator(); + } + public String toString() { return Collections.toString(this); } From 58ea1442fdb971ef8b49a34f4665b2ac0763b2b9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 31 Oct 2013 01:42:09 -0500 Subject: [PATCH 8/9] Implement LinkedHashMap This implementation is intentionally simple. If and when the need arises, we can always implement a more performant version. Signed-off-by: Johannes Schindelin --- classpath/java/util/HashMap.java | 2 +- classpath/java/util/LinkedHashMap.java | 267 +++++++++++++++++++++++++ 2 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 classpath/java/util/LinkedHashMap.java diff --git a/classpath/java/util/HashMap.java b/classpath/java/util/HashMap.java index 47050105d8..146a4eb2e1 100644 --- a/classpath/java/util/HashMap.java +++ b/classpath/java/util/HashMap.java @@ -93,7 +93,7 @@ public class HashMap implements Map { array = newArray; } - private Cell find(Object key) { + protected Cell find(Object key) { if (array != null) { int index = helper.hash(key) & (array.length - 1); for (Cell c = array[index]; c != null; c = c.next()) { diff --git a/classpath/java/util/LinkedHashMap.java b/classpath/java/util/LinkedHashMap.java new file mode 100644 index 0000000000..ba5b9c09d9 --- /dev/null +++ b/classpath/java/util/LinkedHashMap.java @@ -0,0 +1,267 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class LinkedHashMap extends HashMap { + private static class LinkedKey { + private final K key; + private LinkedKey previous, next; + + public LinkedKey(K key) { + this.key = key; + } + + public boolean equals(Object other) { + LinkedKey o = (LinkedKey) other; + return key.equals(o.key); + } + + public int hashCode() { + return key.hashCode(); + } + } + + private LinkedKey first, last; + private HashMap> lookup; + + public LinkedHashMap(int capacity) { + super(capacity); + lookup = new HashMap>(); + } + + public LinkedHashMap() { + this(0); + } + + public LinkedHashMap(Map map) { + this(map.size()); + putAll(map); + } + + public V put(K key, V value) { + if (!super.containsKey(key)) { + LinkedKey k = new LinkedKey(key); + if (first == null) { + first = k; + } else { + last.next = k; + k.previous = last; + } + last = k; + lookup.put(key, k); + } + return super.put(key, value); + } + + public V remove(Object key) { + LinkedKey linked = lookup.get(key); + if (linked == null) { + return null; + } + if (linked.previous == null) { + first = linked.next; + } else { + linked.previous.next = linked.next; + } + if (linked.next == null) { + last = linked.previous; + } else { + linked.next.previous = linked.previous; + } + return super.remove(key); + } + + public void clear() { + first = last = null; + super.clear(); + } + + public Set> entrySet() { + return new EntrySet(); + } + + public Set keySet() { + return new KeySet(); + } + + public Collection values() { + return new Values(); + } + + Iterator> iterator() { + return new MyIterator(); + } + + private class EntrySet extends AbstractSet> { + public int size() { + return LinkedHashMap.this.size(); + } + + public boolean isEmpty() { + return LinkedHashMap.this.isEmpty(); + } + + public boolean contains(Object o) { + return (o instanceof Entry) + && containsKey(((Entry)o).getKey()); + } + + public boolean add(Entry e) { + return put(e.getKey(), e.getValue()) != null; + } + + public boolean remove(Object o) { + return (o instanceof Entry) && remove((Entry)o); + } + + public boolean remove(Entry e) { + return LinkedHashMap.this.remove(e.getKey()) != null; + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + return Collections.toArray(this, array); + } + + public void clear() { + LinkedHashMap.this.clear(); + } + + public Iterator> iterator() { + return new MyIterator(); + } + } + + private class KeySet extends AbstractSet { + public int size() { + return LinkedHashMap.this.size(); + } + + public boolean isEmpty() { + return LinkedHashMap.this.isEmpty(); + } + + public boolean contains(Object key) { + return containsKey(key); + } + + public boolean add(K key) { + return put(key, null) != null; + } + + public boolean remove(Object key) { + return LinkedHashMap.this.remove(key) != null; + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + return Collections.toArray(this, array); + } + + public void clear() { + LinkedHashMap.this.clear(); + } + + public Iterator iterator() { + return new Collections.KeyIterator(new MyIterator()); + } + } + + private class Values implements Collection { + public int size() { + return LinkedHashMap.this.size(); + } + + public boolean isEmpty() { + return LinkedHashMap.this.isEmpty(); + } + + public boolean contains(Object value) { + return containsValue(value); + } + + public boolean containsAll(Collection c) { + if (c == null) { + throw new NullPointerException("collection is null"); + } + + Iterator it = c.iterator(); + while (it.hasNext()) { + if (! contains(it.next())) { + return false; + } + } + + return true; + } + + public boolean add(V value) { + throw new UnsupportedOperationException(); + } + + public boolean addAll(Collection collection) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object value) { + throw new UnsupportedOperationException(); + } + + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + return Collections.toArray(this, array); + } + + public void clear() { + LinkedHashMap.this.clear(); + } + + public Iterator iterator() { + return new Collections.ValueIterator(new MyIterator()); + } + } + + private class MyIterator implements Iterator> { + private LinkedKey current = first; + + public Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Entry result = find(current.key); + current = current.next; + return result; + } + + public boolean hasNext() { + return current != null; + } + + public void remove() { + LinkedHashMap.this.remove(current == null ? + last.key : current.previous.key); + } + } +} + From 7fe39792804eed23351fc72c997883f473f15680 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 31 Oct 2013 01:42:09 -0500 Subject: [PATCH 9/9] Implement LinkedHashSet This implementation is intentionally simple. If and when the need arises, we can always implement a more performant version. Signed-off-by: Johannes Schindelin --- classpath/java/util/LinkedHashSet.java | 89 ++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 classpath/java/util/LinkedHashSet.java diff --git a/classpath/java/util/LinkedHashSet.java b/classpath/java/util/LinkedHashSet.java new file mode 100644 index 0000000000..31efc6c26d --- /dev/null +++ b/classpath/java/util/LinkedHashSet.java @@ -0,0 +1,89 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class LinkedHashSet extends AbstractSet implements Set { + private static final Object Value = new Object(); + + private final LinkedHashMap map; + + public LinkedHashSet(Collection c) { + map = new LinkedHashMap(c.size()); + addAll(c); + } + + public LinkedHashSet(int capacity) { + map = new LinkedHashMap(capacity); + } + + public LinkedHashSet() { + this(0); + } + + public int size() { + return map.size(); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public boolean contains(Object element) { + return map.containsKey(element); + } + + public boolean add(T element) { + return map.put(element, Value) != Value; + } + + public boolean addAll(Collection collection) { + boolean change = false; + for (T t: collection) if (add(t)) change = true; + return change; + } + + public boolean remove(Object element) { + return map.remove(element) != Value; + } + + public void clear() { + map.clear(); + } + + public Iterator iterator() { + return new MyIterator(map.iterator()); + } + + public String toString() { + return Collections.toString(this); + } + + private static class MyIterator implements Iterator { + private final Iterator> it; + + public MyIterator(Iterator> it) { + this.it = it; + } + + public T next() { + return it.next().getKey(); + } + + public boolean hasNext() { + return it.hasNext(); + } + + public void remove() { + it.remove(); + } + } +} +