集合removeAll忽略大小写?
-
12-09-2019 - |
题
好的,这是我的问题。我必须 HashSet
的,我用的是 removeAll
方法从一组中删除存在于另一组中的值。
在调用该方法之前,我显然将这些值添加到 Set
s。我打电话 .toUpperCase()
在各个 String
在添加之前,因为两个列表中的值的情况不同。这个案子没有任何规律或理由。
一旦我打电话 removeAll
, ,我需要取回原始案例以获取留在其中的值 Set
. 。有没有一种有效的方法可以做到这一点,而无需运行原始列表并使用 CompareToIgnoreCase
?
例子:
清单1:
"BOB"
"Joe"
"john"
"MARK"
"dave"
"Bill"
清单2:
"JOE"
"MARK"
"DAVE"
之后,创建一个单独的 HashSet
对于每个列表使用 toUpperCase()
在 String
s。然后打电话 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));
这是不幸的是,更详细的
其他提示
这是可以完成的:
- 运动的内容列入的情况不敏感的
TreeSet
s, - 然后去除所有常见的
String
s情况下-还有谢谢TreeSet#removeAll(Collection<?> c)
- 最后依靠的事实,
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
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中。
这应该解决您的问题。
问候