使用Java 8流对多个变量列表进行排序

时间:2015-06-03 15:34:54

标签: java sorting java-8 java-stream

我有一个包含多个变量的对象列表。我想根据运行时决定的序列对此列表进行排序。我提出了以下代码,看起来效率不高。例如,如果变量不是3但是10或30,会发生什么?任何人都可以提出更好的方法吗?

import java.util.List;
import java.util.function.Function;
import static java.util.Comparator.*;
import static java.util.stream.Collectors.*;

public class SortXYZ {
    double x;
    String y;
    double z;

public double getX() {return x; }
public String getY() {return y; }
public double getZ() {return z; }

 public static void main(String[] arg)   {
    Function<SortXYZ, Double> f1 = SortXYZ::getX;
    Function<SortXYZ, String> f2 = SortXYZ::getY;
    Function<SortXYZ, Double> f3 = SortXYZ::getZ;
    //could have f4, f5 etc.

    List<SortXYZ> list = getList(); //get a list of SortXYZ objects 

    String sortSeq = arg[0];
    if (sortSeq.equals("XYZ")) {
        list = list.stream().sorted(comparing(f1).thenComparing(f2).thenComparing(f3)).collect(toList());
    } else if (sortSeq.equals("XZY")) {
        list = list.stream().sorted(comparing(f1).thenComparing(f3).thenComparing(f2)).collect(toList());
    } else if (sortSeq.equals("YXZ")) {
        list = list.stream().sorted(comparing(f2).thenComparing(f1).thenComparing(f3)).collect(toList());
    } else if (sortSeq.equals("YZX")) {
        list = list.stream().sorted(comparing(f2).thenComparing(f3).thenComparing(f1)).collect(toList());
    } else if (sortSeq.equals("ZXY")) {
        list = list.stream().sorted(comparing(f3).thenComparing(f1).thenComparing(f2)).collect(toList());
    } else if (sortSeq.equals("ZYX")) {
        list = list.stream().sorted(comparing(f3).thenComparing(f2).thenComparing(f1)).collect(toList());
    } 


}
 }

2 个答案:

答案 0 :(得分:4)

我暂时忽略了Comparator中的原型。稍后会尝试提高类型安全性。

您可以创建接受比较器模式的方法,并在此基础上创建一个比较器。为了让您的生活更轻松,您还可以将吸气剂参考(f1 f2 ..)存储在地图中,例如

@SuppressWarnings("rawtypes") 
static Map<String, Function<SortXYZ, Comparable>> gettersMap = new HashMap<>();
static {
    gettersMap.put("x", SortXYZ::getX);
    gettersMap.put("y", SortXYZ::getY);
    gettersMap.put("z", SortXYZ::getZ);
}

所以创建比较器的方法看起来像

@SuppressWarnings("unchecked")
static Comparator<SortXYZ> getComparator(String order){
    String[] keys = order.toLowerCase().split("");
    Comparator<SortXYZ> result = Comparator.comparing(gettersMap.get(keys[0]));
    for (int i = 1; i<keys.length; i++){
        result = result.thenComparing(gettersMap.get(keys[i]));
    }
    return result;
}

BTW:在您的代码中:

list = list.stream().sorted(Comparator.comparing(f3).thenComparing(f2).thenComparing(f1)).collect(toList());

您正在排序列表,并将元素写入新列表,然后再将其分配给list引用。如果要对当前列表中的元素进行排序,只需使用

即可
list.sort(yourComparator);

所以你的代码现在看起来更像是:

List<SortXYZ> list = getList(); //get a list of SortXYZ objects 

String sortSeq = arg[0];
list.sort(getComparator(sortSeq));

答案 1 :(得分:2)

您可以使用循环迭代字符并创建比较器 - 请参阅下面的代码。

下面解决方案中的丑陋之处在于comparator必须是#34;有效的最终&#34;,但是因为我们重新分配它需要是一个字段。更多功能解决方案是将字符流转换为函数流,然后将流减少为单个比较器,但它可能需要额外的类和基本情况的特殊处理,因此可能比下面的解决方案更多的锅炉板。

import java.util.Comparator;
import java.util.List;
import java.util.function.Function;

import static java.util.Arrays.asList;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;

public class SortXYZ {

    private final double x;
    private final String y;
    private final double z;

    public SortXYZ(double x, String y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public double getX() {
        return x;
    }

    public String getY() {
        return y;
    }

    public double getZ() {
        return z;
    }

    @Override
    public String toString() {
        return "SortXYZ{x=" + x + ", y=" + y + ", z=" + z + "}";
    }

    private static Comparator<SortXYZ> comparator;

    public static void main(String[] arg) {
        List<SortXYZ> list = asList(new SortXYZ(1, "A", 2), new SortXYZ(1, "A", 0), new SortXYZ(2, "B", 1));

        String sortSeq = "YXZ";

        comparator = comparing(function(sortSeq.charAt(0)));
        sortSeq.chars().skip(1).forEach(c -> comparator = comparator.thenComparing(function((char) c)));

        List<SortXYZ> sortedList = list.stream().sorted(comparator).collect(toList());

        System.out.println(sortedList);
    }

    public static Function<SortXYZ, ? extends Comparable> function(char c) {
        switch (c) {
            case 'X': return SortXYZ::getX;
            case 'Y': return SortXYZ::getY;
            case 'Z': return SortXYZ::getZ;
            default:  throw new IllegalArgumentException("" + c);
        }
    }

}