13

さて、これが私の問題です。HashSet'sする必要があります。このメソッドを使用して、removeAll一方のセットに存在する値をもう一方のセットから削除します。

Setメソッドを呼び出す前に、明らかに値をsに追加します。値は両方のリストで異なるケースであるため、追加する前.toUpperCase()にそれぞれを呼び出します。String事件に韻や理由はありません。

を呼び出したらremoveAll、に残っている値の元のケースを元に戻す必要がありますSet。元のリストを実行して使用せずにこれを行う効率的な方法はありCompareToIgnoreCaseますか?

例:

リスト1:

"BOB"
"Joe"
"john"
"MARK"
"dave"
"Bill"

リスト2:

"JOE"
"MARK"
"DAVE"

この後、を使用しHashSetてリストごとに個別に作成します。次に、を呼び出します。toUpperCase()StringremoveAll

Set1.removeAll(set2);

Set1:
    "BOB"
    "JOHN"
    "BILL"

リストを再び次のようにする必要があります。

"BOB"
"john"
"Bill"

どんなアイデアでも大歓迎です。私はそれが貧弱であることを知っています、元のリストの基準があるはずですが、それは私が決めることではありません。

4

5 に答える 5

13

私の最初の回答では、無​​意識のうちに を使用することを提案しましたComparatorが、これにより が契約TreeSetに違反し、バグが発生するのを待っています。equals

// Don't do this:
Set<String> setA = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
setA.add("hello");
setA.add("Hello");
System.out.println(setA);

Set<String> setB = new HashSet<String>();
setB.add("HELLO");
// Bad code; violates symmetry requirement
System.out.println(setB.equals(setA) == setA.equals(setB));

専用の型を使用することをお勧めします。

public final class CaselessString {
  private final String string;
  private final String normalized;

  private CaselessString(String string, Locale locale) {
    this.string = string;
    normalized = string.toUpperCase(locale);
  }

  @Override public String toString() { return string; }

  @Override public int hashCode() { return normalized.hashCode(); }

  @Override public boolean equals(Object obj) {
    if (obj instanceof CaselessString) {
      return ((CaselessString) obj).normalized.equals(normalized);
    }
    return false;
  }

  public static CaselessString as(String s, Locale locale) {
    return new CaselessString(s, locale);
  }

  public static CaselessString as(String s) {
    return as(s, Locale.ENGLISH);
  }

  // TODO: probably best to implement CharSequence for convenience
}

このコードは、バグを引き起こす可能性が低くなります:

Set<CaselessString> set1 = new HashSet<CaselessString>();
set1.add(CaselessString.as("Hello"));
set1.add(CaselessString.as("HELLO"));

Set<CaselessString> set2 = new HashSet<CaselessString>();
set2.add(CaselessString.as("hello"));

System.out.println("1: " + set1);
System.out.println("2: " + set2);
System.out.println("equals: " + set1.equals(set2));

残念ながら、これはより冗長です。

于 2009-08-06T21:16:59.397 に答える
4

次の方法で実行できます。

  1. リストのコンテンツを大文字と小文字を区別しない に移動しますTreeSet
  2. 次に、String大文字と小文字を区別せずにすべての一般的な sを削除しますTreeSet#removeAll(Collection<?> c)
  3. ArrayList#retainAll(Collection<?> c)そして最後に、リストの要素を反復処理し、要素ごとcontains(Object o)に提供されたコレクションを呼び出して、値を保持する必要があるかどうかを知るという事実に依存します。ここでは、コレクションは大文字と小文字を区別しないため、保持するのは提供されたインスタンスStringにあるものと大文字と小文字を区別せずに一致するs。TreeSet

対応するコード:

List<String> list1 = new ArrayList<>(
    Arrays.asList("BOB", "Joe", "john", "MARK", "dave", "Bill")
);

List<String> list2 = Arrays.asList("JOE", "MARK", "DAVE");

// Add all values of list1 in a case insensitive collection
Set<String> set1 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
set1.addAll(list1);
// Add all values of list2 in a case insensitive collection
Set<String> set2 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
set2.addAll(list2);
// Remove all common Strings ignoring case
set1.removeAll(set2);
// Keep in list1 only the remaining Strings ignoring case
list1.retainAll(set1);

for (String s : list1) {
    System.out.println(s);
}

出力:

BOB
john
Bill

NB 1:TreeSet動作はTreeSet#removeAll(Collection<?> c)両方のコレクションのサイズに依存するため、現在のコレクションのサイズが厳密に提供されたコレクションのサイズ、次に現在のコレクションを直接呼び出しremove(Object o)て各要素を削除します。この場合、提供されたコレクションはリストになる可能性があります。しかし、逆の場合はcontains(Object o)、指定された要素を削除する必要があるかどうかを知るために、提供されたコレクションを呼び出します。そのため、大文字と小文字を区別しないコレクションでないと、期待される結果が得られません。

注意 2:上記のメソッドの動作は、メソッドArrayList#retainAll(Collection<?> c)のデフォルト実装の動作と同じであり、このアプローチは、実装が同じ動作を持つコレクションで実際に機能しますretainAll(Collection<?> c)AbstractCollectionretainAll(Collection<?> c)

于 2016-08-02T16:10:02.473 に答える
1

これは、 google-collectionsを使用して解決する興味深いものです。次のような一定の Predicate を使用できます。

private static final Function<String, String> TO_UPPER = new Function<String, String>() {
    public String apply(String input) {
       return input.toUpperCase();
}

そして、あなたが求めていることは、次のように行うことができます:

Collection<String> toRemove = Collections2.transform(list2, TO_UPPER);

Set<String> kept = Sets.filter(list1, new Predicate<String>() {
    public boolean apply(String input) {
        return !toRemove.contains(input.toUpperCase());
    }
}

あれは:

  • 「破棄する」リストの大文字のみのバージョンを作成する
  • 元のリストにフィルタを適用し、大文字のみのリストにない大文字の値を持つアイテムのみを保持します。

の出力はCollections2.transform効率的なSet実装ではないことに注意してください。そのため、大量のデータを扱っていて、そのリストを精査するコストが負担になる場合は、代わりに次を使用できます。

Set<String> toRemove = Sets.newHashSet(Collections2.transform(list2, TO_UPPER));

これにより、効率的なルックアップが復元され、フィルタリングが O(n^2) ではなく O(n) に戻されます。

于 2009-12-22T10:20:26.717 に答える
1

ハッシュマップを使用し、大/小文字混合セットにマップされるキーとして大文字セットを使用できます。

ハッシュマップのキーは一意であり、HashMap.keyset(); を使用してそれらのセットを取得できます。

元のケースを取得するには、HashMap.get("UPPERCASENAME") と同じくらい簡単です。

そしてドキュメントによると:

このマップに含まれるキーのセット ビューを返します。セットはマップに支えられているため、マップへの変更はセットに反映され、その逆も同様です。セットは、Iterator.remove、Set.remove、removeAll、retainAll、および clear オペレーションを介して、このマップから対応するマッピングを削除する要素の削除をサポートします。add または addAll 操作はサポートされていません。

したがって、 HashMap.keyset().removeAll はハッシュマップに影響します:)

編集: McDowell のソリューションを使用します。実際には文字を大文字にする必要がないという事実を見落としていました:P

于 2009-08-06T21:09:42.110 に答える
0

私の知る限り、ハッシュセットはオブジェクトのhashCodeメソッドを使用してそれらを互いに区別しています。したがって、ケースを区別するために、オブジェクトでこのメソッドをオーバーライドする必要があります。

本当に文字列を使用している場合は、文字列クラスを拡張できないため、このメソッドをオーバーライドすることはできません。

したがって、コンテンツを入力する属性として文字列を含む独自のクラスを作成する必要があります。文字列を変更するために getValue() および setValue(String) メソッドが必要になる場合があります。

その後、独自のクラスをハッシュマップに追加できます。

これで問題が解決するはずです。

よろしく

于 2009-08-06T21:10:29.147 に答える