13

Guava では、コレクション内にあることがわかっているタイプCollection<E>の要素と要素がある場合、最初に並べ替えてから残りのコレクションを並べ替えるカスタムを作成したいと思います。ただし、そこにたどり着くまでの道のりは非常に複雑に思えます。eEOrdering<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(これを最初に並べ替えて、他のすべての要素の順序を保持したいのですが、ドキュメントによると、結果の Ordering は、ClassCastException明示的にリストされていない for 要素をスローします。)

またはこのように:

Ordering.explicit(first, values.toArray(/* etc */));

first(ただし、値が重複するため、これは失敗します)

誰かが私がやりたいことを行う簡潔な方法を思い付くことができますか?

ところで、それは である必要はありません。指定された順序でOrderingを作成するための回避策になることもありますIterableが、これも非常に複雑です。

Iterable<String> sorted = Iterables.concat(
                             ImmutableList.of(first),
                             Iterables.filter(values, not(equalTo(first))));
4

7 に答える 7

10

さて、ここにそれを行う 1 つの方法がありますが、それほど良い方法ではないかもしれません。

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();
      }
    });

比較チェーンのドキュメント

関連する Guava 機能のリクエスト-- 詳細を追加してください。

于 2013-01-19T15:44:41.847 に答える
2

より特別な値がある場合、これはより便利で反復が少なくなります。

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);
    }
}
于 2016-01-22T23:04:33.430 に答える
1

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()))

編集

Ordering の代わりに Comparator を使用するようにコードを変更しました。残りは同じままです。

于 2013-01-18T21:02:51.117 に答える
1

おそらく、この答えはあなたがすでに持っているものよりも簡単/複雑ではありませんが、少なくとも再利用できます:)

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));
于 2013-01-18T17:00:46.773 に答える
1

com.google.common.collect.ExplicitOrdering のソースを見ると、各アイテムのランクを保持するマップが維持され、compare単純にランクが比較されます。自分で同じことを行うことができますが、指定された最初のアイテムのランクを強制的に -1 にします。これは他のすべてのアイテムの前です。

リストがある場合 (質問のタイトルが示すように)、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);

コレクションしかない場合 (質問の本文にあるように)、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 で Comparator を Ordering に変えることができます。

于 2014-09-23T19:17:43.307 に答える
0

最初から明示的な順序付けを使用することを検討していた場合は、リストに重複がないことを前提としています。どの時点で、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);

ただし、ここでの問題は、「最初の」要素が複数ある場合、コピーが失われることです。

ただし、修正は簡単です。

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);

これにはコレクションを 2 回反復する必要がありますが、アルゴリズムは簡単で、おそらく 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

于 2016-09-26T08:32:27.507 に答える