3
\$\begingroup\$

I have a "Palindrome" class that has some functions for verifying if certain things are Palindromes. For the verification, I have 2 different algorithms, one being recursive and the other iterative.

I'm happy with the algorithms however I'm unsure whether the way that I do the overloading and eventually parsing everything to the check charArray function is a smart thing.

I also have some Junit 5 Tests that prove that everything works. Here I'm just unsure if its good code with the multiple nests and the methods/tests I chose and if pretty much having duplicate code for the Iterative and Recursive algorithm is good. Thank you in advance.

Palindrome Class

package com.gr;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Lucifer Uchiha
 * @version 1.0
 */
public class Palindrome {

    /* Iterative */

    /**
     * Returns a boolean of whether the char array is a palindrome or not.
     * This is determined by using the iterative algorithm.
     *
     * @param chars char array containing the characters to be checked.
     * @return boolean of whether the char array is a palindrome or not.
     */
    public static boolean isCharArrayPalindromeIterative(char[] chars) {
        if (chars.length < 1)
            return false;
        char[] formattedChars = convertAllCharsToUpperCase(chars);
        boolean isPalindrome = true;
        for (int i = 0; i != formattedChars.length / 2; i++)
            if (formattedChars[i] != formattedChars[(formattedChars.length - 1) - i]) {
                isPalindrome = false;
                break;
            }
        return isPalindrome;
    }

    /**
     * Returns a boolean of whether the word of type String is a palindrome or not.
     * This is determined by using the iterative algorithm.
     *
     * @param word the word to be checked.
     * @return boolean of whether the word is a palindrome or not.
     */
    public static boolean isWordPalindromeIterative(String word) {
        return isCharArrayPalindromeIterative(word.toCharArray());
    }

    /**
     * Returns a boolean of whether the sentence of type String is a palindrome or not.
     * This is determined by using the iterative algorithm.
     *
     * @param sentence the sentence to be checked.
     * @return boolean of whether the sentence is a palindrome or not.
     */
    public static boolean isSentencePalindromeIterative(String sentence) {
        String newSentence = sentence.replaceAll("[^a-zA-Z]", "");
        return isWordPalindromeIterative(newSentence);
    }

    /**
     * Returns a boolean of whether the number of type byte (-128 to 127) is a palindrome or not.
     * This is determined by using the iterative algorithm.
     *
     * @param number the number to be checked.
     * @return boolean of whether the number is a palindrome or not.
     */
    public static boolean isNumberPalindromeIterative(byte number) {
        return isWordPalindromeIterative(String.valueOf(number));
    }

    /**
     * Returns a boolean of whether the number of type short (32,768 to 32,767) is a palindrome or not.
     * This is determined by using the iterative algorithm.
     *
     * @param number the number to be checked.
     * @return boolean of whether the number is a palindrome or not.
     */
    public static boolean isNumberPalindromeIterative(short number) {
        return isWordPalindromeIterative(String.valueOf(number));
    }

    /**
     * Returns a boolean of whether the number of type int (-2,147,483,648 to 2,147,483,647) is a palindrome or not.
     * This is determined by using the iterative algorithm.
     *
     * @param number the number to be checked.
     * @return boolean of whether the number is a palindrome or not.
     */
    public static boolean isNumberPalindromeIterative(int number) {
        return isWordPalindromeIterative(String.valueOf(number));
    }

    /**
     * Returns a boolean of whether the number of type long (-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807) is a palindrome or not.
     * This is determined by using the iterative algorithm.
     *
     * @param number the number to be checked.
     * @return boolean of whether the number is a palindrome or not.
     */
    public static boolean isNumberPalindromeIterative(long number) {
        return isWordPalindromeIterative(String.valueOf(number));
    }

    /**
     * Returns a List containing all the numbers that are palindromes in the range that is given from
     * start of type long to end of type long.
     * This is determined by using the iterative algorithm.
     *
     * @param start the start of the range, inclusive.
     * @param end   the end of the range, exclusive.
     * @return List containing all the numbers that are palindromes in the given range.
     */
    public static List<Long> getAllNumberPalindromesInRangeIterative(long start, long end) {
        List<Long> results = new ArrayList<>();
        for (long number = start; number != end; number++)
            if (isNumberPalindromeIterative(number))
                results.add(number);
        return results;
    }

    /**
     * Returns a List containing all the numbers that are palindromes in the range that is given from
     * start of type int to end of type int.
     * This is determined by using the iterative algorithm.
     *
     * @param start the start of the range, inclusive.
     * @param end   the end of the range, exclusive.
     * @return List containing all the numbers that are palindromes in the given range.
     */
    public static List<Integer> getAllNumberPalindromesInRangeIterative(int start, int end) {
        return convertLongListToIntegerList(getAllNumberPalindromesInRangeIterative((long) start, (long) end));
    }

    /**
     * Returns a List containing all the numbers that are palindromes in the range that is given from
     * start of type short to end of type short.
     * This is determined by using the iterative algorithm.
     *
     * @param start the start of the range, inclusive.
     * @param end   the end of the range, exclusive.
     * @return List containing all the numbers that are palindromes in the given range.
     */
    public static List<Short> getAllNumberPalindromesInRangeIterative(short start, short end) {
        return convertLongListToShortList(getAllNumberPalindromesInRangeIterative((long) start, (long) end));
    }

    /**
     * Returns a List containing all the numbers that are palindromes in the range that is given from
     * start of type byte to end of type byte.
     * This is determined by using the iterative algorithm.
     *
     * @param start the start of the range, inclusive.
     * @param end   the end of the range, exclusive.
     * @return List containing all the numbers that are palindromes in the given range.
     */
    public static List<Byte> getAllNumberPalindromesInRangeIterative(byte start, byte end) {
        return convertLongListToByteList(getAllNumberPalindromesInRangeIterative((long) start, (long) end));
    }

    /* Recursive */

    /**
     * Returns a boolean of whether the char array is a palindrome or not.
     * This is determined by using the recursive algorithm.
     *
     * @param chars char array containing the characters to be checked.
     * @return boolean of whether the char array is a palindrome or not.
     */
    public static boolean isCharArrayPalindromeRecursive(char[] chars) {
        if (chars.length < 1)
            return false;
        char[] formattedChars = convertAllCharsToUpperCase(chars);
        return recursion(formattedChars, 0, formattedChars.length - 1);
    }

    /**
     * The recursive algorithm.
     *
     * @param chars char array containing the characters to be checked.
     * @param start the left char being compared.
     * @param end   the right char being compared.
     * @return boolean of whether the char array is a palindrome or not.
     */
    private static boolean recursion(char[] chars, int start, int end) {
        if (start == end)
            return true;
        if (chars[start] != chars[end])
            return false;
        if (start < end + 1)
            return recursion(chars, ++start, --end);
        return true;
    }

    /**
     * Returns a boolean of whether the word of type String is a palindrome or not.
     * This is determined by using the recursive algorithm.
     *
     * @param word the word to be checked.
     * @return boolean of whether the word is a palindrome or not.
     */
    public static boolean isWordPalindromeRecursive(String word) {
        return isCharArrayPalindromeRecursive(word.toCharArray());
    }

    /**
     * Returns a boolean of whether the sentence of type String is a palindrome or not.
     * This is determined by using the recursive algorithm.
     *
     * @param sentence the sentence to be checked.
     * @return boolean of whether the sentence is a palindrome or not.
     */
    public static boolean isSentencePalindromeRecursive(String sentence) {
        String newSentence = sentence.replaceAll("[^a-zA-Z]", "");
        return isWordPalindromeRecursive(newSentence);
    }

    /**
     * Returns a boolean of whether the number of type byte (-128 to 127) is a palindrome or not.
     * This is determined by using the recursive algorithm.
     *
     * @param number the number to be checked.
     * @return boolean of whether the number is a palindrome or not.
     */
    public static boolean isNumberPalindromeRecursive(byte number) {
        return isWordPalindromeRecursive(String.valueOf(number));
    }

    /**
     * Returns a boolean of whether the number of type short (32,768 to 32,767) is a palindrome or not.
     * This is determined by using the recursive algorithm.
     *
     * @param number the number to be checked.
     * @return boolean of whether the number is a palindrome or not.
     */
    public static boolean isNumberPalindromeRecursive(short number) {
        return isWordPalindromeRecursive(String.valueOf(number));
    }

    /**
     * Returns a boolean of whether the number of type int (-2,147,483,648 to 2,147,483,647) is a palindrome or not.
     * This is determined by using the recursive algorithm.
     *
     * @param number the number to be checked.
     * @return boolean of whether the number is a palindrome or not.
     */
    public static boolean isNumberPalindromeRecursive(int number) {
        return isWordPalindromeRecursive(String.valueOf(number));
    }

    /**
     * Returns a boolean of whether the number of type long (-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807) is a palindrome or not.
     * This is determined by using the recursive algorithm.
     *
     * @param number the number to be checked.
     * @return boolean of whether the number is a palindrome or not.
     */
    public static boolean isNumberPalindromeRecursive(long number) {
        return isWordPalindromeRecursive(String.valueOf(number));
    }

    /**
     * Returns a List containing all the numbers that are palindromes in the range that is given from
     * start of type long to end of type long.
     * This is determined by using the recursive algorithm.
     *
     * @param start the start of the range, inclusive.
     * @param end   the end of the range, exclusive.
     * @return List containing all the numbers that are palindromes in the given range.
     */
    public static List<Long> getAllNumberPalindromesInRangeRecursive(long start, long end) {
        List<Long> results = new ArrayList<>();
        for (long number = start; number != end; number++)
            if (isNumberPalindromeRecursive(number))
                results.add(number);
        return results;
    }

    /**
     * Returns a List containing all the numbers that are palindromes in the range that is given from
     * start of type int to end of type int.
     * This is determined by using the recursive algorithm.
     *
     * @param start the start of the range, inclusive.
     * @param end   the end of the range, exclusive.
     * @return List containing all the numbers that are palindromes in the given range.
     */
    public static List<Integer> getAllNumberPalindromesInRangeRecursive(int start, int end) {
        return convertLongListToIntegerList(getAllNumberPalindromesInRangeRecursive((long) start, (long) end));
    }

    /**
     * Returns a List containing all the numbers that are palindromes in the range that is given from
     * start of type short to end of type short.
     * This is determined by using the recursive algorithm.
     *
     * @param start the start of the range, inclusive.
     * @param end   the end of the range, exclusive.
     * @return List containing all the numbers that are palindromes in the given range.
     */
    public static List<Short> getAllNumberPalindromesInRangeRecursive(short start, short end) {
        return convertLongListToShortList(getAllNumberPalindromesInRangeRecursive((long) start, (long) end));
    }

    /**
     * Returns a List containing all the numbers that are palindromes in the range that is given from
     * start of type byte to end of type byte.
     * This is determined by using the recursive algorithm.
     *
     * @param start the start of the range, inclusive.
     * @param end   the end of the range, exclusive.
     * @return List containing all the numbers that are palindromes in the given range.
     */
    public static List<Byte> getAllNumberPalindromesInRangeRecursive(byte start, byte end) {
        return convertLongListToByteList(getAllNumberPalindromesInRangeRecursive((long) start, (long) end));
    }

    /**
     * Converts all letters in the given char array to capital letters if they aren't already.
     *
     * @param chars the start of the range, inclusive.
     * @return char array with the capitalized letters.
     */
    private static char[] convertAllCharsToUpperCase(char[] chars) {
        char[] formattedChars = new char[chars.length];
        for (int i = 0; i != chars.length; i++)
            if (Character.isLetter(chars[i]) && Character.isLowerCase(chars[i]))
                formattedChars[i] = Character.toUpperCase(chars[i]);
            else
                formattedChars[i] = chars[i];
        return formattedChars;
    }

    /**
     * Converts a List containing Long values to a List of Bytes.
     *
     * @param listOfLongs the List containing the Long values
     * @return the List containing the Byte values
     */
    private static List<Byte> convertLongListToByteList(List<Long> listOfLongs) {
        List<Byte> result = new ArrayList<>();
        for (Long i : listOfLongs)
            result.add(i.byteValue());
        return result;
    }

    /**
     * Converts a List containing Long values to a List of Shorts.
     *
     * @param listOfLongs the List containing the Long values
     * @return the List containing the Shorts values
     */
    private static List<Short> convertLongListToShortList(List<Long> listOfLongs) {
        List<Short> result = new ArrayList<>();
        for (Long i : listOfLongs)
            result.add(i.shortValue());
        return result;
    }

    /**
     * Converts a List containing Long values to a List of Integers.
     *
     * @param listOfLongs the List containing the Long values
     * @return the List containing the Integers values
     */
    private static List<Integer> convertLongListToIntegerList(List<Long> listOfLongs) {
        List<Integer> result = new ArrayList<>();
        for (Long i : listOfLongs)
            result.add(i.intValue());
        return result;
    }
}

Palindrome Test Class

package com.gr;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

@DisplayName("Palindrome Class")
public class PalindromeTest {

    // Nested Iterative
    @Nested
    class Iterative {

        @Nested
        class Word {

            @Test
            void testEmptyString() {
                assertFalse(Palindrome.isWordPalindromeIterative(""));
            }

            @Test
            void testSingleLetter() {
                assertTrue(Palindrome.isWordPalindromeIterative("A"));
                assertTrue(Palindrome.isWordPalindromeIterative("a"));
            }

            @Test
            void testName() {
                assertTrue(Palindrome.isWordPalindromeIterative("ABBA"));
                assertTrue(Palindrome.isWordPalindromeIterative("Ava"));
                assertTrue(Palindrome.isWordPalindromeIterative("bob"));
                assertFalse(Palindrome.isWordPalindromeIterative("FAIL"));
                assertFalse(Palindrome.isWordPalindromeIterative("Fail"));
                assertFalse(Palindrome.isWordPalindromeIterative("fail"));
            }

            @Test
            void testWord() {
                assertTrue(Palindrome.isWordPalindromeIterative("madam"));
                assertTrue(Palindrome.isWordPalindromeIterative("Racecar"));
                assertTrue(Palindrome.isWordPalindromeIterative("RADAR"));
                assertFalse(Palindrome.isWordPalindromeIterative("FAIL"));
                assertFalse(Palindrome.isWordPalindromeIterative("Fail"));
                assertFalse(Palindrome.isWordPalindromeIterative("fail"));
            }
        }

        @Nested
        class Sentence {
            @Test
            void testEmptyString() {
                assertFalse(Palindrome.isSentencePalindromeIterative(""));
            }

            @Test
            void testSingleLetter() {
                assertTrue(Palindrome.isSentencePalindromeIterative("A"));
                assertTrue(Palindrome.isSentencePalindromeIterative("a"));
            }

            @Test
            void testSingleWord() {
                assertTrue(Palindrome.isSentencePalindromeIterative("madam"));
                assertTrue(Palindrome.isSentencePalindromeIterative("Racecar"));
                assertTrue(Palindrome.isSentencePalindromeIterative("RADAR"));
                assertFalse(Palindrome.isSentencePalindromeIterative("FAIL"));
                assertFalse(Palindrome.isSentencePalindromeIterative("Fail"));
                assertFalse(Palindrome.isSentencePalindromeIterative("fail"));
            }

            @Test
            void testSentence() {
                assertTrue(Palindrome.isSentencePalindromeIterative("Murder for a jar of red rum"));
                assertTrue(Palindrome.isSentencePalindromeIterative("Rats live on no evil star"));
                assertTrue(Palindrome.isSentencePalindromeIterative("step on no pets"));
                assertFalse(Palindrome.isSentencePalindromeIterative("This should fail"));
                assertFalse(Palindrome.isSentencePalindromeIterative("this should fail"));
            }

            @Test
            void testSentenceWithPunctuation() {
                assertTrue(Palindrome.isSentencePalindromeIterative("Do geese see God?"));
                assertTrue(Palindrome.isSentencePalindromeIterative("Live on time, emit no evil"));
                assertTrue(Palindrome.isSentencePalindromeIterative("live on time, emit no evil"));
                assertFalse(Palindrome.isSentencePalindromeIterative("Will this fail?"));
                assertFalse(Palindrome.isSentencePalindromeIterative("will this fail?"));
            }
        }

        @Nested
        class Number {

            @Test
            void testSingleLongNumber() {
                assertTrue(Palindrome.isNumberPalindromeIterative(0L));
                assertTrue(Palindrome.isNumberPalindromeIterative(1L));
                assertTrue(Palindrome.isNumberPalindromeIterative(3L));
            }

            @Test
            void testBiggerLongNumber() {
                assertTrue(Palindrome.isNumberPalindromeIterative(123454321L));
                assertTrue(Palindrome.isNumberPalindromeIterative(1234567890987654321L));
                assertFalse(Palindrome.isNumberPalindromeIterative(123456789L));
                assertFalse(Palindrome.isNumberPalindromeIterative(1234567890123456789L));
            }

            @Test
            void testNegativeLongNumber() {
                assertTrue(Palindrome.isNumberPalindromeIterative(-0L));
                assertFalse(Palindrome.isNumberPalindromeIterative(-123454321L));
                assertFalse(Palindrome.isNumberPalindromeIterative(-1234567890987654321L));
                assertFalse(Palindrome.isNumberPalindromeIterative(-123456789L));
                assertFalse(Palindrome.isNumberPalindromeIterative(-1234567890123456789L));
            }

            @Test
            void testSingleIntegerNumber() {
                assertTrue(Palindrome.isNumberPalindromeIterative(0));
                assertTrue(Palindrome.isNumberPalindromeIterative(1));
                assertTrue(Palindrome.isNumberPalindromeIterative(3));
            }

            @Test
            void testBiggerIntegerNumber() {
                assertTrue(Palindrome.isNumberPalindromeIterative(123454321));
                assertFalse(Palindrome.isNumberPalindromeIterative(123456789));
            }

            @Test
            void testNegativeIntegerNumber() {
                assertTrue(Palindrome.isNumberPalindromeIterative(-0));
                assertFalse(Palindrome.isNumberPalindromeIterative(-123454321));
                assertFalse(Palindrome.isNumberPalindromeIterative(-123456789));
            }

            @Test
            void testSingleShortNumber() {
                assertTrue(Palindrome.isNumberPalindromeIterative((short) 0));
                assertTrue(Palindrome.isNumberPalindromeIterative((short) 1));
                assertTrue(Palindrome.isNumberPalindromeIterative((short) 3));
            }

            @Test
            void testBiggerShortNumber() {
                assertTrue(Palindrome.isNumberPalindromeIterative((short) 12321));
                assertFalse(Palindrome.isNumberPalindromeIterative((short) 12345));
            }

            @Test
            void testNegativeShortNumber() {
                assertTrue(Palindrome.isNumberPalindromeIterative((short) -0));
                assertFalse(Palindrome.isNumberPalindromeIterative((short) -12321));
                assertFalse(Palindrome.isNumberPalindromeIterative((short) -12345));
            }

            @Test
            void testSingleByteNumber() {
                assertTrue(Palindrome.isNumberPalindromeIterative((byte) 0));
                assertTrue(Palindrome.isNumberPalindromeIterative((byte) 1));
                assertTrue(Palindrome.isNumberPalindromeIterative((byte) 3));
            }

            @Test
            void testBiggerByteNumber() {
                assertTrue(Palindrome.isNumberPalindromeIterative((byte) 121));
                assertFalse(Palindrome.isNumberPalindromeIterative((byte) 123));
            }

            @Test
            void testNegativeByteNumber() {
                assertTrue(Palindrome.isNumberPalindromeIterative((byte) -0));
                assertFalse(Palindrome.isNumberPalindromeIterative((byte) -121));
                assertFalse(Palindrome.isNumberPalindromeIterative((byte) -123));
            }

        }

        @Nested
        class NumberInRange {
            @Test
            void testEmptyRangeLong() {
                List<Long> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative(122L, 130L));
            }

            @Test
            void testRangeSingleLong() {
                List<Long> expected = new ArrayList<>() {
                    {
                        add(1L);
                        add(2L);
                        add(3L);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative(1L, 4L));
            }

            @Test
            void testRangeLong() {
                List<Long> expected = new ArrayList<>() {
                    {
                        add(121L);
                        add(131L);
                        add(141L);
                        add(151L);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative(120L, 155L));
            }

            @Test
            void testNegativeRangeLong() {
                List<Long> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative(-131L, 0L));
            }

            @Test
            void testEmptyRangeInteger() {
                List<Integer> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative(122, 130));
            }

            @Test
            void testRangeSingleInteger() {
                List<Integer> expected = new ArrayList<>() {
                    {
                        add(1);
                        add(2);
                        add(3);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative(1, 4));
            }

            @Test
            void testRangeInteger() {
                List<Integer> expected = new ArrayList<>() {
                    {
                        add(121);
                        add(131);
                        add(141);
                        add(151);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative(120, 155));
            }

            @Test
            void testNegativeRangeInteger() {
                List<Integer> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative(-131, 0));
            }

            @Test
            void testEmptyRangeShort() {
                List<Short> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative((short) 122, (short) 130));
            }

            @Test
            void testRangeSingleShort() {
                List<Short> expected = new ArrayList<>() {
                    {
                        add((short) 1);
                        add((short) 2);
                        add((short) 3);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative((short) 1, (short) 4));
            }

            @Test
            void testRangeShort() {
                List<Short> expected = new ArrayList<>() {
                    {
                        add((short) 121);
                        add((short) 131);
                        add((short) 141);
                        add((short) 151);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative((short) 120, (short) 155));
            }

            @Test
            void testNegativeRangeShort() {
                List<Short> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative((short) -131, (short) 0));
            }

            @Test
            void testEmptyRangeByte() {
                List<Byte> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative((byte) 122, (byte) 125));
            }

            @Test
            void testRangeSingleByte() {
                List<Byte> expected = new ArrayList<>() {
                    {
                        add((byte) 1);
                        add((byte) 2);
                        add((byte) 3);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative((byte) 1, (byte) 4));
            }

            @Test
            void testRangeByte() {
                List<Byte> expected = new ArrayList<>() {
                    {
                        add((byte) 101);
                        add((byte) 111);
                        add((byte) 121);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative((byte) 100, (byte) 125));
            }

            @Test
            void testNegativeRangeByte() {
                List<Byte> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeIterative((byte) -125, (byte) 0));
            }
        }
    }

    @Nested
    class Recursive {
        @Nested
        class Word {

            @Test
            void testEmptyString() {
                assertFalse(Palindrome.isWordPalindromeRecursive(""));
            }

            @Test
            void testSingleLetter() {
                assertTrue(Palindrome.isWordPalindromeRecursive("A"));
                assertTrue(Palindrome.isWordPalindromeRecursive("a"));
            }

            @Test
            void testName() {
                assertTrue(Palindrome.isWordPalindromeRecursive("ABBA"));
                assertTrue(Palindrome.isWordPalindromeRecursive("Ava"));
                assertTrue(Palindrome.isWordPalindromeRecursive("bob"));
                assertFalse(Palindrome.isWordPalindromeRecursive("FAIL"));
                assertFalse(Palindrome.isWordPalindromeRecursive("Fail"));
                assertFalse(Palindrome.isWordPalindromeRecursive("fail"));
            }

            @Test
            void testWord() {
                assertTrue(Palindrome.isWordPalindromeRecursive("madam"));
                assertTrue(Palindrome.isWordPalindromeRecursive("Racecar"));
                assertTrue(Palindrome.isWordPalindromeRecursive("RADAR"));
                assertFalse(Palindrome.isWordPalindromeRecursive("FAIL"));
                assertFalse(Palindrome.isWordPalindromeRecursive("Fail"));
                assertFalse(Palindrome.isWordPalindromeRecursive("fail"));
            }
        }

        @Nested
        class Sentence {
            @Test
            void testEmptyString() {
                assertFalse(Palindrome.isSentencePalindromeRecursive(""));
            }

            @Test
            void testSingleLetter() {
                assertTrue(Palindrome.isSentencePalindromeRecursive("A"));
                assertTrue(Palindrome.isSentencePalindromeRecursive("a"));
            }

            @Test
            void testSingleWord() {
                assertTrue(Palindrome.isSentencePalindromeRecursive("madam"));
                assertTrue(Palindrome.isSentencePalindromeRecursive("Racecar"));
                assertTrue(Palindrome.isSentencePalindromeRecursive("RADAR"));
                assertFalse(Palindrome.isSentencePalindromeRecursive("FAIL"));
                assertFalse(Palindrome.isSentencePalindromeRecursive("Fail"));
                assertFalse(Palindrome.isSentencePalindromeRecursive("fail"));
            }

            @Test
            void testSentence() {
                assertTrue(Palindrome.isSentencePalindromeRecursive("Murder for a jar of red rum"));
                assertTrue(Palindrome.isSentencePalindromeRecursive("Rats live on no evil star"));
                assertTrue(Palindrome.isSentencePalindromeRecursive("step on no pets"));
                assertFalse(Palindrome.isSentencePalindromeRecursive("This should fail"));
                assertFalse(Palindrome.isSentencePalindromeRecursive("this should fail"));
            }

            @Test
            void testSentenceWithPunctuation() {
                assertTrue(Palindrome.isSentencePalindromeRecursive("Do geese see God?"));
                assertTrue(Palindrome.isSentencePalindromeRecursive("Live on time, emit no evil"));
                assertTrue(Palindrome.isSentencePalindromeRecursive("live on time, emit no evil"));
                assertFalse(Palindrome.isSentencePalindromeRecursive("Will this fail?"));
                assertFalse(Palindrome.isSentencePalindromeRecursive("will this fail?"));
            }
        }

        @Nested
        class Number {

            @Test
            void testSingleLongNumber() {
                assertTrue(Palindrome.isNumberPalindromeRecursive(0L));
                assertTrue(Palindrome.isNumberPalindromeRecursive(1L));
                assertTrue(Palindrome.isNumberPalindromeRecursive(3L));
            }

            @Test
            void testBiggerLongNumber() {
                assertTrue(Palindrome.isNumberPalindromeRecursive(123454321L));
                assertTrue(Palindrome.isNumberPalindromeRecursive(1234567890987654321L));
                assertFalse(Palindrome.isNumberPalindromeRecursive(123456789L));
                assertFalse(Palindrome.isNumberPalindromeRecursive(1234567890123456789L));
            }

            @Test
            void testNegativeLongNumber() {
                assertTrue(Palindrome.isNumberPalindromeRecursive(-0L));
                assertFalse(Palindrome.isNumberPalindromeRecursive(-123454321L));
                assertFalse(Palindrome.isNumberPalindromeRecursive(-1234567890987654321L));
                assertFalse(Palindrome.isNumberPalindromeRecursive(-123456789L));
                assertFalse(Palindrome.isNumberPalindromeRecursive(-1234567890123456789L));
            }

            @Test
            void testSingleIntegerNumber() {
                assertTrue(Palindrome.isNumberPalindromeRecursive(0));
                assertTrue(Palindrome.isNumberPalindromeRecursive(1));
                assertTrue(Palindrome.isNumberPalindromeRecursive(3));
            }

            @Test
            void testBiggerIntegerNumber() {
                assertTrue(Palindrome.isNumberPalindromeRecursive(123454321));
                assertFalse(Palindrome.isNumberPalindromeRecursive(123456789));
            }

            @Test
            void testNegativeIntegerNumber() {
                assertTrue(Palindrome.isNumberPalindromeRecursive(-0));
                assertFalse(Palindrome.isNumberPalindromeRecursive(-123454321));
                assertFalse(Palindrome.isNumberPalindromeRecursive(-123456789));
            }

            @Test
            void testSingleShortNumber() {
                assertTrue(Palindrome.isNumberPalindromeRecursive((short) 0));
                assertTrue(Palindrome.isNumberPalindromeRecursive((short) 1));
                assertTrue(Palindrome.isNumberPalindromeRecursive((short) 3));
            }

            @Test
            void testBiggerShortNumber() {
                assertTrue(Palindrome.isNumberPalindromeRecursive((short) 12321));
                assertFalse(Palindrome.isNumberPalindromeRecursive((short) 12345));
            }

            @Test
            void testNegativeShortNumber() {
                assertTrue(Palindrome.isNumberPalindromeRecursive((short) -0));
                assertFalse(Palindrome.isNumberPalindromeRecursive((short) -12321));
                assertFalse(Palindrome.isNumberPalindromeRecursive((short) -12345));
            }

            @Test
            void testSingleByteNumber() {
                assertTrue(Palindrome.isNumberPalindromeRecursive((byte) 0));
                assertTrue(Palindrome.isNumberPalindromeRecursive((byte) 1));
                assertTrue(Palindrome.isNumberPalindromeRecursive((byte) 3));
            }

            @Test
            void testBiggerByteNumber() {
                assertTrue(Palindrome.isNumberPalindromeRecursive((byte) 121));
                assertFalse(Palindrome.isNumberPalindromeRecursive((byte) 123));
            }

            @Test
            void testNegativeByteNumber() {
                assertTrue(Palindrome.isNumberPalindromeRecursive((byte) -0));
                assertFalse(Palindrome.isNumberPalindromeRecursive((byte) -121));
                assertFalse(Palindrome.isNumberPalindromeRecursive((byte) -123));
            }

        }

        @Nested
        class NumberInRange {
            @Test
            void testEmptyRangeLong() {
                List<Long> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive(122L, 130L));
            }

            @Test
            void testRangeSingleLong() {
                List<Long> expected = new ArrayList<>() {
                    {
                        add(1L);
                        add(2L);
                        add(3L);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive(1L, 4L));
            }

            @Test
            void testRangeLong() {
                List<Long> expected = new ArrayList<>() {
                    {
                        add(121L);
                        add(131L);
                        add(141L);
                        add(151L);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive(120L, 155L));
            }

            @Test
            void testNegativeRangeLong() {
                List<Long> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive(-131L, 0L));
            }

            @Test
            void testEmptyRangeInteger() {
                List<Integer> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive(122, 130));
            }

            @Test
            void testRangeSingleInteger() {
                List<Integer> expected = new ArrayList<>() {
                    {
                        add(1);
                        add(2);
                        add(3);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive(1, 4));
            }

            @Test
            void testRangeInteger() {
                List<Integer> expected = new ArrayList<>() {
                    {
                        add(121);
                        add(131);
                        add(141);
                        add(151);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive(120, 155));
            }

            @Test
            void testNegativeRangeInteger() {
                List<Integer> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive(-131, 0));
            }

            @Test
            void testEmptyRangeShort() {
                List<Short> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive((short) 122, (short) 130));
            }

            @Test
            void testRangeSingleShort() {
                List<Short> expected = new ArrayList<>() {
                    {
                        add((short) 1);
                        add((short) 2);
                        add((short) 3);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive((short) 1, (short) 4));
            }

            @Test
            void testRangeShort() {
                List<Short> expected = new ArrayList<>() {
                    {
                        add((short) 121);
                        add((short) 131);
                        add((short) 141);
                        add((short) 151);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive((short) 120, (short) 155));
            }

            @Test
            void testNegativeRangeShort() {
                List<Short> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive((short) -131, (short) 0));
            }

            @Test
            void testEmptyRangeByte() {
                List<Byte> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive((byte) 122, (byte) 125));
            }

            @Test
            void testRangeSingleByte() {
                List<Byte> expected = new ArrayList<>() {
                    {
                        add((byte) 1);
                        add((byte) 2);
                        add((byte) 3);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive((byte) 1, (byte) 4));
            }

            @Test
            void testRangeByte() {
                List<Byte> expected = new ArrayList<>() {
                    {
                        add((byte) 101);
                        add((byte) 111);
                        add((byte) 121);
                    }
                };
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive((byte) 100, (byte) 125));
            }

            @Test
            void testNegativeRangeByte() {
                List<Byte> expected = new ArrayList<>();
                assertEquals(expected, Palindrome.getAllNumberPalindromesInRangeRecursive((byte) -125, (byte) 0));
            }
        }
    }
}
```
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

This looks great, enjoyable to read despite the repetition.


package com.gr;

Packages should associate with the author, so they should be something like com.github.lucifer.palindrome for example. But depending on whether this code is being published or not, it does not matter in this case.


This would be a great exercise for object-oriented programming by creating an interface and having two separate implementations:

public interface PalindromeTester;
public class IterativePalindromeTester implements PalindromeTester;
public class RecursivePalindromeTester implements PalindromeTester;

That would solve one-half of your overloading question. The other half is the Number/CharArray/Word thing, you should drop that from the name as it is obvious from the accepted parameters. IT would also cleanup your test-case a bit, as it would be two different test-classes. You even have an abstract test-class and extend that and only set a different instance of PalindromeTester on BeforeEach.

Another thing is, if you can live with widening given values, you could only provide a single method accepting a long. Any short/int will be automatically converted, but that, of course, requires a widening operation under the hood.

There's also Number/BigInteger, which you might want to include.

On another note, you could drop char[] in favor of CharSequence. The later is the base for many different classes (including String, so no overload required) and represents the intent a little better. Regarding that, if you accept any letter, including foreign languages, you most likely want to to work on code-points (int) instead of chars. In UTF-8/UTF-16, not all letters are only a single byte, but can be composed of multiples bytes (up to four, hence int).


 * Returns a boolean of whether the number of type short (32,768 to 32,767)

Don't include min/max values like that in the documentation. First, it's redundant as it is obvious from the used type. Second, it is prone to typos, like in this case.


        List<Long> results = new ArrayList<>();
        for (long number = start; number != end; number++)
            if (isNumberPalindromeRecursive(number))
                results.add(number);

Be aware that this is autoboxing, meaning a primitive is automatically converted to an Object instance.


for (int i = 0; i != formattedChars.length / 2; i++)

First, I'm a persistent advocate of "real" names for loop variables, like index or counter.

Second, you should revise the break condition to be more robust by checking whether the value is equal or greater the half length.


        boolean isPalindrome = true;
        for (int i = 0; i != formattedChars.length / 2; i++)
            if (formattedChars[i] != formattedChars[(formattedChars.length - 1) - i]) {
                isPalindrome = false;
                break;
            }
        return isPalindrome;

Don't store the result, return it directly.

        for (int i = 0; i != formattedChars.length / 2; i++)
            if (formattedChars[i] != formattedChars[(formattedChars.length - 1) - i])
                return false;
        
        return true;
```
\$\endgroup\$
2
  • \$\begingroup\$ Hi, thx for the extensive feedback. gr are my initials which is why I named the package com.gr but I could change it to my Github name since I have a repo there for this project. Im guessing I can create an Interface and 2 implementations for the code itself and also the test cases? \$\endgroup\$
    – George R
    Commented Sep 1, 2020 at 8:36
  • \$\begingroup\$ I wouldn't create an interface for the test-cases, but an abstract test-case with concrete implementations for each implementation would be suitable. \$\endgroup\$
    – Bobby
    Commented Sep 1, 2020 at 18:16
1
\$\begingroup\$

Having a common interface for the algorithm would solve the code duplication between tests for different implementations. You create a set of tests that any implementation of the interface should fulfill and then just throw different implementations at it (see the L in SOLID principles).

For testing a large set of simple string inputs, put the valid entries into one array and invalid ones in another and create two tests that loop over each array.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.