好的,这是我的问题。我必须 HashSet的,我用的是 removeAll 方法从一组中删除存在于另一组中的值。

在调用该方法之前,我显然将这些值添加到 Sets。我打电话 .toUpperCase() 在各个 String 在添加之前,因为两个列表中的值的情况不同。这个案子没有任何规律或理由。

一旦我打电话 removeAll, ,我需要取回原始案例以获取留在其中的值 Set. 。有没有一种有效的方法可以做到这一点,而无需运行原始列表并使用 CompareToIgnoreCase?

例子:

清单1:

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

清单2:

"JOE"
"MARK"
"DAVE"

之后,创建一个单独的 HashSet 对于每个列表使用 toUpperCase()Strings。然后打电话 removeAll.

Set1.removeAll(set2);

Set1:
    "BOB"
    "JOHN"
    "BILL"

我需要让列表再次看起来像这样:

"BOB"
"john"
"Bill"

任何想法将不胜感激。我知道它很差,原始列表应该有一个标准,但这不是我可以决定的。

有帮助吗?

解决方案

在我原来的答复,我使用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));

这是不幸的是,更详细的

其他提示

这是可以完成的:

  1. 运动的内容列入的情况不敏感的 TreeSets,
  2. 然后去除所有常见的 Strings情况下-还有谢谢 TreeSet#removeAll(Collection<?> c)
  3. 最后依靠的事实, ArrayList#retainAll(Collection<?> c) 将迭代要素的列表,并对每个元件就会呼叫 contains(Object o) 在提供集知道是否值应保持或不在这里作为收集情况不敏感,我们将只保留 Strings匹配的情况下-还有什么我们在提供 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

NB1: 重要的是有内容的第二列入一个 TreeSet 特别是,如果我们不知道它的大小,因为行为 TreeSet#removeAll(Collection<?> c) 的大小取决于这两个集合,如果大小目前的收集是严格比的尺寸提供的集合,那么就会直接打电话 remove(Object o) 在当前集合,以删除的每一个元件,在这种情况下所提供的收集可能是一个列表。但如果它是相对的,就会呼叫 contains(Object o) 在提供集知道是否给定的元素应当删除,或者不那么如果它不是一个情况下-不敏感的集合,我们不会取得预期的结果。

NB2: 该行为的方法 ArrayList#retainAll(Collection<?> c) 上述相同行为的默认的执行方法 retainAll(Collection<?> c) 我们可以找到 AbstractCollection 这样,这种做法实际上将工作与任何集其执行情况 retainAll(Collection<?> c) 具有同样的行为。

您可以使用 哈希图 并使用大写集作为映射到混合大小写集的键。

hashmap 的键是唯一的,您可以使用 HashMap.keyset() 获取一组键;

要检索原始大小写,就像 HashMap.get("UPPERCASENAME") 一样简单。

并根据 文档:

返回键的设置视图 包含在这张地图中。 该集合由地图支持,因此对地图的更改反映在集合中,反之亦然。 set supports 元素 removal,这将删除 从这张地图对应的映射, 通过 Iterator.remove、Set.remove、 removeAll、retainAll 和 clear 操作。它不支持 add 或 addAll 操作。

所以 HashMap.keyset().removeAll 将影响 hashmap :)

编辑:使用麦克道尔的解决方案。我忽略了一个事实,即您实际上并不需要字母大写:P

这将是一个有趣的一个要解决的使用 谷歌-集合.你可以有一定谓像这样:

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)代替O(n^2).

据我所知,HashSet的的使用对象的hashCode-方法不同它们彼此。 因此,你应该重写以不同的情况中的对象此方法。

如果你真的使用的字符串,不能覆盖这个方法,你不能扩展字符串类。

因此,你需要创建一个包含一个字符串作为您填写您的内容属性自己的类。你可能希望有一个的getValue()和setValue方法(String)方法,以修改字符串。

,那么你可以添加自己的类HashMap中。

这应该解决您的问题。

问候

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top