Java 1.7:Iterable <T extends Number>的总和

4

我需要创建一个帮助方法,可以对任何Iterable<? extends Number>进行求和,因为我们有许多向量,并且需要一种快速的方法来确定它们的总和,所以我创建了以下方法:

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

这种方法仅适用于整数,但我们还有浮点数和长整型。由于您不能拥有两个具有相同签名的方法(我们的编译器认为Integer sum(Iterable<Integer>)与Double sum(Iterable<Double>)具有相同的签名),因此我尝试编写一个泛型方法。

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

然而,这种方法将无法编译(原因:Object,Object未定义运算符+=)。我该怎么办?我知道在C++中可以重载操作符,但在Java中不行。但是扩展Number的每个类都会重载+=运算符。我该怎么办呢?

提前感谢您。


你可以使用instanceOf Double,Integer等,并在这种情况下将其转换为适当的数字类型。 - rkosegi
instanceOf不起作用,因为在其声明后_result_为空。 - user2366100
1
如果这是程序中最重要的计算之一,我建议首先摆脱AnyGenericClass<T extends Number>和AnyGenericClass<AnyBoxedNumberClass>,因为它非常低效,改用基本类型的数组。 - Display Name
6个回答

2
如果这些数字不能是 BigIntegerBigDecimal,你可以尝试将它们转换为 double,并将它们作为这样的数字进行求和:
double result = 0;
for (T number : it) {
    result += number.doubleValue();
}

你能详细说明一下吗? - Natix
只需查看此答案(我太懒了,不想再重复一遍)http://stackoverflow.com/a/4970056/1418097 - Display Name

0

你的方法能够使用 java.lang.Integer 是因为 auto-boxing

不幸的是,java.lang.Number 是一个非常通用的数字值表示方式,具体来说,你可以将其值作为其中一个具体数字类型之一获取,但不能做其他任何事情。

在你的情况下,这意味着你需要为每个对你重要的返回类型编写一个 sum 方法。

基本上,你最终会得到像这样的东西(假设你想将浮点数四舍五入到非浮点格式进行求和):

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

我们的问题是,我们不知道通过接口接收到的Iterable具有哪种类型。我们只知道它是任何一种Iterable <? extends Number>。您的解决方案似乎是一个很好的解决方法,但并不能真正满足我们的要求。 - user2366100
好的 - 在这种情况下,您只需要将其视为双精度并相应地四舍五入。我会修改答案向您展示。 - Nick Holt

0
你可以在每次迭代时尝试 instanceof 检查,然后进行强制类型转换。 这是一个不太好的解决方案。
       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;
       }

如果像sum(Arrays.asList(1, 1.2, 3)这样调用此方法,则会出错。 - Nick Holt

0

所以我现在写了以下内容,但我并不是很满意...

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
只有当所有数字都是相同类型时,此方法才能正常工作,从方法外部并不十分清楚。例如,sum(Arrays.asList(1, 1.2, 3) 将导致 ClassCastException - Nick Holt
1
顺便说一下,这段代码展示了Java的强大表达能力。 - Display Name

0

看一下如何将两个java.lang.Numbers相加?

你不知道你要相加的数字类型,因此,如果你可以容忍精度损失,可以使用next.doubleValue(),否则,如果你想保持精度(使用字符串构造函数),就可以看一下BigDecimal。


0
如果所有数字都是相同(未知)类型,则无需检查每个元素,只需获取第一个的类型并选择相应的循环来计算总和,可以使用doublelongBigDecimalBigInteger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接