Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 比较方法违反了它的一般约定——但我可能想这样做?_Java_Comparator_Comparable_Compareto - Fatal编程技术网

Java 比较方法违反了它的一般约定——但我可能想这样做?

Java 比较方法违反了它的一般约定——但我可能想这样做?,java,comparator,comparable,compareto,Java,Comparator,Comparable,Compareto,我正在尝试编写一个表排序器,它总是将空值排序到底部。因此,我编写了一个“包装器”类,它实现了可比较的: public class WrappedBigDecimal implements Comparable<WrappedBigDecimal> { public final BigDecimal value; public WrappedBigDecimal(BigDecimal value) { this.value = value; }

我正在尝试编写一个表排序器,它总是将空值排序到底部。因此,我编写了一个“包装器”类,它
实现了可比较的

public class WrappedBigDecimal implements Comparable<WrappedBigDecimal> {
    public final BigDecimal value;

    public WrappedBigDecimal(BigDecimal value) {
        this.value = value;
    }

    @Override
    public int compareTo(WrappedBigDecimal o) {
        if (value == null && (o == null || o.value == null)) { // both are null, thus equal
            return 0;
        } else if (value == null && (o != null && o.value != null)) { // value is null and compared value isn't
            return -1;
        } else if (value != null && (o == null || o.value == null)) {
            return 1;
        } else {
            return value.compareTo(o.value);
        }
    }

    @Override
    public String toString() {
        return String.valueOf(value);
    }

}
但这会抛出一个错误(无法找出原因,无法复制--请继续阅读):

幸运的是,这个错误似乎没有影响任何事情,因为一切都按预期进行

我看到了这个问题(),我想我的问题是我的比较不是传递的。虽然我不确定,因为如果
A
=
B
B
=
C
,那么我想我的也会返回
A
=
C
,但我正试着去想它

无论如何,我的问题是:

  • 以这种方式故意使行分拣机分拣为空到底部是否存在潜在危险
  • 这对我来说似乎是过渡性的。我真的违反了
    compareTo
    的可传递契约吗?或者是否有另一个合同,它必须遵守,而我不知道,这就是抛出错误的原因
  • 我编写了一个方法来测试我的行分类器,但我无法重现错误:

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JTable table = new JTable();
        JScrollPane jsp = new JScrollPane(table);
    
        String[] headers = new String[] {"h1", "h2"};
    
        Object[][] data = new Object[10][2];
        for(int i = 0; i < 7; i++) {
            data[i][0] = new WrappedBigDecimal(BigDecimal.TEN.multiply(BigDecimal.valueOf(i % 3 == 0 ? (i*i*-1) : (i*i))));
        }
    
        data[7][0] = new WrappedBigDecimal(null);
        data[8][0] = new WrappedBigDecimal(null);
        data[9][0] = new WrappedBigDecimal(null);
    
        for(int i = 10; i < 17; i++) {
            data[i-10][1] = new WrappedBigDecimal(BigDecimal.TEN.multiply(BigDecimal.valueOf(i % 3 == 0 ? (i*i*-1) : (i*i))));
        }
    
        data[7][1] = new WrappedBigDecimal(null);
        data[8][1] = new WrappedBigDecimal(null);
        data[9][1] = new WrappedBigDecimal(null);
    
        table.setModel(new DefaultTableModel(data, headers) {
            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }
    
            @Override
            public Class<?> getColumnClass(int columnIndex) {
                return BigDecimal.class;
            }
        });
    
        table.setRowSorter(new WrappedNumberSorter(table.getModel()));
    
        frame.add(jsp);
        frame.setSize(200,400);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    并对其进行了以下测试:

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JTable table = new JTable();
        JScrollPane jsp = new JScrollPane(table);
    
        String[] headers = new String[] {"h1", "h2"};
    
        Object[][] data = new Object[10][2];
        for(int i = 0; i < 7; i++) {
            data[i][0] = BigDecimal.TEN.multiply(BigDecimal.valueOf(i % 3 == 0 ? (i*i*-1) : (i*i)));
        }
    
        data[7][0] = null;
        data[8][0] = null;
        data[9][0] = null;
    
        for(int i = 10; i < 17; i++) {
            data[i-10][1] = BigDecimal.TEN.multiply(BigDecimal.valueOf(i % 3 == 0 ? (i*i*-1) : (i*i)));
        }
    
        data[7][1] = null;
        data[8][1] = null;
        data[9][1] = null;
    
    
    
        table.setModel(new DefaultTableModel(data, headers) {
            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }
    
            @Override
            public Class<?> getColumnClass(int columnIndex) {
                return BigDecimal.class;
            }
        });
    
        table.setRowSorter(new NullsLastSorter(table.getModel()));
    
        frame.add(jsp);
        frame.setSize(200,400);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    

    您可以删除包装器类。在排序方法中,检查每个对象的值并进行相应的排序。例如,您希望将所有空实例放在底部。如果其中一个或两个值都为null,则将它们视为-1的值

    i、 (e)

    @覆盖
    公共比较器getComparator(最后一个int列){
    返回新的比较器(){
    @凌驾
    公共整数比较(对象o1、对象o2){
    如果(o1==null | | o2==null)
    返回-1;
    /**
    *其余的支票都在这里付账
    */
    返回o1。与(o2)相比;
    }
    };
    }
    
    此外,看起来您希望在BigDecimal可比较对象中比较“非BigDecimal”对象

    A) 如果BigDecimal已经实现了Comparable,则无需重新实现接口。只需重写超类中的现有方法


    B) WrappedBigDecimal是泛型类型,您的代码似乎正在接受其他非此类型的类型,这在这里可能并不重要,但肯定会导致错误

    您可以摆脱包装器类。在排序方法中,检查每个对象的值并进行相应的排序。例如,您希望将所有空实例放在底部。如果其中一个或两个值都为null,则将它们视为-1的值

    i、 (e)

    @覆盖
    公共比较器getComparator(最后一个int列){
    返回新的比较器(){
    @凌驾
    公共整数比较(对象o1、对象o2){
    如果(o1==null | | o2==null)
    返回-1;
    /**
    *其余的支票都在这里付账
    */
    返回o1。与(o2)相比;
    }
    };
    }
    
    此外,看起来您希望在BigDecimal可比较对象中比较“非BigDecimal”对象

    A) 如果BigDecimal已经实现了Comparable,则无需重新实现接口。只需重写超类中的现有方法


    B) WrappedBigDecimal是泛型类型,您的代码似乎正在接受非此类型的其他类型,这在这里可能不重要,但肯定会导致错误

    违反合同 我看到两项合同被违反:

    • WrappedBigDecimal.compareTo(null)
    表示
    compareTo(null)
    必须始终抛出
    NullPointerException
    。您的方法不符合此要求

    • WrappedNumberSorter.getComparator()
      处理
      WrappedBigDecimal(null)
    考虑以下代码:

    WrappedBigDecimal x = new WrappedBigDecimal(null);
    WrappedBigDecimal y = new WrappedBigDecimal(null);
    
    。。。连同以下引述:

    实施者必须确保所有
    x
    y
    sgn(比较(x,y))==-sgn(比较(y,x))

    使用您的代码(假设
    升序==true
    ),我们有
    比较(x,y)==1
    。但是
    比较(y,x)==1
    。自
    sgn(1)!=-sgn(1)
    ,这违反了
    比较器接口的合同

    一般性意见 将空值排序到开头或结尾的总体目标很好,事实上这是非常常见的。但是您错过了很多使用库代码的机会

    比较器辅助方法 在Java中,在
    Comparator
    类中使用静态助手方法几乎总是比手工编写自己的
    compare()
    方法更容易。事实上,有一个专门为您的用例定制的:

    不要编写自己的包装器类来处理
    BigDecimal
    null
    值,只需使用
    Optional
    。这正是它的用意

    完全解 由于
    Optional
    是一个非常通用的容器类,它无法实现
    compariable
    本身(无法保证它包装的类型是
    compariable
    )。但是
    Comparator
    的静态助手方法使我们自己做起来很容易

    首先,我们根据是否存在值进行排序,确保空选项位于末尾而不是开头

    然后,当我们有一对“存在”相等的可选项(要么都存在,要么都不存在)时,我们给出一个简单的lambda来提取要比较的值
    orElse()
    正是我们所需要的:如果存在一个值,则获取它并进行比较;如果值不存在,请提供回退。(记住,最后一种情况只有在两个被比较的选项都为空时才会发生,所以我们使用什么值作为回退并不重要,它总是与自身进行比较,从而得到我们想要的结果:两个空选项是等价的。)

    Comparator.comparating(
    
    static class NullsLastSorter extends TableRowSorter<TableModel> {
    
        public NullsLastSorter(TableModel model) {
            super(model);
        }
    
        @Override
        public Comparator<?> getComparator(int column) {
            return Comparator.<Optional<BigDecimal>, Boolean>comparing(Optional::isPresent).reversed().thenComparing(o -> o.orElse(BigDecimal.ONE));
        }
    }
    
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JTable table = new JTable();
        JScrollPane jsp = new JScrollPane(table);
    
        String[] headers = new String[] {"h1", "h2"};
    
        Object[][] data = new Object[10][2];
        for(int i = 0; i < 7; i++) {
            data[i][0] = BigDecimal.TEN.multiply(BigDecimal.valueOf(i % 3 == 0 ? (i*i*-1) : (i*i)));
        }
    
        data[7][0] = null;
        data[8][0] = null;
        data[9][0] = null;
    
        for(int i = 10; i < 17; i++) {
            data[i-10][1] = BigDecimal.TEN.multiply(BigDecimal.valueOf(i % 3 == 0 ? (i*i*-1) : (i*i)));
        }
    
        data[7][1] = null;
        data[8][1] = null;
        data[9][1] = null;
    
    
    
        table.setModel(new DefaultTableModel(data, headers) {
            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }
    
            @Override
            public Class<?> getColumnClass(int columnIndex) {
                return BigDecimal.class;
            }
        });
    
        table.setRowSorter(new NullsLastSorter(table.getModel()));
    
        frame.add(jsp);
        frame.setSize(200,400);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: java.math.BigDecimal cannot be cast to java.util.Optional
    at java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469)
    at java.util.Collections$ReverseComparator2.compare(Collections.java:5178)
    at java.util.Comparator.lambda$thenComparing$36697e65$1(Comparator.java:216)
    at javax.swing.DefaultRowSorter.compare(DefaultRowSorter.java:968)
    at javax.swing.DefaultRowSorter.access$100(DefaultRowSorter.java:112)
    at javax.swing.DefaultRowSorter$Row.compareTo(DefaultRowSorter.java:1376)
    at javax.swing.DefaultRowSorter$Row.compareTo(DefaultRowSorter.java:1366)
    at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
    at java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
    at java.util.Arrays.sort(Arrays.java:1246)
    at javax.swing.DefaultRowSorter.sort(DefaultRowSorter.java:607)
    at javax.swing.DefaultRowSorter.setSortKeys(DefaultRowSorter.java:319)
    at javax.swing.DefaultRowSorter.toggleSortOrder(DefaultRowSorter.java:480)
    at javax.swing.plaf.basic.BasicTableHeaderUI$MouseInputHandler.mouseClicked(BasicTableHeaderUI.java:112)
    at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:270)
    at java.awt.Component.processMouseEvent(Component.java:6536)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
    at java.awt.Component.processEvent(Component.java:6298)
    at java.awt.Container.processEvent(Container.java:2236)
    at java.awt.Component.dispatchEventImpl(Component.java:4889)
    at java.awt.Container.dispatchEventImpl(Container.java:2294)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4534)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
    at java.awt.Container.dispatchEventImpl(Container.java:2280)
    at java.awt.Window.dispatchEventImpl(Window.java:2746)
    at java.awt.Component.dispatchEvent(Component.java:4711)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
    at java.awt.EventQueue$4.run(EventQueue.java:731)
    at java.awt.EventQueue$4.run(EventQueue.java:729)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
    
    @Override
    public Comparator<?> getComparator(final int column) {
        return new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 == null || o2 == null)
                    return -1;
                /**
                 * Do the rest of your checks here
                 */
                return o1.compareTo(o2);
            }
        };
    }
    
    WrappedBigDecimal x = new WrappedBigDecimal(null);
    WrappedBigDecimal y = new WrappedBigDecimal(null);
    
    Comparator.<Optional<BigDecimal>,Boolean>comparing(Optional::isPresent)
              .reversed() //default ordering of booleans is false before true
              .thenComparing(o -> o.orElse(BigDecimal.ONE))