Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sum of ArrayList for different types

Tags:

java

arraylist

Is it possible to write a single method total to do a sum of all elements of an ArrayList, where it is of type <Integer> or <Long>?

I cannot just write

public long total(ArrayList<Integer> list) 

and

public long total(ArrayList<Long> list) 

together as there will be an error of erasure, and Integer does not automatically extends to Long and vice versa... but the code inside is identical!

like image 403
Ivan Avatar asked Dec 14 '22 13:12

Ivan


1 Answers

Yes, you can implement such a method, since both Integer and Long extend Number. For example you can use a wildcard type for the list element type:

public static long total(List<? extends Number> list) {
    long sum = 0;
    for (Number n : list) {
        if (!(n instanceof Byte || n instanceof Short || n instanceof Integer || n instanceof Long)) {
            throw new IllegalArgumentException();
        }
        sum += n.longValue();
    }
    return sum;
}

This only works for the integral types however, since the sum variable and the return value are of type long.

Ideally you would like to be able to also use the method with Floats and Doubles and return an object of the same type as the list element type, but this is not easy to do for two reasons:

  1. The only thing you can do with a Number is to get its value as one of the primitive number types. You can not sum two of them in a number dependent way.
  2. It is not possible to create a 0-object of the right class.

EDIT: Much later...

Just for fun, lets do this in a nice way for Java. The thing you have to do is to manually provide the two operations mentioned above. A kind of value with two such operation is usually called a monoid in the context of algebra and functional programming.

The problem can be solved by creating objects that represent the monoid operations:

interface MonoidOps<T> {
    T id();
    T op(T o1, T o2);
}

The total method can now be implemented to take an object of this type in addition to the list:

public static <T> T total(List<T> list, MonoidOps<T> ops) {
    T sum = ops.id();
    for (T e : list) {
        sum = ops.op(e, sum);
    }
    return sum;
}

To provide MonoidOps implementations for the numeric classes, lets create a simple helper class:

class SimpleMonoidOps<T> implements MonoidOps<T> {
    private final T idElem;
    private final BinaryOperator<T> operation;

    public SimpleMonoidOps(T idElem, BinaryOperator<T> operation) {
        this.idElem = idElem;
        this.operation = operation;
    }

    public T id() {
        return idElem;
    }

    public T op(T o1, T o2) {
        return operation.apply(o1, o2);
    }
}

The MonoidOps implementations can now be written neatly like this:

static final MonoidOps<Integer> INT_MONOID_OPS = new SimpleMonoidOps<>(0, Integer::sum);
static final MonoidOps<Double> DOUBLE_MONOID_OPS = new SimpleMonoidOps<>(0.0, Double::sum);

And the total method would be called like this:

int sum = total(Arrays.asList(1, 2, 3), INT_MONOID_OPS);
like image 75
Lii Avatar answered Jan 04 '23 12:01

Lii