classlib: fix issues related to ArrayList, Arrays and Comparator

This commit is contained in:
Ivan Hetman 2023-04-03 11:07:28 +03:00 committed by GitHub
parent c5572bc573
commit a1074918cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 157 additions and 40 deletions

View File

@ -99,8 +99,7 @@ public abstract class TAbstractList<E> extends TAbstractCollection<E> implements
public int indexOf(Object o) { public int indexOf(Object o) {
int sz = size(); int sz = size();
for (int i = 0; i < sz; ++i) { for (int i = 0; i < sz; ++i) {
Object e = get(i); if (TObjects.equals(o, get(i))) {
if (o == null ? e == null : o.equals(e)) {
return i; return i;
} }
} }
@ -111,8 +110,7 @@ public abstract class TAbstractList<E> extends TAbstractCollection<E> implements
public int lastIndexOf(Object o) { public int lastIndexOf(Object o) {
int sz = size(); int sz = size();
for (int i = sz - 1; i >= 0; --i) { for (int i = sz - 1; i >= 0; --i) {
Object e = get(i); if (TObjects.equals(o, get(i))) {
if (o == null ? e == null : o.equals(e)) {
return i; return i;
} }
} }

View File

@ -129,6 +129,7 @@ public class TArrayList<E> extends TAbstractList<E> implements TCloneable, TSeri
public void clear() { public void clear() {
Arrays.fill(array, 0, size, null); Arrays.fill(array, 0, size, null);
size = 0; size = 0;
++modCount;
} }
@Override @Override
@ -210,4 +211,10 @@ public class TArrayList<E> extends TAbstractList<E> implements TCloneable, TSeri
buffer.append(array[length] == this ? "(this Collection)" : array[length]); buffer.append(array[length] == this ? "(this Collection)" : array[length]);
return buffer.append(']').toString(); return buffer.append(']').toString();
} }
@Override
public void sort(TComparator<? super E> comp) {
TArrays.sort(array, 0, size, comp);
modCount++;
}
} }

View File

@ -912,27 +912,17 @@ public class TArrays extends TObject {
} }
public static void sort(Object[] a) { public static void sort(Object[] a) {
sort(a, new NaturalOrder()); sort(a, TComparator.NaturalOrder.instance());
} }
public static void sort(Object[] a, int fromIndex, int toIndex) { public static void sort(Object[] a, int fromIndex, int toIndex) {
sort(a, fromIndex, toIndex, new NaturalOrder()); sort(a, fromIndex, toIndex, TComparator.NaturalOrder.instance());
}
private static class NaturalOrder implements TComparator<Object> {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override public int compare(Object o1, Object o2) {
if (o1 != null) {
return ((TComparable) o1).compareTo(o2);
} else if (o2 != null) {
return ((TComparable) o2).compareTo(o1);
} else {
return 0;
}
}
} }
public static <T> void sort(T[] a, int fromIndex, int toIndex, TComparator<? super T> c) { public static <T> void sort(T[] a, int fromIndex, int toIndex, TComparator<? super T> c) {
if (c == null) {
c = TComparator.NaturalOrder.instance();
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
T[] subarray = (T[]) new Object[toIndex - fromIndex]; T[] subarray = (T[]) new Object[toIndex - fromIndex];
for (int i = fromIndex; i < toIndex; ++i) { for (int i = fromIndex; i < toIndex; ++i) {
@ -949,6 +939,9 @@ public class TArrays extends TObject {
if (a.length == 0) { if (a.length == 0) {
return; return;
} }
if (c == null) {
c = TComparator.NaturalOrder.instance();
}
Object[] first = a; Object[] first = a;
Object[] second = new Object[a.length]; Object[] second = new Object[a.length];
int chunkSize = 1; int chunkSize = 1;
@ -1225,7 +1218,7 @@ public class TArrays extends TObject {
} }
public static int binarySearch(Object[] a, int fromIndex, int toIndex, Object key) { public static int binarySearch(Object[] a, int fromIndex, int toIndex, Object key) {
return binarySearch(a, fromIndex, toIndex, key, new NaturalOrder()); return binarySearch(a, fromIndex, toIndex, key, TComparator.NaturalOrder.instance());
} }
public static <T> int binarySearch(T[] a, T key, TComparator<? super T> c) { public static <T> int binarySearch(T[] a, T key, TComparator<? super T> c) {
@ -1233,6 +1226,9 @@ public class TArrays extends TObject {
} }
public static <T> int binarySearch(T[] a, int fromIndex, int toIndex, T key, TComparator<? super T> c) { public static <T> int binarySearch(T[] a, int fromIndex, int toIndex, T key, TComparator<? super T> c) {
if (c == null) {
c = TComparator.NaturalOrder.instance();
}
if (fromIndex > toIndex) { if (fromIndex > toIndex) {
throw new TIllegalArgumentException(); throw new TIllegalArgumentException();
} }

View File

@ -216,7 +216,7 @@ public class TCollections extends TObject {
public static <T> void sort(TList<T> list, TComparator<? super T> c) { public static <T> void sort(TList<T> list, TComparator<? super T> c) {
if (c == null) { if (c == null) {
c = naturalOrder; c = TComparator.NaturalOrder.instance();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
T[] array = (T[]) new Object[list.size()]; T[] array = (T[]) new Object[list.size()];
@ -228,7 +228,7 @@ public class TCollections extends TObject {
} }
public static <T extends TComparable<? super T>> void sort(TList<T> list) { public static <T extends TComparable<? super T>> void sort(TList<T> list) {
sort(list, naturalOrder); sort(list, TComparator.NaturalOrder.instance());
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -244,20 +244,15 @@ public class TCollections extends TObject {
} }
public static <T> int binarySearch(TList<? extends TComparable<? super T>> list, T key) { public static <T> int binarySearch(TList<? extends TComparable<? super T>> list, T key) {
return binarySearch(list, key, naturalOrder); return binarySearch(list, key, TComparator.NaturalOrder.instance());
} }
@SuppressWarnings("unchecked")
private static TComparator<Object> naturalOrder = (o1, o2) -> o1 != null
? ((TComparable<Object>) o1).compareTo(o2)
: -((TComparable<Object>) o2).compareTo(o1);
public static <T> int binarySearch(TList<? extends T> list, T key, TComparator<? super T> c) { public static <T> int binarySearch(TList<? extends T> list, T key, TComparator<? super T> c) {
if (!(list instanceof TRandomAccess)) { if (!(list instanceof TRandomAccess)) {
list = new TArrayList<>(list); list = new TArrayList<>(list);
} }
if (c == null) { if (c == null) {
c = naturalOrder; c = TComparator.NaturalOrder.instance();
} }
int l = 0; int l = 0;
int u = list.size() - 1; int u = list.size() - 1;
@ -339,12 +334,12 @@ public class TCollections extends TObject {
} }
public static <T extends Object & TComparable<? super T>> T min(TCollection<? extends T> coll) { public static <T extends Object & TComparable<? super T>> T min(TCollection<? extends T> coll) {
return min(coll, naturalOrder); return min(coll, TComparator.NaturalOrder.instance());
} }
public static <T> T min(TCollection<? extends T> coll, TComparator<? super T> comp) { public static <T> T min(TCollection<? extends T> coll, TComparator<? super T> comp) {
if (comp == null) { if (comp == null) {
comp = naturalOrder; comp = TComparator.NaturalOrder.instance();
} }
TIterator<? extends T> iter = coll.iterator(); TIterator<? extends T> iter = coll.iterator();
T min = iter.next(); T min = iter.next();
@ -358,12 +353,12 @@ public class TCollections extends TObject {
} }
public static <T extends Object & TComparable<? super T>> T max(TCollection<? extends T> coll) { public static <T extends Object & TComparable<? super T>> T max(TCollection<? extends T> coll) {
return max(coll, naturalOrder); return max(coll, TComparator.NaturalOrder.instance());
} }
public static <T> T max(TCollection<? extends T> coll, TComparator<? super T> comp) { public static <T> T max(TCollection<? extends T> coll, TComparator<? super T> comp) {
if (comp == null) { if (comp == null) {
comp = naturalOrder; comp = TComparator.NaturalOrder.instance();
} }
TIterator<? extends T> iter = coll.iterator(); TIterator<? extends T> iter = coll.iterator();
T max = iter.next(); T max = iter.next();
@ -557,9 +552,7 @@ public class TCollections extends TObject {
return (TComparator<T>) reverseOrder; return (TComparator<T>) reverseOrder;
} }
private static TComparator<Object> reverseOrder = (o1, o2) -> o1 != null private static TComparator<Object> reverseOrder = (o1, o2) -> ((TComparable<Object>) o2).compareTo(o1);
? -((TComparable<Object>) o1).compareTo(o2)
: ((TComparable<Object>) o2).compareTo(o1);
public static <T> TComparator<T> reverseOrder(final TComparator<T> cmp) { public static <T> TComparator<T> reverseOrder(final TComparator<T> cmp) {
return (o1, o2) -> -cmp.compare(o1, o2); return (o1, o2) -> -cmp.compare(o1, o2);

View File

@ -56,7 +56,7 @@ public interface TComparator<T> {
if (r == 0) { if (r == 0) {
U k = keyExtractor.apply(a); U k = keyExtractor.apply(a);
U m = keyExtractor.apply(b); U m = keyExtractor.apply(b);
r = k != null ? k.compareTo(m) : -m.compareTo(k); r = k.compareTo(m);
} }
return r; return r;
}; };
@ -102,16 +102,31 @@ public interface TComparator<T> {
return (a, b) -> { return (a, b) -> {
U k = keyExtractor.apply(a); U k = keyExtractor.apply(a);
U m = keyExtractor.apply(b); U m = keyExtractor.apply(b);
return k != null ? k.compareTo(m) : -m.compareTo(k); return k.compareTo(m);
}; };
} }
class NaturalOrder implements TComparator<Object> {
private static final TComparator<Object> INSTANCE = new NaturalOrder();
@Override
@SuppressWarnings("unchecked")
public int compare(Object o1, Object o2) {
return ((Comparable<Object>) o1).compareTo(o2);
}
@SuppressWarnings("unchecked")
static <T> TComparator<T> instance() {
return (TComparator<T>) INSTANCE;
}
}
static <T extends TComparable<? super T>> TComparator<T> naturalOrder() { static <T extends TComparable<? super T>> TComparator<T> naturalOrder() {
return (o1, o2) -> o1 != null ? o1.compareTo(o2) : o2.compareTo(o1); return NaturalOrder.instance();
} }
static <T extends TComparable<? super T>> TComparator<T> reverseOrder() { static <T extends TComparable<? super T>> TComparator<T> reverseOrder() {
return (o1, o2) -> o2 != null ? o2.compareTo(o1) : o1.compareTo(o2); return TCollections.reverseOrder();
} }
static <T> TComparator<T> nullsFirst(TComparator<? super T> comparator) { static <T> TComparator<T> nullsFirst(TComparator<? super T> comparator) {

View File

@ -18,6 +18,7 @@ package org.teavm.classlib.java.util;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.ConcurrentModificationException; import java.util.ConcurrentModificationException;
import java.util.List; import java.util.List;
import org.junit.Test; import org.junit.Test;
@ -184,4 +185,17 @@ public class ArrayListTest {
assertEquals("[(this Collection), A]", list.toString()); assertEquals("[(this Collection), A]", list.toString());
assertEquals("[]", new ArrayList().toString()); assertEquals("[]", new ArrayList().toString());
} }
@Test
public void testSort() {
int size = 10;
List<Integer> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
list.add(size - 1 - i);
}
list.sort(Comparator.naturalOrder());
for (int i = 0; i < size; i++) {
assertEquals(i, list.get(i).intValue());
}
}
} }

View File

@ -37,6 +37,7 @@ public class ArraysTest {
assertEquals(Integer.valueOf(5), array[3]); assertEquals(Integer.valueOf(5), array[3]);
assertEquals(Integer.valueOf(6), array[4]); assertEquals(Integer.valueOf(6), array[4]);
assertEquals(Integer.valueOf(7), array[5]); assertEquals(Integer.valueOf(7), array[5]);
Arrays.sort(array, null); // NPE check
} }
@Test @Test
@ -50,6 +51,7 @@ public class ArraysTest {
assertEquals(-3, Arrays.binarySearch(array, 5)); assertEquals(-3, Arrays.binarySearch(array, 5));
assertEquals(-8, Arrays.binarySearch(array, 15)); assertEquals(-8, Arrays.binarySearch(array, 15));
assertEquals(-9, Arrays.binarySearch(array, 17)); assertEquals(-9, Arrays.binarySearch(array, 17));
assertEquals(3, Arrays.binarySearch(array, 8, null)); // NPE check
} }
@Test @Test

View File

@ -0,0 +1,92 @@
/*
* Copyright 2023 ihromant.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.java.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Comparator;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
import org.teavm.junit.WholeClassCompilation;
@RunWith(TeaVMTestRunner.class)
@WholeClassCompilation
public class ComparatorTest {
@Test
public void naturalReverseOrder() {
Integer i = 2;
Integer j = 3;
Comparator<Integer> comp = Comparator.naturalOrder();
assertTrue(comp.compare(i, j) < 0);
assertEquals(0, comp.compare(i, i));
assertTrue(comp.compare(j, i) > 0);
Comparator<Integer> rev = Comparator.reverseOrder();
assertTrue(rev.compare(i, j) > 0);
assertEquals(0, rev.compare(i, i));
assertTrue(rev.compare(j, i) < 0);
try {
comp.compare(i, null);
fail("Expected NPE for comparing with null");
} catch (NullPointerException e) {
// OK
}
try {
comp.compare(null, i);
fail("Expected NPE for comparing with null");
} catch (NullPointerException e) {
// OK
}
}
@Test
public void testComparing() {
Point a = new Point(1, 1);
Point b = new Point(1, 2);
Point c = new Point(2, 1);
Point d = new Point(1, null);
Comparator<Point> comp = Comparator.comparing(Point::getX).thenComparing(Point::getY);
assertTrue(comp.compare(a, c) < 1);
assertTrue(comp.compare(a, b) < 1);
assertTrue(comp.compare(b, c) < 1);
assertEquals(0, comp.compare(a, a));
try {
comp.compare(a, d);
fail("Expected NPE for comparing null fields");
} catch (NullPointerException e) {
// OK
}
}
private static class Point {
private final Integer x;
private final Integer y;
private Point(Integer x, Integer y) {
this.x = x;
this.y = y;
}
private int getX() {
return x;
}
private int getY() {
return y;
}
}
}