diff --git a/core-java-modules/core-java-string-algorithms-5/pom.xml b/core-java-modules/core-java-string-algorithms-5/pom.xml index d410f9436c86..cefd82859464 100644 --- a/core-java-modules/core-java-string-algorithms-5/pom.xml +++ b/core-java-modules/core-java-string-algorithms-5/pom.xml @@ -13,6 +13,12 @@ + + net.jqwik + jqwik + ${jqwik.version} + test + com.vdurmont emoji-java @@ -22,6 +28,7 @@ 4.0.0 + 1.7.4 \ No newline at end of file diff --git a/core-java-modules/core-java-string-algorithms-5/src/main/java/com/baeldung/uniqueint/StringToUniqueInt.java b/core-java-modules/core-java-string-algorithms-5/src/main/java/com/baeldung/uniqueint/StringToUniqueInt.java new file mode 100644 index 000000000000..5128714be205 --- /dev/null +++ b/core-java-modules/core-java-string-algorithms-5/src/main/java/com/baeldung/uniqueint/StringToUniqueInt.java @@ -0,0 +1,50 @@ +package com.baeldung.uniqueint; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.CRC32; + +class StringToUniqueInt { + + private static final Map lookupMap = new HashMap<>(); + private static final AtomicInteger counter = new AtomicInteger(Integer.MIN_VALUE); + + public static int toIntByHashCode(String value) { + return value.hashCode(); + } + + public static int toIntByCR32(String value) { + CRC32 crc32 = new CRC32(); + crc32.update(value.getBytes()); + return (int) crc32.getValue(); + } + + public static int toIntByCharFormula(String value) { + return value.chars() + .reduce(17, (a, b) -> a * 13 + (b / a)); + } + + public static int toIntByMD5(String value) { + try { + MessageDigest digest = MessageDigest.getInstance("MD5"); + byte[] hash = digest.digest(value.getBytes()); + return ((hash[0] & 0xFF) << 24) | ((hash[1] & 0xFF) << 16) | ((hash[2] & 0xFF) << 8) | (hash[3] & 0xFF); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("MD5 not supported", e); + } + } + + public static int toIntByLookup(String value) { + var found = lookupMap.get(value); + if (found != null) { + return found; + } + + var intValue = counter.incrementAndGet(); + lookupMap.put(value, intValue); + return intValue; + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-string-algorithms-5/src/test/java/com/baeldung/uniqueint/StringToUniqueIntUnitTest.java b/core-java-modules/core-java-string-algorithms-5/src/test/java/com/baeldung/uniqueint/StringToUniqueIntUnitTest.java new file mode 100644 index 000000000000..31449a39b4b1 --- /dev/null +++ b/core-java-modules/core-java-string-algorithms-5/src/test/java/com/baeldung/uniqueint/StringToUniqueIntUnitTest.java @@ -0,0 +1,48 @@ +package com.baeldung.uniqueint; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Named; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import net.jqwik.api.Arbitraries; + +class StringToUniqueIntUnitTest { + + @ParameterizedTest + @MethodSource("implementations") + public void given1kElements_whenMappedToInt_thenItShouldHaveNoDuplicates(Function implementation) { + Stream strings = uniqueStringsOfSize(1_000); // increase to test higher guarantee + + List integers = strings.map(implementation) + .toList(); + + assertThat(integers).doesNotHaveDuplicates(); + } + + private static Stream implementations() { + return Stream.of(Arguments.of(Named.> of("toIntByHashCode", StringToUniqueInt::toIntByHashCode)), + Arguments.of(Named.> of("toIntByCR32", StringToUniqueInt::toIntByCR32)), + Arguments.of(Named.> of("toIntByCharFormula", StringToUniqueInt::toIntByCharFormula)), + Arguments.of(Named.> of("toIntByMD5", StringToUniqueInt::toIntByMD5)), + Arguments.of(Named.> of("toIntByLookup", StringToUniqueInt::toIntByLookup)) + ); + } + + private static Stream uniqueStringsOfSize(int size) { + return Arbitraries.strings() + .filter(it -> !it.isBlank()) + .stream() + .ofMinSize(size) + .ofMaxSize(size) + .uniqueElements() + .sample(); + } +} \ No newline at end of file