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) @Fork(1)
@Threads(1) @Threads(1)
@State(Scope.Benchmark) @State(Scope.Benchmark)
@Warmup(iterations = 3) @Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5) @Measurement(iterations = 5, time = 3)
@BenchmarkMode(Mode.Throughput) @BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS) @OutputTimeUnit(TimeUnit.MILLISECONDS)
public class Throughput { public class Throughput {

View File

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

View File

@ -337,6 +337,28 @@ public class UlidTest {
assertEquals(new BigInteger(random2).add(increment), new BigInteger(ulid2.getRandom())); 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 @Test
public void testEquals() { public void testEquals() {
@ -366,15 +388,63 @@ public class UlidTest {
@Test @Test
public void testCompareTo() { public void testCompareTo() {
final long zero = 0L;
Random random = new Random(); Random random = new Random();
byte[] bytes = new byte[Ulid.ULID_BYTES]; byte[] bytes = new byte[Ulid.ULID_BYTES];
for (int i = 0; i < DEFAULT_LOOP_MAX; i++) { 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); Ulid ulid1 = Ulid.from(bytes);
BigInteger number1 = new BigInteger(1, 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 ulid2 = Ulid.from(bytes);
Ulid ulid3 = Ulid.from(bytes); Ulid ulid3 = Ulid.from(bytes);
BigInteger number2 = new BigInteger(1, bytes); BigInteger number2 = new BigInteger(1, bytes);