问题
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 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();
}
回答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