From 76a4694c42d883bac35e8a860d3bfd928b000872 Mon Sep 17 00:00:00 2001 From: Martin van Wingerden Date: Fri, 22 Nov 2019 06:28:58 +0100 Subject: [PATCH] Some changes in the is-numeric (#8171) * Perform a null-check instead of catching NPE's * Use pre-compiled regular expressions and perform a null-check before testing it against the regex * Made the test usable with two modes, one using a single values and another one using a more realistic workload. * Upgraded the commons-lang version --- .../core-java-string-operations/pom.xml | 2 +- .../com/baeldung/isnumeric/Benchmarking.java | 60 ++++++++++++++++--- .../com/baeldung/isnumeric/IsNumeric.java | 19 +++++- .../baeldung/isnumeric/IsNumericDriver.java | 4 +- .../isnumeric/CoreJavaIsNumericUnitTest.java | 8 ++- .../isnumeric/RegularExpressionsUnitTest.java | 13 +++- 6 files changed, 87 insertions(+), 19 deletions(-) diff --git a/core-java-modules/core-java-string-operations/pom.xml b/core-java-modules/core-java-string-operations/pom.xml index fdddd99433..ebbbade7a4 100644 --- a/core-java-modules/core-java-string-operations/pom.xml +++ b/core-java-modules/core-java-string-operations/pom.xml @@ -58,7 +58,7 @@ - 3.8.1 + 3.9 3.6.1 1.10 diff --git a/core-java-modules/core-java-string-operations/src/main/java/com/baeldung/isnumeric/Benchmarking.java b/core-java-modules/core-java-string-operations/src/main/java/com/baeldung/isnumeric/Benchmarking.java index 97e7a46757..847b587cdc 100644 --- a/core-java-modules/core-java-string-operations/src/main/java/com/baeldung/isnumeric/Benchmarking.java +++ b/core-java-modules/core-java-string-operations/src/main/java/com/baeldung/isnumeric/Benchmarking.java @@ -1,6 +1,9 @@ package com.baeldung.isnumeric; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -14,6 +17,8 @@ import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; public class Benchmarking { + private static final TestMode MODE = TestMode.DIVERS; + public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder().include(Benchmarking.class.getSimpleName()) .forks(1) @@ -22,52 +27,89 @@ public class Benchmarking { new Runner(opt).run(); } + private static final IsNumeric subject = new IsNumeric(); + @State(Scope.Thread) public static class ExecutionPlan { - public String number = Integer.toString(Integer.MAX_VALUE); - public boolean isNumber = false; - public IsNumeric isNumeric = new IsNumeric(); + private final Map testPlan = testPlan(); + + void validate(Function functionUnderTest) { + testPlan.forEach((key, value) -> { + Boolean result = functionUnderTest.apply(key); + + assertEquals(value, result, key); + }); + } + + private void assertEquals(Boolean expectedResult, Boolean result, String input) { + if (result != expectedResult) { + throw new IllegalStateException("The output does not match the expected output, for input: " + input); + } + } + + private Map testPlan() { + HashMap plan = new HashMap<>(); + plan.put(Integer.toString(Integer.MAX_VALUE), true); + + if (MODE == TestMode.SIMPLE) { + return plan; + } + + plan.put("x0", false); + plan.put("0..005", false); + plan.put("--11", false); + plan.put("test", false); + plan.put(null, false); + for (int i = 0; i < 94; i++) { + plan.put(Integer.toString(i), true); + } + return plan; + } } @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public void usingCoreJava(ExecutionPlan plan) { - plan.isNumber = plan.isNumeric.usingCoreJava(plan.number); + plan.validate(subject::usingCoreJava); } @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public void usingRegularExpressions(ExecutionPlan plan) { - plan.isNumber = plan.isNumeric.usingRegularExpressions(plan.number); + plan.validate(subject::usingPreCompiledRegularExpressions); } @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public void usingNumberUtils_isCreatable(ExecutionPlan plan) { - plan.isNumber = plan.isNumeric.usingNumberUtils_isCreatable(plan.number); + plan.validate(subject::usingNumberUtils_isCreatable); } @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public void usingNumberUtils_isParsable(ExecutionPlan plan) { - plan.isNumber = plan.isNumeric.usingNumberUtils_isParsable(plan.number); + plan.validate(subject::usingNumberUtils_isParsable); } @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public void usingStringUtils_isNumeric(ExecutionPlan plan) { - plan.isNumber = plan.isNumeric.usingStringUtils_isNumeric(plan.number); + plan.validate(subject::usingStringUtils_isNumeric); } @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public void usingStringUtils_isNumericSpace(ExecutionPlan plan) { - plan.isNumber = plan.isNumeric.usingStringUtils_isNumericSpace(plan.number); + plan.validate(subject::usingStringUtils_isNumericSpace); + } + + private enum TestMode { + SIMPLE, DIVERS } } \ No newline at end of file diff --git a/core-java-modules/core-java-string-operations/src/main/java/com/baeldung/isnumeric/IsNumeric.java b/core-java-modules/core-java-string-operations/src/main/java/com/baeldung/isnumeric/IsNumeric.java index 6eed0d777d..4cfaf384c2 100644 --- a/core-java-modules/core-java-string-operations/src/main/java/com/baeldung/isnumeric/IsNumeric.java +++ b/core-java-modules/core-java-string-operations/src/main/java/com/baeldung/isnumeric/IsNumeric.java @@ -1,20 +1,33 @@ package com.baeldung.isnumeric; +import java.util.regex.Pattern; + import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; public class IsNumeric { + private final Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?"); + public boolean usingCoreJava(String strNum) { + if (strNum == null) { + return false; + } + try { Double.parseDouble(strNum); - } catch (NumberFormatException | NullPointerException nfe) { + } catch (NumberFormatException nfe) { return false; } return true; } - public boolean usingRegularExpressions(String strNum) { - return strNum.matches("-?\\d+(\\.\\d+)?"); + public boolean usingPreCompiledRegularExpressions(String strNum) { + if (strNum == null) { + return false; + } + + return pattern.matcher(strNum) + .matches(); } public boolean usingNumberUtils_isCreatable(String strNum) { diff --git a/core-java-modules/core-java-string-operations/src/main/java/com/baeldung/isnumeric/IsNumericDriver.java b/core-java-modules/core-java-string-operations/src/main/java/com/baeldung/isnumeric/IsNumericDriver.java index a6c5449696..f174497323 100644 --- a/core-java-modules/core-java-string-operations/src/main/java/com/baeldung/isnumeric/IsNumericDriver.java +++ b/core-java-modules/core-java-string-operations/src/main/java/com/baeldung/isnumeric/IsNumericDriver.java @@ -13,8 +13,8 @@ public class IsNumericDriver { boolean res = isNumeric.usingCoreJava("1001"); LOG.info("Using Core Java : " + res); - res = isNumeric.usingRegularExpressions("1001"); - LOG.info("Using Regular Expressions : " + res); + res = isNumeric.usingPreCompiledRegularExpressions("1001"); + LOG.info("Using Pre-compiled Regular Expressions : " + res); res = isNumeric.usingNumberUtils_isCreatable("1001"); LOG.info("Using NumberUtils.isCreatable : " + res); diff --git a/core-java-modules/core-java-string-operations/src/test/java/com/baeldung/isnumeric/CoreJavaIsNumericUnitTest.java b/core-java-modules/core-java-string-operations/src/test/java/com/baeldung/isnumeric/CoreJavaIsNumericUnitTest.java index 6c03b00e69..5159012aff 100644 --- a/core-java-modules/core-java-string-operations/src/test/java/com/baeldung/isnumeric/CoreJavaIsNumericUnitTest.java +++ b/core-java-modules/core-java-string-operations/src/test/java/com/baeldung/isnumeric/CoreJavaIsNumericUnitTest.java @@ -6,9 +6,13 @@ import org.junit.Test; public class CoreJavaIsNumericUnitTest { public static boolean isNumeric(String strNum) { + if (strNum == null) { + return false; + } + try { - double d = Double.parseDouble(strNum); - } catch (NumberFormatException | NullPointerException nfe) { + Double.parseDouble(strNum); + } catch (NumberFormatException nfe) { return false; } return true; diff --git a/core-java-modules/core-java-string-operations/src/test/java/com/baeldung/isnumeric/RegularExpressionsUnitTest.java b/core-java-modules/core-java-string-operations/src/test/java/com/baeldung/isnumeric/RegularExpressionsUnitTest.java index 04f3de8dc5..48c9825baa 100644 --- a/core-java-modules/core-java-string-operations/src/test/java/com/baeldung/isnumeric/RegularExpressionsUnitTest.java +++ b/core-java-modules/core-java-string-operations/src/test/java/com/baeldung/isnumeric/RegularExpressionsUnitTest.java @@ -2,11 +2,19 @@ package com.baeldung.isnumeric; import static org.assertj.core.api.Assertions.assertThat; +import java.util.regex.Pattern; + import org.junit.Test; public class RegularExpressionsUnitTest { - public static boolean isNumeric(String strNum) { - return strNum.matches("-?\\d+(\\.\\d+)?"); + private final Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?"); + + public boolean isNumeric(String strNum) { + if (strNum == null) { + return false; + } + return pattern.matcher(strNum) + .matches(); } @Test @@ -17,6 +25,7 @@ public class RegularExpressionsUnitTest { assertThat(isNumeric("-200")).isTrue(); // Invalid Numbers + assertThat(isNumeric(null)).isFalse(); assertThat(isNumeric("abc")).isFalse(); } }