java 对 List<Number> 进行排序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4191855/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 05:14:56  来源:igfitidea点击:

Sorting a List<Number>

javalistsortingcollections

提问by Emil

How to sort a List<Number>?

如何排序List<Number>

Example:

例子:

List<Number> li = new ArrayList<Number>(); //list of numbers
li.add(new Integer(20));
li.add(new Double(12.2));
li.add(new Float(1.2));

回答by Emil

Collections.sort(li,new Comparator<Number>() {
    @Override
    public int compare(Number o1, Number o2) {
        Double d1 = (o1 == null) ? Double.POSITIVE_INFINITY : o1.doubleValue();
        Double d2 = (o2 == null) ? Double.POSITIVE_INFINITY : o2.doubleValue();
        return  d1.compareTo(d2);
    }
});

Have a look at Andreas_D'sanswerfor explanation.In the above code all null values and +Infinity values are handled such that they move to the end.

看看Andreas_D 的解释答案。在上面的代码中,所有空值和 +Infinity 值都被处理,以便它们移动到最后。

Update 1:

更新 1:

As jarnbjoand aioobepoints out a flaw in the above implementation.So I thought it's better to restrict the implementation's of Number.

由于jarnbjoaioobe指出了上述实现中的一个缺陷。所以我认为最好限制 Number 的实现。

Collections.sort(li, new Comparator<Number>() {
    HashSet<Class<? extends Number>> allowedTypes;
    {
        allowedTypes = new HashSet<Class<? extends Number>>();
        allowedTypes.add(Integer.class);
        allowedTypes.add(Double.class);
        allowedTypes.add(Float.class);
        allowedTypes.add(Short.class);
        allowedTypes.add(Byte.class);

    }

    @Override
    public int compare(Number o1, Number o2) {
        Double d1 = (o1 == null) ? Double.POSITIVE_INFINITY : o1.doubleValue();
        Double d2 = (o2 == null) ? Double.POSITIVE_INFINITY : o2.doubleValue();

        if (o1 != null && o2 != null) {
            if (!(allowedTypes.contains(o1.getClass()) && allowedTypes.contains(o2.getClass()))) {
                throw new UnsupportedOperationException("Allowed Types:" + allowedTypes);
            }
        }

        return d1.compareTo(d2);

    }
});

Update 2:

更新 2:

Using guava'sconstrained list(will not allow entry of null or unsupported type to list):

使用guava 的约束列表不允许将 null 或不受支持的类型输入 list):

List<Number> li = Constraints.constrainedList(new ArrayList<Number>(),
    new Constraint<Number>() {
        HashSet<Class<? extends Number>> allowedTypes;
        {
            allowedTypes = new HashSet<Class<? extends Number>>();
            allowedTypes.add(Integer.class);
            allowedTypes.add(Double.class);
            allowedTypes.add(Float.class);
            allowedTypes.add(Short.class);
            allowedTypes.add(Byte.class);

        }

        @Override
        public Number checkElement(Number arg0) {
            if (arg0 != null) {
                if (allowedTypes.contains(arg0.getClass())) {
                    return arg0;
                }
            }

            throw new IllegalArgumentException("Type Not Allowed");
        }
    }
);

li.add(Double.POSITIVE_INFINITY);
li.add(new Integer(20));
li.add(new Double(12.2));
li.add(new Float(1.2));
li.add(Double.NEGATIVE_INFINITY);
li.add(Float.NEGATIVE_INFINITY);
// li.add(null); //throws exception
// li.add(new BigInteger("22"); //throws exception
li.add(new Integer(20));
System.out.println(li);

Collections.sort(li, new Comparator<Number>() {
    @Override
    public int compare(Number o1, Number o2) {
        Double d1 = o1.doubleValue();
        Double d2 = o2.doubleValue();

        return d1.compareTo(d2);
    }
});

System.out.println(li);

回答by aioobe

As jarnbjopoints out in his answer, there is no way to implement a Comparator<Number>correctly, as instances of Numbermay very well represent numbers larger than Double.MAX_VALUE(and that's unfortunately as far as the Numberinterface allows us to "see"). An example of a Numberlarger than Double.MAX_VALUEis

正如jarnbjo他的回答中指出的那样,没有办法Comparator<Number>正确实现 a ,因为 的实例Number很可能表示大于的数字Double.MAX_VALUE(不幸的是,就Number接口允许我们“看到”而言,这是)。一个Number大于的例子Double.MAX_VALUE

new BigDecimal("" + Double.MAX_VALUE).multiply(BigDecimal.TEN)

The solution below however, handles

然而,下面的解决方案处理

  • Bytes, Shorts, Integers, Longs, Floats and Doubles

  • Arbitrary large BigIntegers

  • Arbitrary large BigDecimals

  • Instances of {Double, Float}.NEGATIVE_INFINITYand {Double, Float}.POSITIVE_INFINITY

    Note that these should always come before/after any BigDecimaleven though the BigDecimal.doubleValuemay return Double.NEGATIVE_INFINITYor Double.POSITIVE_INFINITY

  • nullelements

  • A mixture of all of the above, and

  • Unknown implementations of Numberthat also implements Comparable.

    (This seems to be a reasonable assumption since all Numbers in the standard API implements Comparable.)

  • Bytes, Shorts, Integers, Longs, Floats 和Doubles

  • 任意大BigIntegers

  • 任意大BigDecimals

  • {Double, Float}.NEGATIVE_INFINITY和的实例{Double, Float}.POSITIVE_INFINITY

    请注意,BigDecimal即使BigDecimal.doubleValue可能返回Double.NEGATIVE_INFINITYDouble.POSITIVE_INFINITY

  • null元素

  • 以上所有的混合,

  • 的未知实现Number也实现了Comparable.

    (这似乎是一个合理的假设,因为Number标准 API 中的所有s都实现了 Comparable。)

 

 

@SuppressWarnings("unchecked")
class NumberComparator implements Comparator<Number> {

    // Special values that are treated as larger than any other.
    private final static List<?> special =
            Arrays.asList(Double.NaN, Float.NaN, null);

    private final static List<?> largest =
            Arrays.asList(Double.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);

    private final static List<?> smallest =
            Arrays.asList(Double.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);

    public int compare(Number n1, Number n2) {

        // Handle special cases (including null)
        if (special.contains(n1)) return  1;
        if (special.contains(n2)) return -1;

        if (largest.contains(n1) || smallest.contains(n2)) return  1;
        if (largest.contains(n2) || smallest.contains(n1)) return -1;

        // Promote known values (Byte, Integer, Long, Float, Double and
        // BigInteger) to BigDecimal, as this is the most generic known type.
        BigDecimal bd1 = asBigDecimal(n1);
        BigDecimal bd2 = asBigDecimal(n2);
        if (bd1 != null && bd2 != null)
            return bd1.compareTo(bd2);

        // Handle arbitrary Number-comparisons if o1 and o2 are of same class
        // and implements Comparable.
        if (n1 instanceof Comparable<?> && n2 instanceof Comparable<?>)
            try {
                return ((Comparable) n1).compareTo((Comparable) n2);
            } catch (ClassCastException cce) {
            }

        // If the longValue()s differ between the two numbers, trust these.
        int longCmp = ((Long) n1.longValue()).compareTo(n2.longValue());
        if (longCmp != 0)
            return longCmp;

        // Pray to god that the doubleValue()s differ between the two numbers.
        int doubleCmp = ((Double) n1.doubleValue()).compareTo(n2.doubleValue());
        if (doubleCmp != 0)
            return longCmp;

        // Die a painful death...
        throw new UnsupportedOperationException(
                "Cannot compare " + n1 + " with " + n2);
    }


    // Convert known Numbers to BigDecimal, and the argument n otherwise.
    private BigDecimal asBigDecimal(Number n) {
        if (n instanceof Byte)       return new BigDecimal((Byte) n);
        if (n instanceof Integer)    return new BigDecimal((Integer) n);
        if (n instanceof Short)      return new BigDecimal((Short) n);
        if (n instanceof Long)       return new BigDecimal((Long) n);
        if (n instanceof Float)      return new BigDecimal((Float) n);
        if (n instanceof Double)     return new BigDecimal((Double) n);
        if (n instanceof BigInteger) return new BigDecimal((BigInteger) n);
        if (n instanceof BigDecimal) return (BigDecimal) n;
        return null;
    }
}

Here is a small test program (here is an ideone.com demo):

这是一个小测试程序(这里是ideone.com 演示):

public class Main {

    public static void main(String[] args) {
        List<Number> li = new ArrayList<Number>();

        // Add an Integer, a Double, a Float, a Short, a Byte and a Long.
        li.add(20);         li.add((short) 17);
        li.add(12.2);       li.add((byte) 100);
        li.add(0.2f);       li.add(19518926L);
        li.add(Double.NaN); li.add(Double.NEGATIVE_INFINITY);
        li.add(Float.NaN);  li.add(Double.POSITIVE_INFINITY);

        // A custom Number
        li.add(new BoolNumber(1));
        li.add(new BoolNumber(0));

        // Add two BigDecimal that are larger than Double.MAX_VALUE.
        BigDecimal largeDec = new BigDecimal("" + Double.MAX_VALUE);
        li.add(largeDec/*.multiply(BigDecimal.TEN)*/);
        li.add(largeDec.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN));

        // Add two BigInteger that are larger than Double.MAX_VALUE.
        BigInteger largeInt = largeDec.toBigInteger().add(BigInteger.ONE);
        li.add(largeInt.multiply(BigInteger.TEN));
        li.add(largeInt.multiply(BigInteger.TEN).multiply(BigInteger.TEN));

        // ...and just for fun...
        li.add(null);

        Collections.shuffle(li);
        Collections.sort(li, new NumberComparator());

        for (Number num : li)
            System.out.println(num);
    }

    static class BoolNumber extends Number {
        boolean b;
        public BoolNumber(int i)    { b = i != 0; }
        public double doubleValue() { return b ?  1d :  0d; }
        public float floatValue()   { return b ?  1f :  0f; }
        public int intValue()       { return b ?   1 :   0; }
        public long longValue()     { return b ?  1L :  0L; }
        public String toString()    { return b ? "1" : "0"; }
    }
}

...which prints (I removed a few zeros):

...打印(我删除了几个零):

-Infinity
0
0.2
1
12.2
17
20
100
19518926
1.7976931348623157E+308
17976931348623157000000000...00000000010
1.797693134862315700E+310
179769313486231570000000000000...00000100
Infinity
NaN
null
NaN

回答by Andreas Dolk

You'll need a solution for nullvalues, because they may be in the collection - you can't create a collection of objects that doesn't take null.

您将需要一个null值的解决方案,因为它们可能在集合中 - 您无法创建不带null.

So you could check for nulland throw IllegalArgumentException- with the sideeffect, that you won't be able to sort "polluted" lists and have to handle those exceptions at runtime.

因此,您可以检查null并抛出IllegalArgumentException- 带有副作用,您将无法对“受污染”列表进行排序,并且必须在运行时处理这些异常。

Another idea is to convert a nullto some kind of number. I've shown this approach (based on you own solution from your own answer) by converting any nullto Double.NaNby convention. You could also consider converting them to 0or to Double.POSITIVE_INFINITYor Double.NEGATIVE_INFINITYif you want nullvalues sorted to the far ends.

另一个想法是将 a 转换null为某种数字。我已经通过将 any 转换nullDouble.NaN约定来展示了这种方法(基于您自己的答案中的解决方案)。您还可以考虑将它们转换为0或 ,Double.POSITIVE_INFINITY或者Double.NEGATIVE_INFINITY如果您希望将null值排序到远端。

Collections.sort(li,new Comparator<Number>() {
    @Override
    public int compare(Number o1, Number o2) {

        // null values converted to NaN by convention
        Double d1= (o1 == null) ? Double.NaN : o1.doubleValue();
        Double d2= (o2 == null) ? Double.NaN : o2.doubleValue();

        return  d1.compareTo(d2);
    }
});

Further Information

更多信息

Here's some code that shows how thoses special values are handled by "default":

这是一些代码,显示了“默认”如何处理这些特殊值:

Set<Double> doubles = new TreeSet<Double>();
doubles.add(0.);
// doubles.add(null);   // uncommenting will lead to an exception!
doubles.add(Double.NaN);
doubles.add(Double.POSITIVE_INFINITY);
doubles.add(Double.NEGATIVE_INFINITY);

for (Double d:doubles) System.out.println(d);

The result (with no nulladdded) is:

结果(没有null添加)是:

-Infinity
0.0
Infinity
NaN

回答by jarnbjo

Simple answer: You can't. A proprietary Number implementation may have higher precision or a larger value range than what is available through the getXXX() methods defined for the actual value in the Number interface.

简单的回答:你不能。与通过为 Number 接口中的实际值定义的 getXXX() 方法可用的相比,专有 Number 实现可能具有更高的精度或更大的值范围。

回答by Nathan Nelson

try my java sorting algorithm:

试试我的java排序算法:

package drawFramePackage;

import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.Random;

public class QuicksortAlgorithm {
    ArrayList<AffineTransform> affs;
    ListIterator<AffineTransform> li;
    Integer count, count2;

    /**
     * @param args
     */
    public static void main(String[] args) {
        new QuicksortAlgorithm();
    }

    public QuicksortAlgorithm(){
        count = new Integer(0);
        count2 = new Integer(1);
        affs = new ArrayList<AffineTransform>();

        for (int i = 0; i <= 128; i++) {
            affs.add(new AffineTransform(1, 0, 0, 1, new Random().nextInt(1024), 0));
        }

        affs = arrangeNumbers(affs);
        printNumbers();
    }

    public ArrayList<AffineTransform> arrangeNumbers(ArrayList<AffineTransform> list) {
        while (list.size() > 1 && count != list.size() - 1) {
            if (list.get(count2).getTranslateX() > list.get(count).getTranslateX()) {
                list.add(count, list.get(count2));
                list.remove(count2 + 1);
            }

            if (count2 == list.size() - 1) {
                count++;
                count2 = count + 1;
            } else {
                count2++;
            }
        }
        return list;
    }

    public void printNumbers(){
        li = affs.listIterator();

        while (li.hasNext()) {
            System.out.println(li.next());
        }
    }
}