diff --git a/src/main/java/info/debatty/java/stringsimilarity/Damerau.java b/src/main/java/info/debatty/java/stringsimilarity/Damerau.java index b6fe914..419c86f 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/Damerau.java +++ b/src/main/java/info/debatty/java/stringsimilarity/Damerau.java @@ -57,13 +57,7 @@ public class Damerau implements MetricStringDistance { */ public final double distance(final String s1, final String s2) { - if (s1 == null) { - throw new NullPointerException("s1 must not be null"); - } - - if (s2 == null) { - throw new NullPointerException("s2 must not be null"); - } + StringUtils.requireNonNull(s1, s2); if (s1.equals(s2)) { return 0; diff --git a/src/main/java/info/debatty/java/stringsimilarity/Jaccard.java b/src/main/java/info/debatty/java/stringsimilarity/Jaccard.java index 84417ec..7c6a678 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/Jaccard.java +++ b/src/main/java/info/debatty/java/stringsimilarity/Jaccard.java @@ -76,13 +76,7 @@ public Jaccard() { * @throws NullPointerException if s1 or s2 is null. */ public final double similarity(final String s1, final String s2) { - if (s1 == null) { - throw new NullPointerException("s1 must not be null"); - } - - if (s2 == null) { - throw new NullPointerException("s2 must not be null"); - } + StringUtils.requireNonNull(s1, s2); if (s1.equals(s2)) { return 1; diff --git a/src/main/java/info/debatty/java/stringsimilarity/JaroWinkler.java b/src/main/java/info/debatty/java/stringsimilarity/JaroWinkler.java index 267cbb4..726b276 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/JaroWinkler.java +++ b/src/main/java/info/debatty/java/stringsimilarity/JaroWinkler.java @@ -22,8 +22,13 @@ public class JaroWinkler implements NormalizedStringSimilarity, NormalizedStringDistance { private static final double DEFAULT_THRESHOLD = 0.7; - private static final int THREE = 3; - private static final double JW_COEF = 0.1; + private static final int IDX_MATCHES = 0; + private static final int IDX_TRANSPOSITIONS = 1; + private static final int IDX_PREFIX = 2; + private static final int IDX_MAX_LEN = 3; + private static final double JW_COEF = 0.1; + private static final int JARO_DIVISOR = 3; + private final double threshold; /** @@ -62,29 +67,27 @@ public final double getThreshold() { * @throws NullPointerException if s1 or s2 is null. */ public final double similarity(final String s1, final String s2) { - if (s1 == null) { - throw new NullPointerException("s1 must not be null"); - } - - if (s2 == null) { - throw new NullPointerException("s2 must not be null"); - } + StringUtils.requireNonNull(s1, s2); if (s1.equals(s2)) { return 1; } int[] mtp = matches(s1, s2); - float m = mtp[0]; + float m = mtp[IDX_MATCHES]; if (m == 0) { return 0f; } - double j = ((m / s1.length() + m / s2.length() + (m - mtp[1]) / m)) - / THREE; + double j = ( m / s1.length() + + m / s2.length() + + (m - mtp[IDX_TRANSPOSITIONS]) / m + ) / JARO_DIVISOR; + double jw = j; if (j > getThreshold()) { - jw = j + Math.min(JW_COEF, 1.0 / mtp[THREE]) * mtp[2] * (1 - j); + jw = j + Math.min(JW_COEF, 1.0 / mtp[IDX_MAX_LEN]) + * mtp[IDX_PREFIX] * (1 - j); } return jw; } diff --git a/src/main/java/info/debatty/java/stringsimilarity/Levenshtein.java b/src/main/java/info/debatty/java/stringsimilarity/Levenshtein.java index d2ba4fb..25eb791 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/Levenshtein.java +++ b/src/main/java/info/debatty/java/stringsimilarity/Levenshtein.java @@ -52,13 +52,7 @@ public final double distance(final String s1, final String s2) { */ public final double distance(final String s1, final String s2, final int limit) { - if (s1 == null) { - throw new NullPointerException("s1 must not be null"); - } - - if (s2 == null) { - throw new NullPointerException("s2 must not be null"); - } + StringUtils.requireNonNull(s1, s2); if (s1.equals(s2)) { return 0; @@ -111,8 +105,7 @@ public final double distance(final String s1, final String s2, return limit; } - // copy v1 (current row) to v0 (previous row) for next iteration - //System.arraycopy(v1, 0, v0, 0, v0.length); + // Flip references to current and previous row vtemp = v0; diff --git a/src/main/java/info/debatty/java/stringsimilarity/LongestCommonSubsequence.java b/src/main/java/info/debatty/java/stringsimilarity/LongestCommonSubsequence.java index e8d091a..36452a4 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/LongestCommonSubsequence.java +++ b/src/main/java/info/debatty/java/stringsimilarity/LongestCommonSubsequence.java @@ -37,13 +37,7 @@ public class LongestCommonSubsequence implements StringDistance { * @throws NullPointerException if s1 or s2 is null. */ public final double distance(final String s1, final String s2) { - if (s1 == null) { - throw new NullPointerException("s1 must not be null"); - } - - if (s2 == null) { - throw new NullPointerException("s2 must not be null"); - } + StringUtils.requireNonNull(s1, s2); if (s1.equals(s2)) { return 0; diff --git a/src/main/java/info/debatty/java/stringsimilarity/MetricLCS.java b/src/main/java/info/debatty/java/stringsimilarity/MetricLCS.java index ab4cc61..f503bb4 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/MetricLCS.java +++ b/src/main/java/info/debatty/java/stringsimilarity/MetricLCS.java @@ -50,13 +50,7 @@ public class MetricLCS * @throws NullPointerException if s1 or s2 is null. */ public final double distance(final String s1, final String s2) { - if (s1 == null) { - throw new NullPointerException("s1 must not be null"); - } - - if (s2 == null) { - throw new NullPointerException("s2 must not be null"); - } + StringUtils.requireNonNull(s1, s2); if (s1.equals(s2)) { return 0; diff --git a/src/main/java/info/debatty/java/stringsimilarity/NGram.java b/src/main/java/info/debatty/java/stringsimilarity/NGram.java index c2a5f2d..8562bf6 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/NGram.java +++ b/src/main/java/info/debatty/java/stringsimilarity/NGram.java @@ -43,13 +43,7 @@ public NGram() { * @throws NullPointerException if s0 or s1 is null. */ public final double distance(final String s0, final String s1) { - if (s0 == null) { - throw new NullPointerException("s0 must not be null"); - } - - if (s1 == null) { - throw new NullPointerException("s1 must not be null"); - } + StringUtils.requireNonNull(s0, s1); if (s0.equals(s1)) { return 0; diff --git a/src/main/java/info/debatty/java/stringsimilarity/NormalizedLevenshtein.java b/src/main/java/info/debatty/java/stringsimilarity/NormalizedLevenshtein.java index 49eaa21..a020fd2 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/NormalizedLevenshtein.java +++ b/src/main/java/info/debatty/java/stringsimilarity/NormalizedLevenshtein.java @@ -50,13 +50,7 @@ public class NormalizedLevenshtein implements */ public final double distance(final String s1, final String s2) { - if (s1 == null) { - throw new NullPointerException("s1 must not be null"); - } - - if (s2 == null) { - throw new NullPointerException("s2 must not be null"); - } + StringUtils.requireNonNull(s1, s2); if (s1.equals(s2)) { return 0; diff --git a/src/main/java/info/debatty/java/stringsimilarity/OptimalStringAlignment.java b/src/main/java/info/debatty/java/stringsimilarity/OptimalStringAlignment.java index f7fbba5..32d2c0a 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/OptimalStringAlignment.java +++ b/src/main/java/info/debatty/java/stringsimilarity/OptimalStringAlignment.java @@ -52,13 +52,7 @@ public final class OptimalStringAlignment implements StringDistance { * @throws NullPointerException if s1 or s2 is null. */ public double distance(final String s1, final String s2) { - if (s1 == null) { - throw new NullPointerException("s1 must not be null"); - } - - if (s2 == null) { - throw new NullPointerException("s2 must not be null"); - } + StringUtils.requireNonNull(s1, s2); if (s1.equals(s2)) { return 0; diff --git a/src/main/java/info/debatty/java/stringsimilarity/QGram.java b/src/main/java/info/debatty/java/stringsimilarity/QGram.java index ae6f9fc..41c9d13 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/QGram.java +++ b/src/main/java/info/debatty/java/stringsimilarity/QGram.java @@ -60,13 +60,7 @@ public QGram() { * @throws NullPointerException if s1 or s2 is null. */ public final double distance(final String s1, final String s2) { - if (s1 == null) { - throw new NullPointerException("s1 must not be null"); - } - - if (s2 == null) { - throw new NullPointerException("s2 must not be null"); - } + StringUtils.requireNonNull(s1, s2); if (s1.equals(s2)) { return 0; diff --git a/src/main/java/info/debatty/java/stringsimilarity/RatcliffObershelp.java b/src/main/java/info/debatty/java/stringsimilarity/RatcliffObershelp.java index 22e4ea3..c015676 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/RatcliffObershelp.java +++ b/src/main/java/info/debatty/java/stringsimilarity/RatcliffObershelp.java @@ -57,13 +57,7 @@ public class RatcliffObershelp implements */ @Override public final double similarity(final String s1, final String s2) { - if (s1 == null) { - throw new NullPointerException("s1 must not be null"); - } - - if (s2 == null) { - throw new NullPointerException("s2 must not be null"); - } + StringUtils.requireNonNull(s1, s2); if (s1.equals(s2)) { return 1.0d; diff --git a/src/main/java/info/debatty/java/stringsimilarity/ShingleBased.java b/src/main/java/info/debatty/java/stringsimilarity/ShingleBased.java index 24017e2..2d103bc 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/ShingleBased.java +++ b/src/main/java/info/debatty/java/stringsimilarity/ShingleBased.java @@ -77,7 +77,7 @@ public ShingleBased(final int k) { /** * */ - ShingleBased() { + protected ShingleBased() { this(DEFAULT_K); } diff --git a/src/main/java/info/debatty/java/stringsimilarity/SorensenDice.java b/src/main/java/info/debatty/java/stringsimilarity/SorensenDice.java index dec376b..c7710f9 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/SorensenDice.java +++ b/src/main/java/info/debatty/java/stringsimilarity/SorensenDice.java @@ -78,13 +78,7 @@ public SorensenDice() { * @throws NullPointerException if s1 or s2 is null. */ public final double similarity(final String s1, final String s2) { - if (s1 == null) { - throw new NullPointerException("s1 must not be null"); - } - - if (s2 == null) { - throw new NullPointerException("s2 must not be null"); - } + StringUtils.requireNonNull(s1, s2); if (s1.equals(s2)) { return 1; diff --git a/src/main/java/info/debatty/java/stringsimilarity/StringUtils.java b/src/main/java/info/debatty/java/stringsimilarity/StringUtils.java new file mode 100644 index 0000000..bee31e8 --- /dev/null +++ b/src/main/java/info/debatty/java/stringsimilarity/StringUtils.java @@ -0,0 +1,17 @@ +package info.debatty.java.stringsimilarity; + +/** + * Shared validation helpers for all similarity/distance methods. + */ + + +public class StringUtils { + private StringUtils() {} // utility class — no instantiation + + + static void requireNonNull(String s1, String s2) { + if (s1 == null) throw new NullPointerException("s1 must not be null"); + if (s2 == null) throw new NullPointerException("s2 must not be null"); + } + +} diff --git a/src/main/java/info/debatty/java/stringsimilarity/WeightedLevenshtein.java b/src/main/java/info/debatty/java/stringsimilarity/WeightedLevenshtein.java index 69413e2..6e018b1 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/WeightedLevenshtein.java +++ b/src/main/java/info/debatty/java/stringsimilarity/WeightedLevenshtein.java @@ -80,13 +80,7 @@ public final double distance(final String s1, final String s2) { */ public final double distance(final String s1, final String s2, final double limit) { - if (s1 == null) { - throw new NullPointerException("s1 must not be null"); - } - - if (s2 == null) { - throw new NullPointerException("s2 must not be null"); - } + StringUtils.requireNonNull(s1, s2); if (s1.equals(s2)) { return 0; diff --git a/src/main/java/info/debatty/java/stringsimilarity/experimental/Sift4.java b/src/main/java/info/debatty/java/stringsimilarity/experimental/Sift4.java index 29a8946..d569c69 100644 --- a/src/main/java/info/debatty/java/stringsimilarity/experimental/Sift4.java +++ b/src/main/java/info/debatty/java/stringsimilarity/experimental/Sift4.java @@ -24,6 +24,8 @@ package info.debatty.java.stringsimilarity.experimental; import info.debatty.java.stringsimilarity.interfaces.StringDistance; + +import java.util.Iterator; import java.util.LinkedList; /** @@ -62,34 +64,52 @@ public final void setMaxOffset(final int max_offset) { * @param s2 * @return */ + + // AFTER — Offset moved to private static nested class + private static final class Offset { + final int c1; + final int c2; + boolean trans; // mutable — documented intentionally + Offset(int c1, int c2, boolean trans) { + this.c1 = c1; this.c2 = c2; this.trans = trans; + } + } + + /** Validates that neither string is null, consistent with all other algorithms. */ + private static void validateInputs(String s1, String s2) { + if (s1==null) throw new NullPointerException("s1 must not be null/NULL"); + if (s2==null) throw new NullPointerException("s2 must not be null/NULL"); // added null handling + } + + /** Resolves whether a new match at (c1, c2) is a transposition. */ + private boolean resolveTransposition( + int c1, int c2, LinkedList offsetArr, int[] trans) { + boolean isTrans = false; + Iterator it = offsetArr.iterator(); + while (it.hasNext()) { + Offset ofs = it.next(); + if (c1 <= ofs.c1 || c2 <= ofs.c2) { + isTrans = Math.abs(c2 - c1) >= Math.abs(ofs.c2 - ofs.c1); + if (isTrans) { trans[0]++; } + else if (!ofs.trans) { ofs.trans = true; trans[0]++; } + break; + } else if (c1 > ofs.c2 && c2 > ofs.c1) { it.remove(); } + } + return isTrans; + } + public final double distance(final String s1, final String s2) { /** * Used to store relation between same character in different positions * c1 and c2 in the input strings. */ - class Offset { - - private final int c1; - private final int c2; - private boolean trans; - - Offset(final int c1, final int c2, final boolean trans) { - this.c1 = c1; - this.c2 = c2; - this.trans = trans; - } - } - - if (s1 == null || s1.isEmpty()) { - if (s2 == null) { - return 0; - } + if (s1.isEmpty()) { return s2.length(); } - if (s2 == null || s2.isEmpty()) { + if (s2.isEmpty()) { return s1.length(); }