对于我正在进行的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?
}
(注意,该函数是纯函数,即每个输入产生相同的输出,没有副作用。)
答案 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]