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

Sort a list with known values before unknown values

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

  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]

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

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

但这仅在映射值是整数时才有效,并且需要数字格式以在“10”之前排序“02”等。

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

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

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

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

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

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

我认为此解决方案应该可以解决您的问题。对于使用 ArrayLists,我深表歉意——我在 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]

我还应该提到:此解决方案不适用于字符串和原始包装器以外的对象。