高效的排序功能

时间:2015-03-10 22:39:59

标签: java performance sorting

对于我正在进行的Java项目,我需要创建一个sort-by方法,该方法使用映射函数对列表进行排序。最明显的解决方案是使用内置的Collections.sort()方法:

static <D, R extends Comparable> void sortBy(List<D> list, Function<D, R> function) {
    Collections.sort(list, new Comparator<D>() {
        @Override
        public int compare(D d1, D d2) {
            return function.apply(d1).compareTo(function.apply(d2));
        }
    });
}

问题是这会多次调用每个元素上的函数(我认为大约2 log N)。此外,该函数可能很慢,每次调用至少需要几毫秒,可能更长。我想要一种更有效的算法,尽可能少地调用该函数。

我考虑过在开始时应用每个功能,然后对映射列表进行排序,但我不知道如何回到原始列表:

static <D, R extends Comparable> void sortBy(List<D> list, Function<D, R> function) {
    List<R> newList = new ArrayList<>();
    for (D d : list){
        newList.add(function.apply(d));
    }
    Collections.sort(newList);

    // now what?
}

(注意,该函数是纯函数,即每个输入产生相同的输出,没有副作用。)

3 个答案:

答案 0 :(得分:1)

不是简单地包含结果的R,而是实现它,使它成为一个复合对象,同时包含对应D的引用。然后,您可以按R排序,并从排序列表中的每个D元素中提取R

答案 1 :(得分:1)

您可以使用新的Java 8 Map#computeIfAbsent(...)方法实现简单的memoization:

  static <D, R extends Comparable<? super R>> void sortBy(List<D> list, Function<D, R> function) {
    Map<D, R> memo = new HashMap<>();
    Collections.sort(list, new Comparator<D>() {
      @Override
      public int compare(D d1, D d2) {
        R r1 = memo.computeIfAbsent(d1, function);
        R r2 = memo.computeIfAbsent(d2, function);
        return r1.compareTo(r2);
      }
    });
  }

答案 2 :(得分:1)

正如评论中提出的那样,您可以对包含原始值和计算值的某种元组的列表进行排序。然后,通过按排序顺序提取原始值来构建新列表。 此解决方案创建临时对象(元组),但如果映射函数很昂贵,则应该是有效的。当然,这需要衡量......

static <D, R extends Comparable> List<D> sortBy(List<D> list, Function<D, R> function) {
    // Build the list of pairs
    List<Pair<D,R>> newList = list.stream()
            .map(d -> new Pair<>(d, function.apply(d)))
            .collect(Collectors.toList());

    // Sort the list of pairs on second member, which is the computed one
    Collections.sort(newList, new Comparator<Pair<D,R>>() {
        @Override
        public int compare(Pair<D, R> p1, Pair<D, R> p2) {
            return p1.second.compareTo(p2.second);
        }
    });

    // extract the first member of pair, which is the original value 
    return newList.stream().map(p -> p.first).collect(Collectors.toList());
}

给出一个简单的类Pair<U, V>,如:

public final class Pair<U,V> {
   public final U first;
   public final V second;
   public Pair(U u, V v) {
      this.first = u;
      this.second = v;
   }
   public String toString() {
      return "["+first+","+second+"]";
   }
}

然后:

List<String> data = Arrays.asList("blah", "foo", "bar", "hello world", "bye bye", "fizz", "buzz");

List<String> sortedDataByLength = sortBy(data, new Function<String, Integer>() {
    @Override
    public Integer apply(String t) {
        return t.length();
    }});
System.out.println(sortedDataByLength);

收率:

[foo, bar, blah, fizz, buzz, bye bye, hello world]