컬렉션 removeall 사건을 무시합니까?
-
12-09-2019 - |
문제
좋아, 여기 내 문제가 있습니다. 나는해야한다 HashSet
나는 그것을 사용한다 removeAll
메소드 한 세트에서 존재하는 값을 삭제하는 방법.
메소드를 호출하기 전에 분명히 값을 Set
에스. 나는 전화한다 .toUpperCase()
각각에 String
값이 두 목록에서 다른 경우 다른 경우에 추가하기 전에. 사건에 대한 운율이나 이유는 없습니다.
일단 전화하면 removeAll
, 나는 남은 값에 대해 원래 케이스를 다시 가져야합니다. Set
. 원래 목록을 실행하고 사용하지 않고이 작업을 수행하는 효율적인 방법이 있습니까? CompareToIgnoreCase
?
예시:
목록 1 :
"BOB"
"Joe"
"john"
"MARK"
"dave"
"Bill"
List2 :
"JOE"
"MARK"
"DAVE"
그런 다음 별도를 만듭니다 HashSet
사용하는 각 목록에 대해 toUpperCase()
~에 String
에스. 그런 다음 전화하십시오 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
에스, - 그런 다음 모든 공통점을 제거합니다
String
S 사례에 관계없이 감사합니다TreeSet#removeAll(Collection<?> c)
- 그리고 마지막으로 그 사실에 의존합니다
ArrayList#retainAll(Collection<?> c)
목록의 요소를 반복하고 각 요소에 대해 호출됩니다.contains(Object o)
제공된 컬렉션에서 값이 보관되어야하는지 여부를 알 수있는 컬렉션에서 컬렉션이 사례에 민감하지 않기 때문에 우리는String
우리가 제공 한 내용과 비교적으로 일치하는 것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)
주어진 요소를 제거 해야하는지 여부를 알 수있는 제공된 컬렉션에서는 사례에 민감하지 않은 컬렉션이 아닌 경우 예상 결과를 얻지 못합니다.
NB 2 : 방법의 동작 ArrayList#retainAll(Collection<?> c)
위에서 설명한 메소드의 기본 구현의 동작과 동일합니다. retainAll(Collection<?> c)
우리가 찾을 수있는 것 AbstractCollection
이 접근법은 실제로 구현하는 모든 컬렉션과 함께 작동합니다. retainAll(Collection<?> c)
동일한 행동이 있습니다.
당신은 a를 사용할 수 있습니다 해시 맵 그리고 자본 세트를 혼합 케이스 세트에 매핑하는 키로 사용하십시오.
해시 맵의 키는 고유하며 hashmap.keyset ()을 사용하여 세트를 얻을 수 있습니다.
원래 케이스를 검색하려면 hashmap.get ( "UppercaseName")만큼 간단합니다.
그리고에 따르면 선적 서류 비치:
이지도에 포함 된 키의 세트보기를 반환합니다. 세트는 맵에 의해 뒷받침되므로 맵의 변경 사항은 세트에 반영되고 그 반대도 마찬가지입니다. 이 세트는 요소 제거를 지원하며,이 맵에서 해당 매핑을 iterator.remove, set.remove, removeall, retainall 및 clear 작업을 통해 제거합니다. ADD 또는 ADDALL 작업을 지원하지 않습니다.
따라서 hashmap.keyset (). removeall은 해시 맵에 영향을 미칩니다 :)
편집 : McDowell의 솔루션을 사용하십시오. 나는 당신이 실제로 대문자가되기 위해 문자가 필요하지 않다는 사실을 간과했습니다 : P
이것은 사용을 해결하기에 흥미로운 것입니다 Google 수집. 당신은 다음과 같은 일정한 술어를 가질 수 있습니다.
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)로 되돌립니다.
내가 아는 한, Hashset은 객체의 해시 코드 메드를 사용하여 서로를 구별합니다. 그러므로 뚜렷한 경우를 위해서는이 메소드를 객체에서 무시해야합니다.
String을 실제로 사용하는 경우 String 클래스를 확장 할 수 없으므로이 메소드를 재정의 할 수 없습니다.
따라서 컨텐츠로 채우는 속성으로 문자열을 포함하는 자신의 클래스를 만들어야합니다. 문자열을 수정하기 위해 getValue () 및 setValue (String) 메소드를 가질 수 있습니다.
그런 다음 해시 맵에 자신의 클래스를 추가 할 수 있습니다.
이것은 당신의 문제를 해결해야합니다.
문안 인사