Java 1.7: Sum of Iterable<T extends Number>

放肆的年华 提交于 2021-01-27 07:33:46

问题


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.


回答1:


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



回答2:


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




回答3:


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.




回答4:


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



回答5:


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



回答6:


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


来源:https://stackoverflow.com/questions/20950060/java-1-7-sum-of-iterablet-extends-number

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!