From 741afcbfda199fa9344c6f83fc19aa276a5b6a02 Mon Sep 17 00:00:00 2001 From: Felseje Date: Fri, 10 Apr 2026 12:09:47 -0300 Subject: [PATCH 1/2] Improve Alphabetical implementation, tests and documentation --- .../thealgorithms/strings/Alphabetical.java | 52 ++++++++++++++----- .../strings/AlphabeticalTest.java | 40 +++++++++++--- 2 files changed, 73 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/thealgorithms/strings/Alphabetical.java b/src/main/java/com/thealgorithms/strings/Alphabetical.java index ef2974eb427d..37b1fb068b44 100644 --- a/src/main/java/com/thealgorithms/strings/Alphabetical.java +++ b/src/main/java/com/thealgorithms/strings/Alphabetical.java @@ -1,32 +1,58 @@ package com.thealgorithms.strings; +import java.util.Locale; + /** - * Utility class for checking if a string's characters are in alphabetical order. + * Utility class for checking whether a string's characters are in non-decreasing + * lexicographical order based on Unicode code points (case-insensitive). + *

+ * This does NOT implement language-aware alphabetical ordering (collation rules). + * It simply compares lowercase Unicode character values. *

- * Alphabetical order is a system whereby character strings are placed in order - * based on the position of the characters in the conventional ordering of an - * alphabet. + * Non-letter characters are not allowed and will cause the check to fail. *

- * Reference: Wikipedia: Alphabetical Order + * Reference: + * Wikipedia: Alphabetical order */ public final class Alphabetical { + private Alphabetical() { } /** - * Checks whether the characters in the given string are in alphabetical order. - * Non-letter characters will cause the check to fail. + * Checks whether the characters in the given string are in non-decreasing + * lexicographical order (case-insensitive). + *

+ * Rules: + *

* - * @param s the input string - * @return {@code true} if all characters are in alphabetical order (case-insensitive), otherwise {@code false} + * @param s input string + * @return {@code true} if characters are in non-decreasing order, otherwise {@code false} */ public static boolean isAlphabetical(String s) { - s = s.toLowerCase(); - for (int i = 0; i < s.length() - 1; ++i) { - if (!Character.isLetter(s.charAt(i)) || s.charAt(i) > s.charAt(i + 1)) { + if (s == null || s.isBlank()) { + return false; + } + + String normalized = s.toLowerCase(Locale.ROOT); + + if (!Character.isLetter(normalized.charAt(0))) { + return false; + } + + for (int i = 1; i < normalized.length(); i++) { + char prev = normalized.charAt(i - 1); + char curr = normalized.charAt(i); + + if (!Character.isLetter(curr) || prev > curr) { return false; } } - return !s.isEmpty() && Character.isLetter(s.charAt(s.length() - 1)); + return true; } } diff --git a/src/test/java/com/thealgorithms/strings/AlphabeticalTest.java b/src/test/java/com/thealgorithms/strings/AlphabeticalTest.java index 7b41e11ef22f..726b92dce636 100644 --- a/src/test/java/com/thealgorithms/strings/AlphabeticalTest.java +++ b/src/test/java/com/thealgorithms/strings/AlphabeticalTest.java @@ -1,15 +1,43 @@ package com.thealgorithms.strings; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.params.provider.Arguments.arguments; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -public class AlphabeticalTest { +@DisplayName("Alphabetical.isAlphabetical()") +class AlphabeticalTest { - @ParameterizedTest(name = "\"{0}\" → Expected: {1}") - @CsvSource({"'abcdefghijklmno', true", "'abcdxxxyzzzz', true", "'123a', false", "'abcABC', false", "'abcdefghikjlmno', false", "'aBC', true", "'abc', true", "'xyzabc', false", "'abcxyz', true", "'', false", "'1', false"}) - void testIsAlphabetical(String input, boolean expected) { - assertEquals(expected, Alphabetical.isAlphabetical(input)); + static Stream testCases() { + return Stream.of(arguments("", false, "Should return false for empty string"), arguments(" ", false, "Should return false for blank string"), arguments("a1b2", false, "Should return false when string contains numbers"), + arguments("abc!DEF", false, "Should return false when string contains symbols"), arguments("#abc", false, "Should return false when first character is not a letter"), arguments("abc", true, "Should return true for non-decreasing order"), + arguments("aBcD", true, "Should return true for mixed case increasing sequence"), arguments("a", true, "Should return true for single letter"), arguments("'", false, "Should return false for single symbol"), arguments("aabbcc", true, "Should return true for repeated letters"), + arguments("cba", false, "Should return false when order decreases"), arguments("abzba", false, "Should return false when middle breaks order")); + } + + private void assertAlphabetical(String input, boolean expected, String message) { + // Arrange & Act + boolean result = Alphabetical.isAlphabetical(input); + + // Assertion + assertEquals(expected, result, message); + } + + @Test + @DisplayName("Should return false for null input") + void nullInputTest() { + assertAlphabetical(null, false, "Should return false for null input"); + } + + @ParameterizedTest(name = "{2}") + @MethodSource("testCases") + @DisplayName("Alphabetical cases") + void isAlphabeticalTest(String input, boolean expected, String message) { + assertAlphabetical(input, expected, message); } } From 3e1e9805bd30b37af5f5dd723accf12db6fafa64 Mon Sep 17 00:00:00 2001 From: Felseje Date: Fri, 10 Apr 2026 15:15:13 -0300 Subject: [PATCH 2/2] Workaround SpotBugs false positive in parameterized tests Replaced boolean literals with Boolean.TRUE/FALSE in Arguments.of(...) to avoid SpotBugs warning (NAB_NEEDLESS_BOOLEAN_CONSTANT_CONVERSION). This is a false positive caused by JUnit's Object... varargs requiring auto-boxing. --- .../com/thealgorithms/strings/AlphabeticalTest.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/thealgorithms/strings/AlphabeticalTest.java b/src/test/java/com/thealgorithms/strings/AlphabeticalTest.java index 726b92dce636..0c7d7e4701cf 100644 --- a/src/test/java/com/thealgorithms/strings/AlphabeticalTest.java +++ b/src/test/java/com/thealgorithms/strings/AlphabeticalTest.java @@ -14,17 +14,19 @@ class AlphabeticalTest { static Stream testCases() { - return Stream.of(arguments("", false, "Should return false for empty string"), arguments(" ", false, "Should return false for blank string"), arguments("a1b2", false, "Should return false when string contains numbers"), - arguments("abc!DEF", false, "Should return false when string contains symbols"), arguments("#abc", false, "Should return false when first character is not a letter"), arguments("abc", true, "Should return true for non-decreasing order"), - arguments("aBcD", true, "Should return true for mixed case increasing sequence"), arguments("a", true, "Should return true for single letter"), arguments("'", false, "Should return false for single symbol"), arguments("aabbcc", true, "Should return true for repeated letters"), - arguments("cba", false, "Should return false when order decreases"), arguments("abzba", false, "Should return false when middle breaks order")); + // Workaround for SpotBugs false positive (NAB_NEEDLESS_BOOLEAN_CONSTANT_CONVERSION) + // due to JUnit Arguments.of(Object...) auto-boxing + return Stream.of(arguments("", Boolean.FALSE, "Should return false for empty string"), arguments(" ", Boolean.FALSE, "Should return false for blank string"), arguments("a1b2", Boolean.FALSE, "Should return false when string contains numbers"), + arguments("abc!DEF", Boolean.FALSE, "Should return false when string contains symbols"), arguments("#abc", Boolean.FALSE, "Should return false when first character is not a letter"), arguments("abc", Boolean.TRUE, "Should return true for non-decreasing order"), + arguments("aBcD", Boolean.TRUE, "Should return true for mixed case increasing sequence"), arguments("a", Boolean.TRUE, "Should return true for single letter"), arguments("'", Boolean.FALSE, "Should return false for single symbol"), + arguments("aabbcc", Boolean.TRUE, "Should return true for repeated letters"), arguments("cba", Boolean.FALSE, "Should return false when order decreases"), arguments("abzba", Boolean.FALSE, "Should return false when middle breaks order")); } private void assertAlphabetical(String input, boolean expected, String message) { // Arrange & Act boolean result = Alphabetical.isAlphabetical(input); - // Assertion + // Assert assertEquals(expected, result, message); }