4

I need to create a helper method which allows to create a sum of any Iterable<? extends Number>, because we have many vectors and require a fast method to determine the sum, so I created the following method:

 static Integer sum(Iterable<Integer> it) {
    Integer result = 0;
    for(T next : it) {
        result += next;
    }
    return result;
 }

This method only works for ints however, but we also have doubles and longs. Because you can't have two methods with the same signature (Our compiler thinks Integer sum(Iterable<Integer>) has the same signature as Double sum(Iterable<Double>).) I tried to write one method with generics.

private static <T extends Number> T sum(Iterable<? extends T> it) {
    T result;
    for(T next : it) {
        result += next;
    }
    return result;
}

However this method will not compile (reason: the operator += is undefined for Object, Object). What can I do here? I know in C++ you can overload operators, but not in Java. But every class which extends Number does overload the += operator. What can I do here?

Thank you in advance.

  • You can use instanceOf Double, Integer, etc and cast it in such case to appropriate numeric type – rkosegi Jan 06 '14 at 12:39
  • instanceOf does not work, because _result_ is null after its declaration ... –  Jan 06 '14 at 12:47
  • 1
    if it's going to be one of the most important calculations in the program, I suggest getting rid of AnyGenericClass as well as AnyGenericClass in the first place, because it is VERY inefficient, and use arrays of primitive types instead. – Display Name Jan 06 '14 at 12:51

6 Answers6

2

If the numbers cannot be BigIntegers or BigDecimals, you can try converting them to doubles and sum them as such:

double result = 0;
for (T number : it) {
    result += number.doubleValue();
}
Natix
  • 14,017
  • 7
  • 54
  • 69
0

Have a look at How to add two java.lang.Numbers?

You don't know the type of the numbers you sum so either use next.doubleValue() if you can tolerate precision loss or have a look at BigDecimal if you want to keep precision (use the String constructor).

Community
  • 1
  • 1
Ununhexium
  • 105
  • 1
  • 5
0

The reason your method works with java.lang.Integer is because of auto-boxing.

Unfortunately java.lang.Number is a representation of a numeric value in a very general sense, specifically that you get it's value as one of the concrete number types but cannot do much else with it.

What this means in your case is that you're going to have a sum method for each return type that important to you.

Basically you'll end up with something like this (which assumes you'll want to round floating point numbers when summing them to a non-floating point format):

public class SumUtils
{
  public static Integer sumToInteger(Iterable<Number> numbers)
  {
    long sum = 0;

    for (Number number : numbers)
    {
      sum += Math.round(number.doubleValue());
    }

    if (sum > Integer.MAX_INT)
    {
      throw new IllegalStateException();
    }  

    return (int)sum;
  }

  public static Long sumToLong(Iterable<Number> numbers)
  {
    long sum = 0;

    for (Number number : numbers)
    {
      sum += Math.round(number.doubleValue());
    }

    return sum;
  }

  public static Float sumToFloat(Iterable<Number> numbers)
  {
    double sum = 0;

    for (Number number : numbers)
    {
      sum += number.doubleValue();
    }

    if (sum > Float.MAX_FLOAT)
    {
      throw new IllegalStateException();
    }  

    return (float)sum;
  }

  public static Double sumToDouble(Iterable<Number> numbers)
  {
    double sum = 0;

    for (Number number : numbers)
    {
      sum += number.doubleValue();
    }

    return sum;
  }

  public static BigDecimal sumToBigDecimal(Iterable<Number> numbers)
  {
    BigDecimal sum = BigDecimal.ZERO;

    for (Number number : numbers)
    {
      if (number instanceof BigDecimal)
      {
        sum = sum.add((BigDecimal)number);
      }
      else 
      { 
        sum = sum.add(new BigDecimal(number.doubleValue()));
      }
    }

    return sum;
  }
}
Nick Holt
  • 33,455
  • 4
  • 52
  • 58
  • Our problem is that we don't know which type the Iterable has, which we receive through the interface. We only know that it is any kind of Iterable extends Number>. Your solution seems to be a good workaround, but does not really fulfil our requirements. –  Jan 06 '14 at 12:53
  • OK - in that case you just need to treat it as a double and round accordingly. I'll modify the answer to show you. – Nick Holt Jan 06 '14 at 13:00
0

If all the numbers are of the same (unknown) type, then you don't need to check every element, just get type of first and choose corresponding loop to calculate sum in either double, long, BigDecimal or BigInteger.

Display Name
  • 8,022
  • 3
  • 31
  • 66
0

You can try instanceof checking and then casting on each iteration Lame solution, howewer

       private static <T extends Number> T sum(Iterable<? extends T> it)
       {
          T result = null;
          Integer inttt = null;

          if (it.iterator()
                .hasNext() && it.iterator()
                .next() instanceof Integer)
          {

             for (T next : it)
             {
                if (next instanceof Integer)
                {
                   inttt += (Integer) next;
                }

             }
              return (T)inttt;
          }
// For other types here
          return result;
       }
lopushen
  • 1,117
  • 2
  • 11
  • 32
0

So I wrote the following now, but I am not very satisfied ...

static <T extends Number> T sum(Iterable<? extends T> it) {
    Iterator<? extends T> iterator = it.iterator();
    Number first = iterator.next();

    if(first instanceof Integer) {
        Integer _result = (Integer) first;
        for(T next : it)
            _result+=(Integer)next;
        return (T) _result;
    }
    else if(first instanceof Double) {
        Double _result = (Double) first;
        for(T next : it)
            _result+=(Double)next;
        return (T) _result;
    }
    else if(first instanceof Long) {
        Long _result = (Long) first;
        for(T next : it)
            _result+=(Long)next;
        return (T) _result;
    }
    else if(first instanceof Float) {
        Float _result = (Float) first;
        for(T next : it)
            _result+=(Float)next;
        return (T) _result;
    }
    else if(first instanceof Byte) {
        Byte _result = (Byte) first;
        for(T next : it)
            _result= (byte)(_result + (Byte)next);
        return (T) _result;
    }
    else if(first instanceof Short) {
        Short _result = (Short) first;
        for(T next : it)
            _result= (short)(_result + (Short)next);
        return (T) _result;
    }
    else if(first instanceof java.math.BigInteger) {
        java.math.BigInteger _result = (java.math.BigInteger) first;
        for(T next : it)
            _result=((java.math.BigInteger)next).add((BigInteger) next);
        return (T) _result;
    }
    else if(first instanceof java.math.BigDecimal) {
        java.math.BigDecimal _result = (java.math.BigDecimal) first;
        for(T next : it)
            _result=((java.math.BigDecimal)next).add((BigDecimal) next);
        return (T) _result;
    }
    else {
        throw new IllegalArgumentException(I18n._(String.format("Type %s not supported."), first.getClass()));
    }
}
  • 1
    This will only work if all the numbers are the same type, which isn't very clear from outside the method. For example `sum(Arrays.asList(1, 1.2, 3)` would cause a `ClassCastException`. – Nick Holt Jan 06 '14 at 13:22
  • 1
    by the way, this code snippet shows the great expression power of Java – Display Name Jan 22 '14 at 19:44