对列表<number> </number>进行排序

时间:2010-11-16 06:57:41

标签: java list sorting collections

如何对List<Number>进行排序?

示例:

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

5 个答案:

答案 0 :(得分:11)

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

请查看Andreas_D's answer以获取解释。在上面的代码中,处理所有空值和+无穷大值,使它们移动到最后。

更新1:

由于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);

    }
});

更新2:

使用guava's constrained list不允许输入null或不支持的类型列表):

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

答案 1 :(得分:4)

正如jarnbjohis answer指出的那样,无法正确实现Comparator<Number>,因为Number的实例可能很好地代表大于{{1}的数字(不幸的是,就Double.MAX_VALUE接口allows us to "see"而言。大于Number的{​​{1}}的示例是

Number

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

  • Double.MAX_VALUE s,new BigDecimal("" + Double.MAX_VALUE).multiply(BigDecimal.TEN) s,Byte s,Short s,Integer s和Long s

  • 任意大Float s

  • 任意大Double s

  • BigIntegerBigDecimal

    的实例

    请注意,即使BigDecimal.doubleValue可能会返回{Double, Float}.NEGATIVE_INFINITY{Double, Float}.POSITIVE_INFINITY

  • ,这些也应始终位于BigDecimal之前/之后
  • Double.NEGATIVE_INFINITY元素

  • 上述所有内容的混合,

  • Double.POSITIVE_INFINITY的未知实现也实现了null

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

Comparable

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

Number

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

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

答案 2 :(得分:2)

您需要null值的解决方案,因为它们可能位于集合中 - 您无法创建不带null的对象集合。

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

另一个想法是将null转换为某种数字。通过按惯例将任何null转换为Double.NaN,我已经展示了这种方法(基于您自己的答案中的自己的解决方案)。如果您希望将0值排序到远端,也可以考虑将它们转换为Double.POSITIVE_INFINITYDouble.NEGATIVE_INFINITYnull

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

更多信息

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

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

结果(没有添加null)是:

-Infinity
0.0
Infinity
NaN

答案 3 :(得分:2)

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

答案 4 :(得分:0)

尝试我的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());
        }
    }
}