[英]Guava: How to create an explicit Ordering from a List and a single element?
番石榴,给定一个Collection<E>
和元素e
型E
,我知道在集合中,我想创建一个自定义Ordering<E>
的排序e
先收集的其余部分。 然而,到达那里的方式似乎非常复杂:
Collection<String> values = ImmutableList.of("apples", "oranges", "pears");
String first = "oranges";
List<String> remainingValues = newArrayList(values); // this
remainingValues.remove(first); // seems
Ordering<String> myOrdering = // very
Ordering.explicit(first, remainingValues.toArray( // complicated!
new String[remainingValues.size()])); // is there an easier way?
我想要的是这样的事情:
Ordering.explicit(first);
(我想这个排序first
的开头,并保留所有其他元素的顺序,但该文档说,导致订货会抛出一个ClassCastException
未明确列出的元素。)
或者像这样:
Ordering.explicit(first, values.toArray(/* etc */));
(但这会失败,因为first
是重复值)
有人能想出一个简洁的方式来做我想做的事吗?
顺便说一下,它不一定是一个Ordering
,它也可以是在指定的Order中创建Iterable
一种解决方法,但同样,这非常复杂:
Iterable<String> sorted = Iterables.concat(
ImmutableList.of(first),
Iterables.filter(values, not(equalTo(first))));
嗯,这是一种方法,但你可能找不到更好的方法。
final String special = "oranges";
Collections.sort(
list,
new Comparator<String>() {
public int compare(String left, String right) {
return ComparisonChain.start()
.compareTrueFirst(left.equals(special), right.equals(special))
.compare(left, right)
.result();
}
});
相关的番石榴功能请求 - 请添加任何详细信息。
也许这个答案并不比你已经拥有的更简单/更简单,但至少可以重复使用:)
class FirstOrdering<T extends Comparable> extends Ordering<T> {
private T first;
public FirstOrdering(T first) {
this.first = first;
}
@Override
public int compare(@Nullable T left, @Nullable T right) {
// TODO Nullchecks...
if (first.equals(left)) return -1;
if (first.equals(right)) return 1;
return left.compareTo(right);
}
}
final String first = "B";
new FirstOrdering(first).
sortedCopy(Arrays.asList("A", "D", "E", first));
只需使用NullsFirstOrdering作为模板,并创建一个排序第一个元素的排序,委托另一个排序:
public class ItemFirstComparator<T> implements Comparator<T> implements Serializable {
private final Comparator<? super T> comparator;
private final Object item;
ItemFirstComparator(Object item, Comparator<? super T> comparator) {
this.item = item;
comparator = checkNotNull(comparator);
}
@Override public int compare(@Nullable T left, @Nullable T right) {
if (left == right) {
return 0;
}
if (Objects.equals(left, item)) {
return -1;
}
if (Objects.equals(right, item)) {
return 1;
}
return comparator.compare(left, right);
}
}
然后,您可以轻松地链接排序: Ordering.from(new ItemFirstComparator("oranges", Ordering.allEqual()))
。
将代码更改为使用Comparator而不是Ordering,其余的保持不变。
如果您查看com.google.common.collect.ExplicitOrdering的来源,它会维护一个包含每个项目排名的地图,并compare
只是比较排名。 你可以自己做同样的事情,但强制指定的第一项的等级为-1,这是在所有其他项之前。
如果你有一个List(作为问题的标题状态),Java 8流使得构建地图适度方便:
Map<T, Integer> rankMap = IntStream.range(0, list.size()).boxed().collect(
Collectors.toMap(list::get, i -> list.get(i).equals(first) ? -1 : i));
Comparator<T> cmp = Comparator.comparing(rankMap::get);
如果你只有一个Collection(作为问题的正文状态),你需要使用for循环来构建地图:
Map<T, Integer> rankMap = new HashMap<>(coll.size());
int rank = 0;
for (T t : coll)
rankMap.put(t, t.equals(first) ? -1 : rank++);
Comparator<T> cmp = Comparator.comparing(rankMap::get);
像往常一样,您可以将比较器转换为Ordering.from的订购。
如果您有更多特殊值,这将更方便,重复性更低:
class PriorityComparator<T> implements Comparator<T> {
private final List<T> values;
public PriorityComparator(T... values) {
this.values = Arrays.asList(values);
}
@Override public int compare(T o1, T o2) {
int idx1 = values.indexOf(o1);
int idx2 = values.indexOf(o2);
if (idx1 > -1) {
return idx2 > -1 ? idx1 - idx2 : -1;
}
return idx2 > -1 ? 1 : 0;
}
}
您可以在比较链中使用它
return ComparisonChain.start()
.compare(left, right, new PriorityComparator<>("oranges", "apples"))
.compare(left, right)
.result();
它将按照PriorityComparator
指定的元素进行排序,其他元素报告为相等。
要求T
具有可比性并将其用作默认值也很容易:
class PriorityComparator2<T extends Comparable<T>> implements Comparator<T> {
private final List<T> values;
public PriorityComparator2(T... values) {
this.values = Arrays.asList(values);
}
@Override public int compare(T o1, T o2) {
int idx1 = values.indexOf(o1);
int idx2 = values.indexOf(o2);
if (idx1 > -1) {
return idx2 > -1 ? idx1 - idx2 : -1;
}
return idx2 > -1 ? 1 : o1.compareTo(o2);
}
}
如果您正在考虑使用显式排序,那么可以假设您的列表没有重复。 此时, FluentIterable
和.toSet()
可以使这个变得微不足道。 将简单地忽略重复项(而不是错误输出)。
Iterable<String> sorted = FluentIterable.of(first).append(values).toSet();
or
ImmutableList<String> sorted =
FluentIterable.of(first).append(values).toSet().toList();
IMO,你的第一个建议实际上并没有那么糟糕,如果你的列表有非重复的重复值,也可以。 如果您使用FluentIterable,它看起来更好,因为您可以连接混合的Iterable和元素类型:
Iterable<String> others = Iterables.filter(values, not(equalTo(first)));
Iterable<String> sorted = FluentIterable.of(first).append(others);
这里的问题是,如果你有超过1个“第一”元素,你将丢失副本。
虽然修复很简单:
Iterable<String> firsts = Iterables.filter(values, equalTo(first)));
Iterable<String> others = Iterables.filter(values, not(equalTo(first));
Iterable<String> sorted = FluentIterable.from(firsts).append(others);
这需要对您的集合进行两次迭代,但算法很简单,并且可能比基于Comparator的任何算法都要快。 如果我不得不对这样的实现进行代码审查,我会毫不犹豫地接受这一点,因为它具有超级可读/可维护性,并且我100%相信它可以按预期工作。
如果所有其他方法都失败了,手工迭代永远不会伤害任何人
List<String> firsts = new ArrayList<>();
List<String> others = new ArrayList<>();
values.forEach(element -> (first.equal(element) ? firsts : others).add(element));
Iterable<String> sorted = FluentIterable.from(firsts).append(others);
最后,请注意,由于这些使用FluentIterable
,得到一个集合( ImmutableList
)出来的这些是附加微不足道.toList()
给你FluentIterable
。
这听起来像是一种“排名”排序,“首先”的对象具有更高的权重:所以1-liner排序将是:
Ordering.explicit(true, false).onResultOf(first::equals);
or the more general
Ordering.natural().reverse().onResultOf(rankFunction);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.