diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TBoolean.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TBoolean.java index 8a08dff17..fbb711f29 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TBoolean.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TBoolean.java @@ -21,7 +21,7 @@ public class TBoolean extends TObject implements TSerializable, TComparable TYPE = boolean.class; - private boolean value; + private final boolean value; public TBoolean(boolean value) { this.value = value; @@ -37,16 +37,7 @@ public class TBoolean extends TObject implements TSerializable, TComparable { public static final Class TYPE = byte.class; public static final int SIZE = 8; public static final int BYTES = 1; - private byte value; + private final byte value; public TByte(byte value) { this.value = value; diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TCharacter.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TCharacter.java index 3790111d6..6df537812 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TCharacter.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TCharacter.java @@ -91,7 +91,7 @@ public class TCharacter extends TObject implements TComparable { private static int[] upperCaseMapping; private static int[] lowerCaseMapping; private static UnicodeHelper.Range[] classMapping; - private char value; + private final char value; private static TCharacter[] characterCache = new TCharacter[128]; private static final int SURROGATE_NEUTRAL_BIT_MASK = 0xF800; private static final int SURROGATE_BITS = 0xD800; @@ -136,6 +136,10 @@ public class TCharacter extends TObject implements TComparable { @Override public int hashCode() { + return hashCode(value); + } + + public static int hashCode(char value) { return value; } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java index 4a2e63fba..b9e5d5130 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java @@ -35,7 +35,7 @@ public class TDouble extends TNumber implements TComparable { public static final int SIZE = 64; public static final int BYTES = SIZE / Byte.SIZE; public static final Class TYPE = double.class; - private double value; + private final double value; public TDouble(double value) { this.value = value; diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java index bf7d209fc..a09f8ff73 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java @@ -33,7 +33,7 @@ public class TFloat extends TNumber implements TComparable { public static final int SIZE = 32; public static final int BYTES = SIZE / Byte.SIZE; public static final Class TYPE = float.class; - private float value; + private final float value; public TFloat(float value) { this.value = value; diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TInteger.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TInteger.java index 8f8e07010..343b7df73 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TInteger.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TInteger.java @@ -27,7 +27,7 @@ public class TInteger extends TNumber implements TComparable { public static final int MAX_VALUE = 0x7FFFFFFF; public static final Class TYPE = int.class; private static TInteger[] integerCache; - private int value; + private final int value; public TInteger(int value) { this.value = value; @@ -45,7 +45,7 @@ public class TInteger extends TNumber implements TComparable { } public static int hashCode(int value) { - return (value >>> 4) ^ (value << 28) ^ (value << 8) ^ (value >>> 24); + return value; } public static String toHexString(int i) { diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java index 8e76391ba..eaaddc5d1 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java @@ -26,7 +26,7 @@ public class TLong extends TNumber implements TComparable { public static final Class TYPE = long.class; public static final int SIZE = 64; public static final int BYTES = SIZE / Byte.SIZE; - private long value; + private final long value; public TLong(long value) { this.value = value; diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TShort.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TShort.java index 6d50d2c7e..ce43efed7 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TShort.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TShort.java @@ -21,7 +21,7 @@ public class TShort extends TNumber implements TComparable { public static final Class TYPE = short.class; public static final int SIZE = 16; public static final int BYTES = SIZE / Byte.SIZE; - private short value; + private final short value; public TShort(short value) { this.value = value; diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TAbstractList.java b/classlib/src/main/java/org/teavm/classlib/java/util/TAbstractList.java index fefad1d18..9aeaa4f6b 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TAbstractList.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TAbstractList.java @@ -15,6 +15,7 @@ */ package org.teavm.classlib.java.util; +import java.util.Objects; import org.teavm.classlib.java.lang.*; import org.teavm.interop.Rename; @@ -161,7 +162,7 @@ public abstract class TAbstractList extends TAbstractCollection implements int hashCode = 1; for (TIterator iter = iterator(); iter.hasNext();) { E elem = iter.next(); - hashCode = 31 * hashCode + (elem != null ? elem.hashCode() : 0); + hashCode = 31 * hashCode + Objects.hashCode(elem); } return hashCode; } diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TArrayList.java b/classlib/src/main/java/org/teavm/classlib/java/util/TArrayList.java index d6efa22ea..1ce5970ed 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TArrayList.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TArrayList.java @@ -16,6 +16,7 @@ package org.teavm.classlib.java.util; import java.util.Arrays; +import java.util.Objects; import java.util.function.Consumer; import org.teavm.classlib.java.io.TSerializable; import org.teavm.classlib.java.lang.*; @@ -212,6 +213,15 @@ public class TArrayList extends TAbstractList implements TCloneable, TSeri return buffer.append(']').toString(); } + @Override + public int hashCode() { + int result = 1; + for (int i = 0; i < size; i++) { + result = 31 * result + Objects.hashCode(array[i]); + } + return result; + } + @Override public void sort(TComparator comp) { TArrays.sort(array, 0, size, comp); diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TArrays.java b/classlib/src/main/java/org/teavm/classlib/java/util/TArrays.java index d917ac766..e9315b424 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TArrays.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TArrays.java @@ -22,10 +22,7 @@ import java.util.function.IntFunction; import java.util.function.IntToDoubleFunction; import java.util.function.IntToLongFunction; import java.util.function.IntUnaryOperator; -import org.teavm.classlib.java.lang.TDouble; -import org.teavm.classlib.java.lang.TFloat; import org.teavm.classlib.java.lang.TIllegalArgumentException; -import org.teavm.classlib.java.lang.TInteger; import org.teavm.classlib.java.lang.TMath; import org.teavm.classlib.java.lang.TObject; import org.teavm.classlib.java.lang.TStringBuilder; @@ -1395,10 +1392,9 @@ public class TArrays extends TObject { if (a == null) { return 0; } - int hash = 0xA5A537FC; + int hash = 1; for (int i = 0; i < a.length; ++i) { - int h = a[i] ? 0x12345678 : 0x87654321; - hash = TInteger.rotateLeft(h, 4) ^ TInteger.rotateRight(h, 7) ^ TInteger.rotateLeft(hash, 13); + hash = 31 * hash + Boolean.hashCode(a[i]); } return hash; } @@ -1407,10 +1403,9 @@ public class TArrays extends TObject { if (a == null) { return 0; } - int hash = 0xA5A537FC; + int hash = 1; for (int i = 0; i < a.length; ++i) { - int h = (int) (a[i] >>> 32) ^ (int) a[i]; - hash = TInteger.rotateLeft(h, 4) ^ TInteger.rotateRight(h, 7) ^ TInteger.rotateLeft(hash, 13); + hash = 31 * hash + Long.hashCode(a[i]); } return hash; } @@ -1421,7 +1416,7 @@ public class TArrays extends TObject { } int hash = 1; for (int i = 0; i < a.length; ++i) { - hash = 31 * hash + a[i]; + hash = 31 * hash + Integer.hashCode(a[i]); } return hash; } @@ -1432,7 +1427,7 @@ public class TArrays extends TObject { } int hash = 1; for (int i = 0; i < a.length; ++i) { - hash = 31 * hash + a[i]; + hash = 31 * hash + Byte.hashCode(a[i]); } return hash; } @@ -1443,7 +1438,7 @@ public class TArrays extends TObject { } int hash = 1; for (int i = 0; i < a.length; ++i) { - hash = 31 * hash + a[i]; + hash = 31 * hash + Short.hashCode(a[i]); } return hash; } @@ -1454,7 +1449,7 @@ public class TArrays extends TObject { } int hash = 1; for (int i = 0; i < a.length; ++i) { - hash = 31 * hash + a[i]; + hash = 31 * hash + Character.hashCode(a[i]); } return hash; } @@ -1463,10 +1458,9 @@ public class TArrays extends TObject { if (a == null) { return 0; } - int hash = 0xA5A537FC; + int hash = 1; for (int i = 0; i < a.length; ++i) { - int h = TFloat.floatToIntBits(a[i]); - hash = TInteger.rotateLeft(h, 4) ^ TInteger.rotateRight(h, 7) ^ TInteger.rotateLeft(hash, 13); + hash = 31 * hash + Float.hashCode(a[i]); } return hash; } @@ -1475,11 +1469,9 @@ public class TArrays extends TObject { if (a == null) { return 0; } - int hash = 0xA5A537FC; + int hash = 1; for (int i = 0; i < a.length; ++i) { - long lh = TDouble.doubleToLongBits(a[i]); - int h = (int) lh ^ (int) (lh >> 32); - hash = TInteger.rotateLeft(h, 4) ^ TInteger.rotateRight(h, 7) ^ TInteger.rotateLeft(hash, 13); + hash = 31 * hash + Double.hashCode(a[i]); } return hash; } @@ -1488,10 +1480,9 @@ public class TArrays extends TObject { if (a == null) { return 0; } - int hash = 0xA5A537FC; + int hash = 1; for (int i = 0; i < a.length; ++i) { - int h = TObjects.hashCode(a[i]) ^ 0x1F7A58E0; - hash = TInteger.rotateLeft(h, 4) ^ TInteger.rotateRight(h, 7) ^ TInteger.rotateLeft(hash, 13); + hash = 31 * hash + Objects.hashCode(a[i]); } return hash; } @@ -1500,7 +1491,7 @@ public class TArrays extends TObject { if (a == null) { return 0; } - int hash = 0xA5A537FC; + int hash = 1; for (int i = 0; i < a.length; ++i) { Object el = a[i]; int h; @@ -1523,9 +1514,9 @@ public class TArrays extends TObject { } else if (a[i] instanceof Object[]) { h = deepHashCode((Object[]) el); } else { - h = TObjects.hashCode(el) ^ 0x1F7A58E0; + h = Objects.hashCode(el); } - hash = TInteger.rotateLeft(h, 4) ^ TInteger.rotateRight(h, 7) ^ TInteger.rotateLeft(hash, 13); + hash = 31 * hash + h; } return hash; } diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/THashMap.java b/classlib/src/main/java/org/teavm/classlib/java/util/THashMap.java index 5b4dbda68..d053ed64b 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/THashMap.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/THashMap.java @@ -33,6 +33,7 @@ package org.teavm.classlib.java.util; import java.util.Arrays; +import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -60,7 +61,7 @@ public class THashMap extends TAbstractMap implements TCloneable, TS HashEntry(K theKey, V theValue) { super(theKey, theValue); - origKeyHash = theKey == null ? 0 : computeHashCode(theKey); + origKeyHash = Objects.hashCode(theKey); } @Override @@ -395,7 +396,7 @@ public class THashMap extends TAbstractMap implements TCloneable, TS if (key == null) { m = findNullKeyEntry(); } else { - int hash = computeHashCode(key); + int hash = key.hashCode(); int index = hash & (elementData.length - 1); m = findNonNullKeyEntry(key, index, hash); } @@ -480,7 +481,7 @@ public class THashMap extends TAbstractMap implements TCloneable, TS } } } else { - int hash = computeHashCode(key); + int hash = key.hashCode(); int index = hash & (elementData.length - 1); entry = findNonNullKeyEntry(key, index, hash); if (entry == null) { @@ -576,7 +577,7 @@ public class THashMap extends TAbstractMap implements TCloneable, TS HashEntry entry; HashEntry last = null; if (key != null) { - int hash = computeHashCode(key); + int hash = key.hashCode(); index = hash & (elementData.length - 1); entry = elementData[index]; while (entry != null && !(entry.origKeyHash == hash && areEqualKeys(key, entry.key))) { @@ -678,10 +679,6 @@ public class THashMap extends TAbstractMap implements TCloneable, TS } } - static int computeHashCode(Object key) { - return key.hashCode(); - } - static boolean areEqualKeys(Object key1, Object key2) { return (key1 == key2) || key1.equals(key2); } diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TVector.java b/classlib/src/main/java/org/teavm/classlib/java/util/TVector.java index acf3dfbbf..d62dde9e7 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TVector.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TVector.java @@ -17,6 +17,7 @@ package org.teavm.classlib.java.util; import java.lang.reflect.Array; import java.util.Arrays; +import java.util.Objects; import org.teavm.classlib.java.io.TSerializable; import org.teavm.classlib.java.lang.TCloneNotSupportedException; import org.teavm.classlib.java.lang.TCloneable; @@ -194,7 +195,7 @@ public class TVector extends TAbstractList implements TList, TRandomAcc while (it.hasNext()) { Object e1 = elementData[index++]; Object e2 = it.next(); - if (!(e1 == null ? e2 == null : e1.equals(e2))) { + if (!Objects.equals(e1, e2)) { return false; } } @@ -268,7 +269,7 @@ public class TVector extends TAbstractList implements TList, TRandomAcc public synchronized int hashCode() { int result = 1; for (int i = 0; i < elementCount; i++) { - result = (31 * result) + (elementData[i] == null ? 0 : elementData[i].hashCode()); + result = 31 * result + Objects.hashCode(elementData[i]); } return result; } diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/concurrent/TConcurrentHashMap.java b/classlib/src/main/java/org/teavm/classlib/java/util/concurrent/TConcurrentHashMap.java index 8a54d18c0..fb72d3973 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/concurrent/TConcurrentHashMap.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/concurrent/TConcurrentHashMap.java @@ -421,7 +421,7 @@ public class TConcurrentHashMap extends TAbstractMap @Override public V putIfAbsent(K key, V value) { - var hash = computeHashCode(key); + var hash = Objects.hashCode(key); var entry = getEntry(key, hash); if (entry != null) { @@ -453,7 +453,7 @@ public class TConcurrentHashMap extends TAbstractMap public V computeIfAbsent(K key, Function mappingFunction) { Objects.requireNonNull(mappingFunction); - var hash = computeHashCode(key); + var hash = Objects.hashCode(key); var entry = getEntry(key, hash); if (entry != null) { return entry.getValue(); @@ -472,7 +472,7 @@ public class TConcurrentHashMap extends TAbstractMap @Override public V computeIfPresent(K key, BiFunction remappingFunction) { - int hash = computeHashCode(key); + int hash = Objects.hashCode(key); V newValue = null; var newValueComputed = false; @@ -499,7 +499,7 @@ public class TConcurrentHashMap extends TAbstractMap @Override public V compute(K key, BiFunction remappingFunction) { - var hash = computeHashCode(key); + var hash = Objects.hashCode(key); while (true) { var entry = getEntry(key, hash); @@ -530,7 +530,7 @@ public class TConcurrentHashMap extends TAbstractMap } private HashEntry getEntry(Object key) { - return getEntry(key, computeHashCode(key)); + return getEntry(key, Objects.hashCode(key)); } private HashEntry getEntry(Object key, int hash) { @@ -575,7 +575,7 @@ public class TConcurrentHashMap extends TAbstractMap } private HashEntry getEntryByKeyAndValue(Object key, Object value) { - var hash = computeHashCode(key); + var hash = Objects.hashCode(key); repeatTable: do { var table = elementData; @@ -667,7 +667,7 @@ public class TConcurrentHashMap extends TAbstractMap } private V putImpl(K key, V value) { - var hash = computeHashCode(key); + var hash = Objects.hashCode(key); var entry = getEntry(key, hash); var index = computeIndex(hash); @@ -789,10 +789,6 @@ public class TConcurrentHashMap extends TAbstractMap return cachedValues; } - private static int computeHashCode(Object key) { - return key == null ? 0 : key.hashCode(); - } - private static boolean areEqualKeys(Object key1, Object key2) { return (key1 == key2) || key1.equals(key2); } diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/BooleanTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/BooleanTest.java index f71e96772..65cf340df 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/BooleanTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/BooleanTest.java @@ -26,9 +26,9 @@ import org.teavm.junit.TeaVMTestRunner; public class BooleanTest { @Test public void parsesBoolean() { - assertEquals(true, new Boolean("TruE")); - assertEquals(false, new Boolean("False")); - assertEquals(false, new Boolean("True15")); + assertEquals(true, Boolean.parseBoolean("TruE")); + assertEquals(false, Boolean.parseBoolean("False")); + assertEquals(false, Boolean.parseBoolean("True15")); } @Test @@ -65,4 +65,12 @@ public class BooleanTest { assertEquals(Boolean.hashCode(true), Boolean.TRUE.hashCode()); assertEquals(Boolean.hashCode(false), Boolean.FALSE.hashCode()); } + + @Test + public void compares() { + assertEquals(0, Boolean.compare(false, false)); + assertEquals(-1, Boolean.compare(false, true)); + assertEquals(1, Boolean.compare(true, false)); + assertEquals(0, Boolean.compare(true, true)); + } } diff --git a/tests/src/test/java/org/teavm/classlib/java/util/ArrayListTest.java b/tests/src/test/java/org/teavm/classlib/java/util/ArrayListTest.java index e17cb65ee..76dea2d9c 100644 --- a/tests/src/test/java/org/teavm/classlib/java/util/ArrayListTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/util/ArrayListTest.java @@ -16,10 +16,13 @@ package org.teavm.classlib.java.util; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.fail; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.ConcurrentModificationException; +import java.util.LinkedList; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -198,4 +201,11 @@ public class ArrayListTest { assertEquals(i, list.get(i).intValue()); } } + + @Test + public void hashCodeEquals() { + assertEquals(956197, new ArrayList<>(Arrays.asList(1, 3, null, 2)).hashCode()); + assertEquals(new LinkedList<>(Arrays.asList(1, 3, null, 2)), new ArrayList<>(Arrays.asList(1, 3, null, 2))); + assertNotEquals(new ArrayList<>(Arrays.asList(1, 3, 2)), new ArrayList<>(Arrays.asList(1, 3, null, 2))); + } } diff --git a/tests/src/test/java/org/teavm/classlib/java/util/ArraysTest.java b/tests/src/test/java/org/teavm/classlib/java/util/ArraysTest.java index dc1fc2348..ec125f757 100644 --- a/tests/src/test/java/org/teavm/classlib/java/util/ArraysTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/util/ArraysTest.java @@ -16,11 +16,14 @@ package org.teavm.classlib.java.util; import static org.junit.Assert.assertEquals; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import org.junit.Test; import org.junit.runner.RunWith; +import org.teavm.classlib.java.lang.DoubleTest; import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.WholeClassCompilation; @@ -124,4 +127,33 @@ public class ArraysTest { result = Arrays.stream(array, 0, 2).mapToObj(Double::toString).collect(Collectors.joining(",")); assertEquals("23.0,42.0", result); } + + @Test + public void testHashcode() { + assertEquals(List.of(1, 2, 3).hashCode(), Arrays.hashCode(new int[] { 1, 2, 3 })); + assertEquals(List.of(1L, 2L, 3L).hashCode(), Arrays.hashCode(new long[] { 1L, 2L, 3L })); + assertEquals(List.of(1.0f, 2.0f, 3.0f).hashCode(), Arrays.hashCode(new float[] { 1.0f, 2.0f, 3.0f })); + assertEquals(List.of(1.0, 2.0, 3.0).hashCode(), Arrays.hashCode(new double[] { 1.0, 2.0, 3.0 })); + assertEquals(List.of(false, true, false).hashCode(), Arrays.hashCode(new boolean[] { false, true, false })); + assertEquals(List.of((byte) 1, (byte) 2).hashCode(), Arrays.hashCode(new byte[] { (byte) 1, (byte) 2 })); + assertEquals(List.of((short) 1, (short) 2).hashCode(), Arrays.hashCode(new short[] { (short) 1, (short) 2 })); + assertEquals(List.of('a', 'b', 'c').hashCode(), Arrays.hashCode(new char[] { 'a', 'b', 'c' })); + assertEquals(List.of(List.of('a', 'b'), List.of('c')).hashCode(), + Arrays.deepHashCode(new char[][] { { 'a', 'b' }, { 'c' } })); + List testList = new ArrayList<>(); + List> innerList = new ArrayList<>(); + innerList.add(Optional.empty()); + innerList.add(null); + innerList.add(Optional.of(5)); + testList.add(innerList); + testList.add(null); + testList.add(List.of(Optional.of(3), Optional.empty())); + testList.add(6); + assertEquals(testList.hashCode(), Arrays.deepHashCode(new Object[] { + new Object[] { Optional.empty(), null, Optional.of(5) }, + null, + new Object[] { Optional.of(3), Optional.empty() }, + 6 + })); + } }