문제

좋아, 여기 내 문제가 있습니다. 나는해야한다 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));

불행히도 이것은 더 많은 장점입니다.

다른 팁

다음과 같이 할 수 있습니다.

  1. 목록의 내용을 대변인으로 이동합니다 TreeSet에스,
  2. 그런 다음 모든 공통점을 제거합니다 StringS 사례에 관계없이 감사합니다 TreeSet#removeAll(Collection<?> c)
  3. 그리고 마지막으로 그 사실에 의존합니다 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) 메소드를 가질 수 있습니다.

그런 다음 해시 맵에 자신의 클래스를 추가 할 수 있습니다.

이것은 당신의 문제를 해결해야합니다.

문안 인사

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top