Optimize comparison and hash #20

This commit is contained in:
Fabio Lima 2022-08-21 07:24:10 -03:00
parent 58bc4398c0
commit 3f56210b53
3 changed files with 97 additions and 27 deletions

View File

@ -21,8 +21,8 @@ import com.github.f4b6a3.ulid.UlidCreator;
@Fork(1)
@Threads(1)
@State(Scope.Benchmark)
@Warmup(iterations = 3)
@Measurement(iterations = 5)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 3)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class Throughput {

View File

@ -622,45 +622,45 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (lsb ^ (lsb >>> 32));
result = prime * result + (int) (msb ^ (msb >>> 32));
return result;
final long bits = msb ^ lsb;
return (int) (bits ^ (bits >>> 32));
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
if (obj.getClass() != Ulid.class)
return false;
Ulid other = (Ulid) obj;
if (lsb != other.lsb)
Ulid that = (Ulid) obj;
if (lsb != that.lsb)
return false;
if (msb != other.msb)
if (msb != that.msb)
return false;
return true;
}
@Override
public int compareTo(Ulid other) {
public int compareTo(Ulid that) {
final long mask = 0xffffffffL;
// used to compare as UNSIGNED longs
final long min = 0x8000000000000000L;
final long[] a = { this.msb >>> 32, this.msb & mask, this.lsb >>> 32, this.lsb & mask };
final long[] b = { other.msb >>> 32, other.msb & mask, other.lsb >>> 32, other.lsb & mask };
final long a = this.msb + min;
final long b = that.msb + min;
// compare as fields unsigned integers
for (int i = 0; i < a.length; i++) {
if (a[i] > b[i]) {
if (a > b)
return 1;
} else if (a[i] < b[i]) {
else if (a < b)
return -1;
final long c = this.lsb + min;
final long d = that.lsb + min;
if (c > d)
return 1;
else if (c < d)
return -1;
}
}
return 0;
}

View File

@ -337,6 +337,28 @@ public class UlidTest {
assertEquals(new BigInteger(random2).add(increment), new BigInteger(ulid2.getRandom()));
}
@Test
public void testHashCode() {
Random random = new Random();
byte[] bytes = new byte[Ulid.ULID_BYTES];
// invoked on the same object
for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
random.nextBytes(bytes);
Ulid ulid1 = Ulid.from(bytes);
assertEquals(ulid1.hashCode(), ulid1.hashCode());
}
// invoked on two equal objects
for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
random.nextBytes(bytes);
Ulid ulid1 = Ulid.from(bytes);
Ulid ulid2 = Ulid.from(bytes);
assertEquals(ulid1.hashCode(), ulid2.hashCode());
}
}
@Test
public void testEquals() {
@ -366,15 +388,63 @@ public class UlidTest {
@Test
public void testCompareTo() {
final long zero = 0L;
Random random = new Random();
byte[] bytes = new byte[Ulid.ULID_BYTES];
for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
random.nextBytes(bytes);
bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(random.nextLong()).array();
Ulid ulid1 = Ulid.from(bytes);
BigInteger number1 = new BigInteger(1, bytes);
random.nextBytes(bytes);
bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(random.nextLong()).array();
Ulid ulid2 = Ulid.from(bytes);
Ulid ulid3 = Ulid.from(bytes);
BigInteger number2 = new BigInteger(1, bytes);
BigInteger number3 = new BigInteger(1, bytes);
// compare numerically
assertEquals(number1.compareTo(number2) > 0, ulid1.compareTo(ulid2) > 0);
assertEquals(number1.compareTo(number2) < 0, ulid1.compareTo(ulid2) < 0);
assertEquals(number2.compareTo(number3) == 0, ulid2.compareTo(ulid3) == 0);
// compare lexicographically
assertEquals(number1.compareTo(number2) > 0, ulid1.toString().compareTo(ulid2.toString()) > 0);
assertEquals(number1.compareTo(number2) < 0, ulid1.toString().compareTo(ulid2.toString()) < 0);
assertEquals(number2.compareTo(number3) == 0, ulid2.toString().compareTo(ulid3.toString()) == 0);
}
for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
bytes = ByteBuffer.allocate(16).putLong(zero).putLong(random.nextLong()).array();
Ulid ulid1 = Ulid.from(bytes);
BigInteger number1 = new BigInteger(1, bytes);
bytes = ByteBuffer.allocate(16).putLong(zero).putLong(random.nextLong()).array();
Ulid ulid2 = Ulid.from(bytes);
Ulid ulid3 = Ulid.from(bytes);
BigInteger number2 = new BigInteger(1, bytes);
BigInteger number3 = new BigInteger(1, bytes);
// compare numerically
assertEquals(number1.compareTo(number2) > 0, ulid1.compareTo(ulid2) > 0);
assertEquals(number1.compareTo(number2) < 0, ulid1.compareTo(ulid2) < 0);
assertEquals(number2.compareTo(number3) == 0, ulid2.compareTo(ulid3) == 0);
// compare lexicographically
assertEquals(number1.compareTo(number2) > 0, ulid1.toString().compareTo(ulid2.toString()) > 0);
assertEquals(number1.compareTo(number2) < 0, ulid1.toString().compareTo(ulid2.toString()) < 0);
assertEquals(number2.compareTo(number3) == 0, ulid2.toString().compareTo(ulid3.toString()) == 0);
}
for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(zero).array();
Ulid ulid1 = Ulid.from(bytes);
BigInteger number1 = new BigInteger(1, bytes);
bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(zero).array();
Ulid ulid2 = Ulid.from(bytes);
Ulid ulid3 = Ulid.from(bytes);
BigInteger number2 = new BigInteger(1, bytes);