Combining a few methods into the only one. Generics

109 views Asked by At

I have some code to figure out the correctness of manipulations on different data types (int, long, double) comparing to BigInteger. The manipulation is getting a factorial of a number until the result is the same as BigInteger has.

The question is how could i change my code, make more generic, compact and clean? How could i get the only one method, not 4 for different types? The logic in this methods is pretty the same as well as the flow.

The code (without comparing logic) is:

private static HashMap<BigInteger, BigInteger> bigIntegerFactorials = new HashMap<>();
private static BigInteger bigIntegerFactorial(BigInteger number) {
    if (number.equals(BigInteger.ONE)) {
        return BigInteger.ONE;
    }
    BigInteger result = bigIntegerFactorials.get(number);
    if (result == null) {
        result = number.multiply(bigIntegerFactorial(number.subtract(BigInteger.ONE)));
        bigIntegerFactorials.put(number, result);
    }
    return result;
}

private static HashMap<Integer, Integer> intFactorials = new HashMap<>();
private static int intFactorial(int number) {
    if (number == 1) {
        return 1;
    }
    Integer result = intFactorials.get(number);
    if (result == null) {
        result = number * intFactorial(number - 1);
        intFactorials.put(number, result);
    }
    return result;
}

private static HashMap<Long, Long> longFactorials = new HashMap<>();
private static long longFactorial(long number) {
    if (number == 1) {
        return 1L;
    }
    Long result = longFactorials.get(number);
    if (result == null) {
        result = number * longFactorial(number - 1);
        longFactorials.put(number, result);
    }
    return result;
}

private static HashMap<Double, Double> doubleFactorials = new HashMap<>();
private static double doubleFactorial(double number) {
    if (number == 1) {
        return 1.;
    }
    Double result = doubleFactorials.get(number);
    if (result == null) {
        result = number * doubleFactorial(number - 1);
        doubleFactorials.put(number, result);
    }
    return result;
}

Thanks a lot in advance.

2

There are 2 answers

1
assylias On BEST ANSWER

You could pass the multiply and decrement functions to a generic method:

private static Map<Number, Number> factorials = new HashMap<> ();

private static <T extends Number> T factorial(T n, BinaryOperator<T> multiply, UnaryOperator<T> decrement) {
  if (n.doubleValue() == 1) return n;
  T result = (T) factorials.get(n);
  if (result == null ){
    result = multiply.apply(n, factorial(decrement.apply(n), multiply, decrement));
    factorials.put(n, result);
  }
  return result;
}

Then you can change your primitive methods like so:

public static int intFactorial(int number) {
  return factorial(number, (i, j) -> i * j, i -> i - 1);
}

WARNING: this method seems to crash Netbeans but compiles fine with javac...

4
MadConan On

If you are really trying to compute a factorial, then there isn't any need for double or BigDouble since factorial only applies to integer values. And since that's true, you can convert any integer type to BigInteger and go with a single method that takes any Number returns BigInteger.

Here's a test class

public class Junk {
    public static void main(String[] args) {
        long val = 9;
        Junk j = new Junk();
        System.out.println(val + "! = " + j.factorial(val));
        BigInteger nine = new BigInteger("9");
        System.out.println(nine + "! = " + j.factorial(nine));
        short nine_short = 9;
        System.out.println(nine_short + "! = " + j.factorial(nine_short));
    }

    private HashMap<BigInteger, BigInteger> map = new HashMap<>();


    public BigInteger factorial(Number number){
        if(1 == number.intValue()){
            return BigInteger.ONE;
        }

        BigInteger bigInteger = new BigInteger(number.toString());
        BigInteger result = map.get(bigInteger);

        if(result == null){
            result = bigInteger.multiply(factorial(bigInteger.subtract(BigInteger.ONE)));
            map.put(bigInteger,result);
        }

        return result;
    }
}

And the output

9! = 362880
9! = 362880
9! = 362880