繁体   English   中英

Guava:如何从List和单个元素创建显式排序?

[英]Guava: How to create an explicit Ordering from a List and a single element?

番石榴,给定一个Collection<E>和元素eE ,我知道在集合中,我想创建一个自定义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();
      }
    });

ComparisonChain docs

相关的番石榴功能请求 - 请添加任何详细信息。

也许这个答案并不比你已经拥有的更简单/更简单,但至少可以重复使用:)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM