【问题标题】:java - comparing numbers of different type, semanticallyjava - 从语义上比较不同类型的数字
【发布时间】:2015-01-13 12:42:52
【问题描述】:

假设我有三个数字:

Comparable n1 = new Integer(432);
Comparable n2 = new Long(40);
Comparable n3 = new Double(500.12);

我想通过Comparable 接口比较这些数字。但是,这会导致ClassCastException。

n1.compareTo(n2);

有没有办法(实用函数、外部库等)来比较不同类型的数字?在上面的例子中,数字n2小于n1,n1小于n3,语义上。

在代码中,我不知道数字的具体类型。因此,我依赖Comparable 接口。

【问题讨论】:

  • 您可以将它们全部转换为相同的类型(在本例中为 double)并从那里开始工作。除此之外,我认为没有适合您的标准界面。我认为可比不是正确的选择
  • 是的,这行得通。我正在寻找可以为我做这件事的函数或库。我宁愿重用也不愿自己实现。我想我不是第一个。
  • 我假设您认为您可能有可以实现 Comparable 的数字类,但不能使用正常的比较运算符(即 )?例如,BigDecimal。否则 - 如果你被限制为 int、float、double、long - 我只会使用小于大于运算符,例如int b = n3 > n2 ? 1 : (n3 < n2 ? -1 : 0); 将给出与 n3.compareTo(n2) 相同的结果

标签: java numbers comparison comparator comparable


【解决方案1】:

first answer 完全正确地解释了为什么会出现 ClassCastException - Comparable 接口是通用接口,只能用于相同具体类型的对象。

可能还有其他方法可以解决您的问题:

基元及其对象包装器

如果您确定所有数字都是基元 (int, long, float, double) 或表示基元的类的对象 (Integer, Long, Float, Double),那么您可以只使用普通的比较运算符,例如等同于n1.compareTo(n2);

int b = n1 > n2 ? 1 : (n1 < n2 ? -1 : 0);

Number 的子类

如果您确定所有变量都属于扩展 Number 的类,即 AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short,则可以使用以下命令:

    private int compareNumbers(Number n1, Number n2)
    {
        Double n2c = n2.doubleValue();
        Double n1c = n1.doubleValue();

        return n1c.compareTo(n2c);
    }

    public static void main (String[] args)
    {
        Integer n1 = new Integer(432);
        Long n2 = new Long(40);
        Double n3 = new Double(500.12);

        System.out.println(compareNumbers(n1,n2));
    }

编辑

针对与 OP 的评论讨论 - 我们注意到,如果比较两个超出 @ 范围的 BigDecimal 或 BigInteger 数字,使用 Double 作为 Number 比较的基类可能会产生虚假相等987654334@(因此 .doubleValue() 会根据需要返回 Double.POSITIVE_INFINITY 或 Double.NEGATIVE_INFINITY)

如果这是一个问题,compareNumbers() 可以更改如下:

    private int compareNumbers(Number n1, Number n2)
    {
        BigDecimal n2c = new BigDecimal(n2.toString());
        BigDecimal n1c = new BigDecimal(n1.toString());

        return n1c.compareTo(n2c);
    }

/编辑

实现Comparable,但不要扩展Number

如果您甚至无法确定您的变量是否扩展了 Number,并且您可能正在处理具有表示数字的类的变量,则实现 Comparable,但不扩展 Number,我相信您只能比较参数,如果他们属于同一类。在这种情况下,这应该可以工作,但我真的无法想象用例:

    Comparable n1 = new WierdNonNumberExtendingInteger(432);
    Comparable n2 = new WierdNonNumberExtendingLong(40);
    Comparable n3 = new WierdNonNumberExtendingDouble(500.12);

    Integer b =  null;
    if (n1.getClass().isInstance(n2))
    {
       b =  n1.compareTo(n2);
    }
    else
    {
       System.err.println("Can't compare apples and oranges");
       System.err.print("You tried to compare " + n1.getClass().getSimpleName());
       System.err.print(" with " + n2.getClass().getSimpleName());
    }

    System.out.println(b);

【讨论】:

  • 感谢您的广泛反馈。在您的第二个选项中,您将所有原始数字转换为 double,因为 double 有足够的空间来表示所有原始类型?这种说法总是正确的吗?
  • 就我而言,所有数字确实是Number 的子类。但我事先不知道某个数字是否会由int 或long 或float 或...表示,也可能是BigInteger 或BigDecimal。跨度>
  • 第一个说法是正确的,所有的原语都可以在双倍空间中表示(尽管不是完全精确)。由于BigInteger 和BigDecimal 扩展编号,它们还必须在.doubleValue() 中返回某些内容,因此选项2 不仅仅适用于原语。我不得不承认——如果BigDecimal 和BigInteger 代表的值大于Double.MAX_VALUE,我不确定它们的行为方式。他们被要求实施.doubleValue(),因此检查他们的 API 文档/规范应该会澄清。
  • 我得去看看。如果BigDecimal 的BigInteger 的值大于Double.MAX_VALUE,我认为结果将是不确定的。
  • 抱歉连续评论 - 刚刚检查。如果大数的幅度太大,它们会根据需要返回 Double.POSITIVE_INFINITY 或 Double.NEGATIVE_INFINITY。在这种情况下,它不会给出未定义的值,但会给出虚假的相等性。我会相应地编辑我的答案。当然 - 你总是可以使用 BigDecimal 作为你的基类进行比较......
【解决方案2】:

你不能这样比较它们。 Comparable 是一个泛型接口,它只接受相同的类型。

您可以使用转换方法:

Comparable<Double> n1 = convertNumber(new Integer(432));
Comparable<Double> n2 = convertNumber(new Long(40));
Comparable<Double> n3 = convertNumber(new Double(500.12));

System.out.println(n1.compareTo(n2));

...

public Double convertNumber(Object o) {
    if(o instanceof Double) {
        return (Double)o;
    } else if(o instanceof Long) {
        return ((Long)o).doubleValue();
    } else if(o instanceof Integer) {
        return ((Integer)o).doubleValue();
    }
    return null;
}

【讨论】:

    【解决方案3】:

    正如人们所提到的,您不能“按原样”比较它们,但您可以实现一个新的比较器,它将接收不同的可能值并知道如何处理它们。下面是一个可能的实现,其中您有不同的构造函数(每个可能的值 - 可能可以改进该部分)将给定值转换为“基本”类型(在我的情况下为双精度) - compareTo 方法可以处理所有支持的类型由不同的构造函数。

    public class MyTestClass
    {
        public static void main (String[] args) throws java.lang.Exception
        {
            MyComparator n1 = new MyComparator(new Integer(432));
            MyComparator n2 = new MyComparator(new Long(40));
            MyComparator n3 = new MyComparator(new Double(500.12));
        }
    
    static class MyComparator implements Comparable
    {
        double baseVal = 0.0d;
    
        public double getValue() {
            return baseVal;
        }
    
        public MyComparator(double d) {
            this.baseVal = d;
        }
    
        public MyComparator(Long l) {
            this.baseVal = l.doubleValue();
        }
    
        public MyComparator(int i) {
            this.baseVal = (double)i;
        }
    
        public int compareTo(MyComparator toCompare) {
            return ((Double)this.getValue()).compareTo(toCompare.getValue());
        }
    
        public int compareTo(Object o) {
            // implemented only to support required interface methods
            return 0;
        }
    }
    }
    

    【讨论】:

    • 我得去看看。如果BigDecimal 的BigInteger 的值大于Double.MAX_VALUE,我认为结果将是不确定的。
    猜你喜欢
    • 1970-01-01
    • 2012-10-10
    • 1970-01-01
    • 2011-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多