【问题标题】:List Sorting puzzle列表排序谜题
【发布时间】:2010-06-24 13:18:29
【问题描述】:

假设我有

final Iterable<String> unsorted = asList("FOO", "BAR", "PREFA", "ZOO", "PREFZ", "PREFOO");

我该怎么做才能把这个未排序的列表变成这样:

[PREFZ, PREFA, BAR, FOO, PREFOO, ZOO]

(以必须首先出现的已知值开头的列表(此处为“PREFA”和“PREFZ”),其余按字母顺序排序)

我认为番石榴中有一些有用的类可以完成这项工作(排序,谓词......),但我还没有找到解决方案......

【问题讨论】:

  • 我必须接受这种练习的解决方案吗?

标签: java list sorting guava


【解决方案1】:

我会保留单独的列表。

一个用于已知值和未知值。并分别对它们进行排序,当您需要将它们放在一个列表中时,您可以将它们连接起来。

knownUnsorted.addAll(unsorted.size - 1, unknonwUnsorted);

【讨论】:

  • 我觉得还可以,但是要在entry中分开list。
【解决方案2】:

我建议用您的值填写List 并使用Collections.sort(...)

类似

Collections.sort(myList, new FunkyComparator());

使用这个:

class FunkyComparator implements Comparator {

    private static Map<String,Integer> orderedExceptions =
        new HashMap<String,Integer>(){{ 
            put("PREFZ", Integer.valueOf(1));
            put("PREFA", Integer.valueOf(2));
        }};

    public int compare(Object o1, Object o2) {
        String s1 = (String) o1;
        String s2 = (String) o2;
        Integer i1 = orderedExceptions.get(s1);
        Integer i2 = orderedExceptions.get(s2);

        if (i1 != null && i2 != null) {
            return i1 - i2;
        }
        if (i1 != null) {
            return -1;
        }
        if (i2 != null) {
            return +1;
        }
        return s1.compareTo(s2);
    }
}

【讨论】:

  • 不错,但是 Set 会杀死重复项。此外,不需要虚拟 equals()
  • 嗯,如果我有两个以上的已知值,我该如何转换它?
  • @unbeli 两个优点。由于从 SortedSet 切换到 List 不会改变我的答案的要点,我已经更新了 List 的 Comparator 的“用法”。
  • @Sylvain_M,只需在orderedExceptions 映射中添加所需数量的“已知值”即可。确保使用的整数是唯一的并且按照您的需要进行排序 - 地图可以保持快速查找,但整数决定了最终的排序。
  • 哦,是的,所以维护起来并不难。谢谢
【解决方案3】:

注意:这不是最有效的解决方案。这只是一个简单、直接的解决方案,可以完成工作。

我会先使用Collections.sort(list) 对列表进行排序。

然后,我会删除已知的项目,并将它们添加到前面。

String special = "PREFA";
if (list.remove(special)
    list.add(0, special);

或者,如果您在前面有一个包含这些值的数组列表,您可以这样做:

String[] knownValues = {};
for (String s: knownValues) {
    if (list.remove(s))
        list.add(0, s);
}

【讨论】:

  • list.remove() 返回一个布尔值,而不是您删除的对象。但我认为它可以工作: for (final String s : asList("PREFA", "PREFZ")) { if (list.remove(s)) { list.add(0, s); } }
  • @Syl 哎呀,我把这两个删除搞混了。我会解决的,谢谢。
  • 除非你不能对一个 Iterable 进行排序并首先将其转储到一个列表中,否则它会变得丑陋,因为你可以在转储时分离未知数。
  • @unbeli:我同意你的观点,但现在,这似乎是最简单的工作方式
  • @Sylvain 如果您想要最简单的方法,请不要将其标记为“拼图”和“练习”;)
【解决方案4】:

因为我是 guava lib 的粉丝,所以我想找到一个使用它的解决方案。我不知道它是否有效,也不知道它是否像其他解决方案一样简单,但它就在这里:

final Iterable<String> all = asList("FOO", "BAR", "PREFA", "ZOO", "PREFOO", "PREFZ");
final List<String> mustAppearFirst = asList("PREFZ", "PREFA");
final Iterable<String> sorted = 
      concat(
            Ordering.explicit(mustAppearFirst).sortedCopy(filter(all, in(mustAppearFirst))),
            Ordering.<String>natural().sortedCopy(filter(all, not(in(mustAppearFirst)))));

【讨论】:

  • 不错的方法。如果您好奇的话,我的回答中又采用了“guavaey”方法
【解决方案5】:

你特别提到了番石榴;除了 Sylvain M 的回答,这里还有另一种方式(更多的是作为学术练习和展示番石榴的灵活性而不是其他任何东西)

// List is not efficient here; for large problems, something like SkipList 
// is more suitable
private static final List<String> KNOWN_INDEXES = asList("PREFZ", "PREFA");

private static final Function<Object, Integer> POSITION_IN_KNOWN_INDEXES 
    = new Function<Object, Integer>() {
  public Integer apply(Object in) {
     int index = KNOWN_INDEXES.indexOf(in);
     return index == -1 ? null : index;
  }     
};


...


List<String> values = asList("FOO", "BAR", "PREFA", "ZOO", "PREFZ", "PREFOO");

Collections.sort(values,
  Ordering.natural().nullsLast().onResultOf(POSITION_IN_KNOWN_INDEXES).compound(Ordering.natural())
);

因此,换句话说,按照List.indexOf() 返回的Integer 的自然顺序进行排序,然后打破与对象本身的自然顺序的联系。

也许很乱,但很有趣。

【讨论】:

  • 您好 Cowan,我尝试了您的解决方案,但无法编译。当我修改它进行编译时(通过在排序和帮助类型推断上添加 asList() ),我得到了一个 NPE...
  • 对不起 Sylvain,我写的时候不在编译器附近。已经修好了。出于某种原因,我认为Ordering.compound() 是一种静态方法;你也需要nullsLast().onResultOf() 而不是onResultOf().nullsLast() 否则它会以错误的顺序链接事物。现在按原样为我工作。
  • 因为我被建议接受一个答案,所以我接受这个,因为它是我读过的最简洁的。嗯,这是主观的,但这是我的首选答案(简洁+使用番石榴)
【解决方案6】:

我也会使用Collections.sort(list),但我想我会使用比较器,并且在比较器中您可以定义自己的规则,例如

class MyComparator implements Comparator<String> {

    public int compare(String o1, String o2) {
        // Now you can define the behaviour for your sorting.
        // For example your special cases should always come first, 
        // but if it is not a special case then just use the normal string comparison.

        if (o1.equals(SPECIAL_CASE)) {
            // Do something special
        }
        // etc.
        return o1.compareTo(o2);
    }

}

然后进行排序:

Collections.sort(list, new MyComparator());

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-07
    • 2021-06-22
    • 1970-01-01
    • 2015-07-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多