Question

Ok, donc voici ma question. Je dois HashSet de, j'utilise la méthode removeAll pour supprimer des valeurs qui existent dans un jeu de l'autre.

Avant d'appeler la méthode, j'ajoute évidemment les valeurs aux Sets. J'appelle .toUpperCase() sur chaque String avant d'ajouter, car les valeurs sont des cas différents dans les deux listes. Il n'y a pas de rime ni raison de l'affaire.

Une fois que j'appelle removeAll, je dois avoir les cas originaux de retour pour les valeurs qui sont laissés dans le Set. Y at-il un moyen efficace de le faire sans courir dans la liste originale et en utilisant CompareToIgnoreCase?

Exemple:

List1:

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

List2:

"JOE"
"MARK"
"DAVE"

Après cela, créez un HashSet pour chaque liste à l'aide toUpperCase() sur Strings. Ensuite, appelez removeAll.

Set1.removeAll(set2);

Set1:
    "BOB"
    "JOHN"
    "BILL"

Je dois obtenir la liste pour ressembler à nouveau:

"BOB"
"john"
"Bill"

Toutes les idées seraient appréciées. Je sais qu'il est pauvre, il devrait y avoir une norme pour la liste initiale, mais ce n'est pas pour moi de décider.

Était-ce utile?

La solution

Dans ma première réponse, je l'ai suggéré inconsidérément l'aide d'un Comparator, mais cela provoque le TreeSet de violer le contrat de equals et est un bug en attente de se produire:

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

Il est préférable d'utiliser un type dédié:

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
}

Ce code est moins susceptible de causer des bugs:

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

Ceci est, malheureusement, plus bavard.

Autres conseils

Il pourrait se faire par:

  1. Déplacement du contenu de vos listes en TreeSets insensibles à la casse,
  2. puis supprimer tous les Strings communes indépendamment de la casse grâce TreeSet#removeAll(Collection<?> c)
  3. et appuyer enfin sur le fait que ArrayList#retainAll(Collection<?> c) itérera sur les éléments de la liste et pour chaque élément, il appellera contains(Object o) sur la collecte fournie à savoir si la valeur doit être maintenue ou non, et ici comme la collection est insensible à la casse , nous garderons seulement les Strings qui correspondent indépendamment de la casse avec ce que nous avons fourni dans l'instance TreeSet.

Le code correspondant:

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

Sortie:

BOB
john
Bill

NB 1: Il est important d'avoir le contenu de la deuxième liste dans un TreeSet surtout si nous ne savons pas la taille de celui-ci parce que le comportement de TreeSet#removeAll(Collection<?> c) dépend de la taille des deux collections , si la taille de la collection actuelle est strictement plus grande que la taille de la collection fournie, il appellera directement remove(Object o) sur la collection en cours pour supprimer chaque élément, dans ce cas, la collection pourrait être fourni une liste. Mais si elle est à l'opposé, il appellera contains(Object o) sur la collection fournie de savoir si un élément donné doit être retiré ou non si ce n'est pas une collection insensible à la casse, nous n'obtiendrons pas le résultat attendu.

NB 2: Le comportement de la méthode ArrayList#retainAll(Collection<?> c) décrit ci-dessus est le même que le comportement de l'implémentation par défaut de la méthode retainAll(Collection<?> c) que nous pouvons trouver dans AbstractCollection de telle sorte que cette approche fonctionne réellement avec tout collections dont la mise en œuvre de retainAll(Collection<?> c) a le même comportement.

Vous pouvez utiliser un hashmap et utiliser le capital défini comme les clés qui tracent le cas mixte défini.

Les clés de hashmaps sont uniques et vous pouvez obtenir un ensemble d'entre eux en utilisant HashMap.keyset ();

pour récupérer l'affaire initiale, il est aussi simple que HashMap.get ( "UPPERCASENAME").

Et selon le documentation :

  

Retourne une vue ensemble des clés   contenues dans cette carte. L'ensemble est   soutenu par la carte, de sorte que des modifications à la   la carte se reflètent dans l'ensemble, et   vice-versa. L'ensemble prend en charge élément   l'enlèvement, ce qui supprime la   mappage correspondant de la carte,   par l'intermédiaire du Iterator.remove, Set.remove,   removeAll, retainAll et clair   opérations. Il ne supporte pas la   opérations d'ajout ou addAll.

HashMap.keyset () removeAll effectuera la hashmap.)

EDIT: utiliser la solution de McDowell. Je compte du fait que vous n'avez pas réellement besoin les lettres majuscules pour être: P

Ce serait intéressante à résoudre en utilisant google-collections . Vous pourriez avoir une constante prédicats comme ceci:

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

et ce que vous êtes après pourrait être fait someting comme ceci:

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

C'est:

  • Construire une version en majuscules seule de la liste « se défaire »
  • Appliquer un filtre à la liste initiale, en conservant uniquement ces éléments dont la valeur est en majuscule pas dans la liste uniquement en majuscules.

Notez que la sortie de Collections2.transform n'est pas une mise en œuvre efficace Set, donc si vous avez affaire à un grand nombre de données et le coût de sondage cette liste vous blesser, vous pouvez utiliser à la place

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

qui rétablira une recherche efficace, renvoyant le filtrage à O (n) au lieu de O (n ^ 2).

pour autant que je sache, HashSet Utilisons hashCode méthode de l'objet pour les distincts les uns des autres. vous devez donc remplacer cette méthode dans votre objet pour des cas distincts.

si vous utilisez vraiment la chaîne, vous ne pouvez pas remplacer cette méthode que vous ne pouvez pas étendre la chaîne de classe.

Par conséquent, vous devez créer votre propre classe contenant une chaîne comme attribut que vous remplissez votre contenu. vous pouvez avoir une méthode getValue () et setValue (String) afin de modifier la chaîne.

vous pouvez ajouter votre propre classe au hashmap.

ce qui devrait résoudre votre problème.

concerne

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top