在未知值之前对具有已知值的列表进行排序

时间:2018-07-18 02:05:54

标签: java guava java-7 comparator

我正在尝试使用以下规则对列表进行排序:

  1. 已知值应先于未知值排序。
  2. 已知值应通过单独定义的键排序。
  3. 未知值应按其自然顺序进行排序。

我有(1)和(2),只是想在混合中添加(3)。

到目前为止,我有这个:

List<String> values = Arrays.asList(
    "red", "orange", "yellow", "green", "blue", "indigo", "violet");

ImmutableMap<String, Integer> map = ImmutableMap.of("red", 1, "green", 2, "blue", 3);

Ordering<String> order = Ordering.natural()
    .nullsLast()
    .onResultOf(Functions.forMap(map, null));

Collections.sort(values, order);

System.out.println(values);

哪个会产生:

[red, green, blue, orange, yellow, indigo, violet]

但是最后4个按其原始顺序排列,而我希望它们按其自然顺序排序:

[red, green, blue, indigo, orange, violet, yellow]

我唯一想到的就是编写自己的自定义函数,该函数会在地图中查找内容并将地图结果添加到原始值,如果找不到地图,则使用地图大小-例如它会返回:

"1-red", "4-orange", "4-yellow", "2-green", "3-blue", "4-indigo", "4-violet"

但这仅在映射值是整数且要求数字格式将“ 02”放在“ 10”之前的情况下有效。

有人有更好的方法实现这一目标吗?

2 个答案:

答案 0 :(得分:4)

这是Guava版本(当您使用Java 7或更低版​​本时):

Ordering<String> ordering = Ordering.natural().nullsLast()
        .onResultOf(Functions.forMap(map, null))
        .compound(Ordering.natural());

这是使用纯Comparator的非番石榴版本(在JDK 8+上):

Comparator<String> comparator = Comparator
        .<String, Integer>comparing(map::get, Comparator.nullsLast(Comparator.naturalOrder()))
        .thenComparing(Comparator.naturalOrder());

PS。如您所见,使用Guava API的类型推断更好(不需要指定显式类型参数)。

答案 1 :(得分:0)

我认为该解决方案应该可以解决您的问题。我为使用ArrayList而致歉-我在Java 8 API中找不到与ImmutableMap相对应的任何文档。我还假定 natural 顺序表示ASCII顺序。希望这会有所帮助:)

    //Values to be sorted
    ArrayList<Object> values = new ArrayList<>();

    values.add("red");
    values.add("orange");
    values.add("orange");
    values.add("yellow");
    values.add("green");
    values.add("blue");
    values.add("indigo");
    values.add("violet");
    values.add(1);    //I added 1 to verify output

    //List containing order of known values
    ArrayList<Object> knownOrder = new ArrayList<>();

    knownOrder.add("red");
    knownOrder.add(1);
    knownOrder.add("green");
    knownOrder.add("blue");
    knownOrder.add(3);

    ArrayList<Object> knownValues = new ArrayList<>();
    ArrayList<Object> unknownValues = new ArrayList<>();

    for (Object value: values) {
        if (knownOrder.contains(value)) {
            knownValues.add(value);
        }else {
            unknownValues.add(value);
        }
    }

    //Sort known values in required order
    Collections.sort(knownValues, (m1, m2)->knownOrder.indexOf(m1)-knownOrder.indexOf(m2));

    //Sort unknown values in natural order
    Collections.sort(unknownValues, (m1, m2)->m1.toString().charAt(0)-m2.toString().charAt(0));

    //Combine the known and unknown values into one ArrayList
    knownValues.addAll(unknownValues);

    System.out.println(knownValues);
    //Prints [red, 1, green, blue, indigo, orange, orange, violet, yellow]

我还应该提到:该解决方案不适用于String和原始Wrappers以外的对象。