简体   繁体   中英

Java 1.7: Sum of Iterable<T extends Number>

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.

If the numbers cannot be BigInteger s or BigDecimal s, you can try converting them to double s and sum them as such:

double result = 0;
for (T number : it) {
    result += number.doubleValue();
}

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).

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;
  }
}

如果所有数字都是相同(未知)类型,那么您不需要检查每个元素,只需获取第一个类型并选择相应的循环来计算doublelongBigDecimalBigInteger sum。

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;
       }

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()));
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM