【问题标题】:Sort multiple variable list with Java 8 stream使用 Java 8 流对多个变量列表进行排序
【发布时间】:2015-06-03 15:34:53
【问题描述】:

我有一个包含多个变量的对象列表。我想根据运行时决定的顺序对这个列表进行排序。我提出了以下看起来根本没有效率的代码。例如,如果变量不是 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());
    } 


}
 }

【问题讨论】:

    标签: java sorting java-8 java-stream


    【解决方案1】:

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


    您可以创建将接受比较器模式的方法,并基于它创建一个比较器。为了让生活更轻松,您还可以将您的 getter 引用 (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;
    }
    

    顺便说一句:在您的代码中:

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

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • 对此进行了测试,它可以工作。将其应用于我的现实世界问题,看看会发生什么。非常感谢!
      猜你喜欢
      • 1970-01-01
      • 2018-02-12
      • 2018-10-03
      • 2011-12-05
      • 1970-01-01
      • 1970-01-01
      • 2016-10-20
      • 1970-01-01
      • 2017-07-30
      相关资源
      最近更新 更多