세트에서 요소 얻기


323

Set다른 요소와 동일한 요소를 가져 오는 작업을 제공 하지 않는 이유는 무엇 입니까?

Set<Foo> set = ...;
...
Foo foo = new Foo(1, 2, 3);
Foo bar = set.get(foo);   // get the Foo element from the Set that equals foo

Set같은 요소 가 포함되어 있는지 물어볼 수 있는데 bar왜 해당 요소를 얻을 수 없습니까? :(

명확히하기 위해 equals메서드가 재정의되지만 필드 중 하나만 검사하지는 않습니다. 따라서 Foo동일한 것으로 간주되는 두 객체는 실제로 다른 값을 가질 수 있으므로을 사용할 수 없습니다 foo.


2
이 게시물은 이미 광범위하게 논의되었으며 좋은 답변이 제안되었습니다. 그러나 순서가 지정된 세트를 찾고 있다면 SortedSet맵 기반의 구현 및 구현을 사용하십시오 (예 : TreeSet액세스 가능 first()).
Eliran Malka

3
위에서 설명한 것과 똑같은 경우에도 해당 방법이 누락되었습니다. Objective-C ( NSSet)에는 이러한 방법이 있습니다. 이 member함수 는 호출 되어 메소드 내의 매개 변수와 "같음"을 비교하는 세트 내의 오브젝트를 리턴합니다 member(물론 다른 오브젝트 일 수도 있고 다른 특성을 가질 수도 있습니다.
Mecki

답변:


118

요소가 같으면 요소를 가져올 필요가 없습니다. Map이 사용 사례에 A 가 더 적합합니다.


여전히 요소를 찾으려면 반복자를 사용하는 것 외에 다른 옵션이 없습니다.

public static void main(String[] args) {

    Set<Foo> set = new HashSet<Foo>();
    set.add(new Foo("Hello"));

    for (Iterator<Foo> it = set.iterator(); it.hasNext(); ) {
        Foo f = it.next();
        if (f.equals(new Foo("Hello")))
            System.out.println("foo found");
    }
}

static class Foo {
    String string;
    Foo(String string) {
        this.string = string;
    }
    @Override
    public int hashCode() { 
        return string.hashCode(); 
    }
    @Override
    public boolean equals(Object obj) {
        return string.equals(((Foo) obj).string);
    }
}

234
요소를 얻는 것이 절대적으로 중요 할 수 있습니다. 요소에 이미 요소가 추가 된 후 일부 요소 값을 업데이트하려면 어떻게해야합니까? 예를 들어, .equals ()가 지정된 OP로 모든 필드를 사용하지 않는 경우. 덜 효율적인 해결책은 요소를 제거하고 업데이트 된 값으로 다시 추가하는 것입니다.
KyleM

14
나는 여전히 a Map가 더 적합 하다고 주장 할 것이다 ( Map<Foo, Foo>이 경우).
dacwe

22
@ dacwe, 나는 그것을 정확하게 피할 수있는 방법을 찾기 시작했기 때문에 여기에 도착했습니다! 키와 해당 값으로 동시에 작용하는 객체는 세트의 모든 것입니다. 제 경우에는 키 (문자열)로 세트에서 복잡한 객체를 얻고 싶습니다. 이 문자열은 매핑되는 객체에 캡슐화되어 고유합니다. 실제로, 전체 대상은 그 열쇠를 중심으로 '회전'합니다. 또한 호출자는 해당 문자열을 알고 있지만 객체 자체는 알지 못합니다. 그것이 정확히 키로 검색하려는 이유입니다. 물론 지금지도를 사용하고 있지만 여전히 이상한 행동입니다.
pauluss86

4
@KyleM 유스 케이스를 이해하지만 hashCode / equals의 일부인 속성을 건드리지 않는 것이 중요하다는 점을 강조하고 싶습니다. Javadoc 세트에서 : "참고 : 가변 오브젝트가 세트 요소로 사용되는 경우 세심한주의를 기울여야합니다. 오브젝트가 세트의 요소. " -해당 객체를 변경할 수 없거나 최소한 변경할 수없는 키 속성이있는 것이 좋습니다.
stivlo

5
나는 당신 Map<Foo, Foo>이 대체물로 사용할 수 있다는 것에 동의합니다 . 단점은 맵이 항상 최소한 키와 값을 저장해야하며 (성능도 해시도 저장해야 함) 세트는 값을 저장하는 것만으로 벗어날 수 있다는 것입니다. 어쩌면 성능 해시). 따라서 좋은 세트 구현은 똑같이 빠르지 Map<Foo, Foo>만 최대 50 % 적은 메모리를 사용합니다. Java의 경우 HashSet은 내부적으로 HashMap을 기반으로하므로 중요하지 않습니다.
Mecki

372

" 다른 요소와 같은 요소를 얻기 위해 작업을 제공 하지 않는 이유무엇입니까? "라는 정확한 질문에 대답 Set하기 위해, 컬렉션 프레임 워크의 디자이너가 매우 기대하지 않았기 때문에 대답은 다음과 같습니다. 그들은 매우 합법적 인 사용 사례를 예상하지 않았고, 순진하게 "수학적 집합 추상화"(javadoc에서)를 모델링하려고 시도했으며 단순히 유용한 get()방법 을 추가하는 것을 잊었습니다 .

이제 " 어떻게 요소를 얻는가? "라는 묵시적 질문으로 , 가장 좋은 해결책은 요소 Map<E,E>대신에 Set<E>요소를 매핑하는 것입니다. 이런 식으로 "set"에서 요소를 효율적으로 검색 할 수 있습니다 . 왜냐하면 get () 메소드 Map는 효율적인 해시 테이블 또는 트리 알고리즘을 사용하여 요소를 찾기 때문입니다. 원하는 경우을 캡슐화하여 Set추가 get()메소드 를 제공하는 자체 구현을 작성할 수 있습니다 Map.

다음 대답은 내 의견으로는 나쁘거나 잘못되었습니다.

"이미 같은 객체를 가지고 있기 때문에 요소를 얻을 필요가 없습니다": 질문에서 이미 보여 주듯이 주장이 잘못되었습니다. 동일한 두 개체는 여전히 개체 동등성과 관련이없는 다른 상태를 가질 수 있습니다. 목표는 Set"쿼리"로 사용되는 객체의 상태가 아니라에 포함 된 요소의이 상태에 액세스하는 것 입니다.

"반복자를 사용하는 것 외에는 다른 옵션이 없습니다.": 대규모 집합에 대해 전혀 비효율적 인 컬렉션에 대한 선형 검색 Set입니다. 하지마! 이 방법을 사용하여 실제 시스템에서 심각한 성능 문제가 발생했습니다. 내 의견으로는 누락 된 get()방법 에 대해 끔찍한 것은 해결하기가 다소 번거롭지 않지만 대부분의 프로그래머는 의미를 생각하지 않고 선형 검색 방법을 사용한다는 것입니다.


27
meh. 같지 않은 객체가 "동일"하도록 equals의 구현을 재정의하는 것이 여기서 문제입니다. "이 오브젝트와 동일한 오브젝트를 가져 오십시오"라는 메소드를 요청한 후 동일하지 않은 오브젝트가 리턴 될 것으로 예상하면 유지 보수 문제점을 야기하기가 미치며 쉬워 보입니다. 다른 사람들이 제안했듯이,지도를 사용하면 이러한 모든 문제가 해결됩니다. 두 개의 같지 않은 객체는 맵에서 동일한 키를 가질 수 있으며 동일한 키를 가지면 그 사이의 관계를 보여줍니다.
David Ogren

20
@David Ogren의 강력한 단어. 메? 미친? 그러나 당신의 의견에서, 당신은 마치 같은 것을 의미하는 것처럼 "동일한"과 "동일한"이라는 단어를 사용하고 있습니다. 그들은하지 않습니다. 특히 Java에서 ID는 "=="연산자로 표현되고 동등성은 equals () 메소드로 표현됩니다. 그들이 같은 것을 의미한다면, equals () 메소드가 전혀 필요하지 않을 것입니다. 다른 언어에서는 물론 다를 수 있습니다. 예를 들어, Groovy에서 identity는 is () 메소드이고 동등성은 "=="입니다. 재밌지 않니?
jschreiner

15
동등한 단어를 사용해야했을 때 동일한 단어를 사용했다는 비판은 매우 유효합니다. 그러나 Foo와 Bar가 "동일"하지만 동일하게 사용하기에 "동일"하지 않도록 객체에 동등을 정의하면 기능과 가독성 / 유지 보수성 모두에서 모든 종류의 문제가 발생합니다. 이 문제는 잠재적 문제에 대한 빙산의 일각을 설정합니다. 예를 들어, 동일한 객체에는 동일한 해시 코드가 있어야합니다. 그래서 그는 잠재적 인 해시 충돌을 일으킬 것입니다. foo 이외의 것을 얻기 위해 .get (foo)을 호출하는 것이 미친 겁니까?
David Ogren

12
예를 들어, HashSet은 HashMap 주위에 래퍼로 ​​구현됩니다 (키를 더미 값으로 매핑). 따라서 HashSet 대신 HashMap을 명시 적으로 사용하면 메모리 사용에 오버 헤드가 발생하지 않습니다.
Alexey B.

4
@ user686249 나는 이것이 단지 학계 토론으로 나왔다고 생각합니다. 나는 평등을 재정의하는 것에 반대하여 선외에 있었을 수도 있음을 인정한다. 특히 당신과 같은 용도로. 그러나 여전히이 메소드를 호출한다는 아이디어에 반대합니다 get(). 귀하의 예에서 customerSet.get (thisCustomer)에 의해 매우 혼란 스럽습니다. (많은 답변에서 제안한 Map)은 canonicalCustomerMap.get (이 고객)으로 충분합니다. NSNS에서 Objective-C의 멤버 메소드와 같이 더 명확하게 명명 된 메소드로도 괜찮을 것입니다.
David Ogren

19

같은 물건을 가지고 있다면 왜 세트의 물건이 필요합니까? 키만으로 "동일" Map하면 더 나은 선택이 될 것입니다.

어쨌든 다음과 같은 작업이 수행됩니다.

Foo getEqual(Foo sample, Set<Foo> all) {
  for (Foo one : all) {
    if (one.equals(sample)) {
      return one;
    }
  } 
  return null;
}

Java 8에서는 이것이 하나의 라이너가 될 수 있습니다.

return all.stream().filter(sample::equals).findAny().orElse(null);

나는이 답변을 더 좋아한다 .OOP에 반대하고 Cyclomatic Complexity 값을 높이기 때문에 두 개의 return 문을 사용하지 않는 것이 좋다.
Leo

8
@Leo에게 감사하지만, 단일 출구 패러다임은 OOP에 반대하지 않으며 Fortran 또는 COBOL보다 최신 언어에는 대부분 유효하지 않습니다. softwareengineering.stackexchange.com/questions/118703/…
Arne Burmeister

1
세트 대신 맵을 사용하는 것이 더 나은 옵션 인 것 같습니다. 세트에서 요소를 반복하는 것이 맵에서 단일 값을 얻는 것보다 더 효과적입니다. (O (N) vs O (1))
Jamie Flournoy

@JamieFlournoy, 다른 요소에 대해 동일한 세트를 여러 번 확인 해야하는 경우 훨씬 좋습니다. 지도를 먼저 작성하려면 더 많은 노력이 필요하므로 단일 사용이 아닌 경우.
Arne Burmeister

18

세트를리스트로 변환 한 후 get리스트의 방법 을 사용

Set<Foo> set = ...;
List<Foo> list = new ArrayList<Foo>(set);
Foo obj = list.get(0);

37
나는 이것을 얻지 못한다. 이것은 세트 의 임의의 객체를 검색합니다 . 아니 객체입니다.
aioobe

14

불행히도 Java의 기본 설정은 jschreiner가 정확하게 설명했듯이 "get"작업을 제공하도록 설계되지 않았습니다 .

반복자를 사용하여 관심있는 요소를 찾 거나 ( dacwe제안한 ) 요소를 제거하고 업데이트 된 값으로 Kyle 요소가 제안한 값을 다시 추가하는 솔루션 이 작동 할 수는 있지만 매우 비효율적 일 수 있습니다.

등호 구현을 재정의하면 David Ogren이 올바르게 언급 한 것처럼 동일하지 않은 객체가 "동일"하므로 유지 관리 문제가 쉽게 발생할 수 있습니다.

imho는 Map을 명시적인 대체물로 사용하여 코드를 덜 우아하게 만듭니다.

목표가 세트에 포함 된 요소의 원래 인스턴스에 액세스하는 것입니다 (유스 케이스를 올바르게 이해했으면합니다). 또 다른 가능한 해결책이 있습니다.


Java로 클라이언트 서버 비디오 게임을 개발하는 동안 개인적으로 동일한 요구가있었습니다. 필자의 경우 각 클라이언트에는 서버에 저장된 구성 요소의 사본이 있었으며 문제는 클라이언트가 서버의 객체를 수정해야 할 때마다 발생했습니다.

인터넷을 통해 객체를 전달한다는 것은 클라이언트가 해당 객체의 다른 인스턴스를 가지고 있음을 의미했습니다. 이 "복사 된"인스턴스를 원래 인스턴스와 일치시키기 위해 Java UUID를 사용하기로 결정했습니다.

그래서 추상 클래스 UniqueItem을 만들었습니다.이 클래스는 하위 클래스의 각 인스턴스에 임의의 고유 ID를 자동으로 부여합니다.

이 UUID는 클라이언트와 서버 인스턴스간에 공유되므로 맵을 사용하여 쉽게 일치시킬 수 있습니다.

그러나 유사한 유스 케이스에서 맵을 직접 사용하는 것은 여전히 ​​우아하지 않았습니다. 누군가지도를 사용하는 것은 관리하고 다루기가 더 복잡 할 수 있다고 주장 할 수 있습니다.

이러한 이유로 저는 MagicSet이라는 라이브러리를 구현하여 개발자에게 맵을 "투명하게"만듭니다.

https://github.com/ricpacca/magicset


원래 Java HashSet과 마찬가지로 MagicHashSet (라이브러리에서 제공되는 MagicSet의 구현 중 하나임)은 지원 HashMap을 사용하지만 요소를 키로, 더미 값을 값으로 사용하는 대신 요소의 UUID를 키로 사용합니다. 그리고 요소 자체는 가치입니다. 일반적인 HashSet에 비해 메모리 사용에 오버 헤드가 발생하지 않습니다.

또한 MagicSet을 Set으로 정확하게 사용할 수 있지만 getFromId (), popFromId (), removeFromId () 등과 같은 추가 기능을 제공하는 몇 가지 메소드가 있습니다.

이를 사용하기위한 유일한 요구 사항은 MagicSet에 저장하려는 모든 요소가 추상 클래스 UniqueItem을 확장해야한다는 것입니다.


다음은 동일한 UUID (또는 UUID)를 가진 해당 도시의 다른 인스턴스가 제공된 경우 MagicSet에서 도시의 원래 인스턴스를 검색하는 코드 예제입니다.

class City extends UniqueItem {

    // Somewhere in this class

    public void doSomething() {
        // Whatever
    }
}

public class GameMap {
    private MagicSet<City> cities;

    public GameMap(Collection<City> cities) {
        cities = new MagicHashSet<>(cities);
    }

    /*
     * cityId is the UUID of the city you want to retrieve.
     * If you have a copied instance of that city, you can simply 
     * call copiedCity.getId() and pass the return value to this method.
     */
    public void doSomethingInCity(UUID cityId) {
        City city = cities.getFromId(cityId);
        city.doSomething();
    }

    // Other methods can be called on a MagicSet too
}

11

세트가 실제로 a NavigableSet<Foo>(예 : a TreeSet) 인 Foo implements Comparable<Foo>경우

Foo bar = set.floor(foo); // or .ceiling
if (foo.equals(bar)) {
    // use bar…
}

(힌트에 대한 @ eliran-malka의 의견에 감사드립니다.)


5
내가 완전히 미쳤다고 생각한 내 코드를 읽는 사람이 마음에 들지 않으면 훌륭한 해결책이 될 것입니다.
Adam

10

Java 8을 사용하면 다음을 수행 할 수 있습니다.

Foo foo = set.stream().filter(item->item.equals(theItemYouAreLookingFor)).findFirst().get();

그러나 .get ()은 NoSuchElementException을 발생 시키거나 Optional 항목을 조작 할 수 있습니다.


5
item->item.equals(theItemYouAreLookingFor)단축 될 수 있습니다theItemYouAreLookingFor::equals
Henno Vermeulen

5
Object objectToGet = ...
Map<Object, Object> map = new HashMap<Object, Object>(set.size());
for (Object o : set) {
    map.put(o, o);
}
Object objectFromSet = map.get(objectToGet);

하나만 가져 오면 모든 요소를 ​​반복하기 때문에 큰 성능을 발휘하지 못하지만 큰 세트에서 여러 검색을 수행하면 차이를 알 수 있습니다.


5

왜:

Set은 비교 수단을 제공하는 데 유용한 역할을하는 것으로 보입니다. 중복 요소를 저장하지 않도록 설계되었습니다.

이 의도 / 디자인으로 인해 저장된 객체에 대한 참조를 get ()시킨 다음 변경하면 Set의 디자인 의도가 왜곡되어 예기치 않은 동작이 발생할 수 있습니다.

로부터 JavaDoc을

가변 객체가 세트 요소로 사용되는 경우 세심한주의를 기울여야합니다. 오브젝트가 세트의 요소 인 동안 오브젝트의 값이 동등 비교에 영향을주는 방식으로 변경되면 세트의 동작이 지정되지 않습니다.

어떻게:

스트림이 소개되었으므로 다음을 수행 할 수 있습니다.

mySet.stream()
.filter(object -> object.property.equals(myProperty))
.findFirst().get();

2

Arrays 클래스를 사용하는 것은 어떻습니까?

import java.util.Arrays;
import java.util.List;
import java.util.HashSet;
import java.util.Arrays;

public class MyClass {
    public static void main(String args[]) {
        Set mySet = new HashSet();
        mySet.add("one");
        mySet.add("two");
        List list = Arrays.asList(mySet.toArray());
        Object o0 = list.get(0);
        Object o1 = list.get(1);
        System.out.println("items " + o0+","+o1);
    }
}

출력 :
항목 하나, 둘



1

나는 이것이 오래 전에 요청되고 대답되었지만, 누군가 관심이 있다면, 여기 내 솔루션-HashMap이 지원하는 사용자 정의 세트 클래스가 있습니다.

http://pastebin.com/Qv6S91n9

다른 모든 Set 메서드를 쉽게 구현할 수 있습니다.


7
하나만 연결하는 대신 예제를 포함하는 것이 좋습니다.
모든 근로자가 필수적

1

거기에 있었다!! Guava를 사용하는 경우이를지도로 변환하는 빠른 방법은 다음과 같습니다.

Map<Integer,Foo> map = Maps.uniqueIndex(fooSet, Foo::getKey);

1

Iterator 클래스를 사용할 수 있습니다

import java.util.Iterator;
import java.util.HashSet;

public class MyClass {
 public static void main(String[ ] args) {
 HashSet<String> animals = new HashSet<String>();
animals.add("fox");
animals.add("cat");
animals.add("dog");
animals.add("rabbit");

Iterator<String> it = animals.iterator();
while(it.hasNext()) {
  String value = it.next();
  System.out.println(value);   
 }
 }
}

1

HashSet에서 n 번째 요소를 원한다면 아래 솔루션으로 갈 수 있습니다. 여기서 HashSet에 ModelClass의 객체를 추가했습니다.

ModelClass m1 = null;
int nth=scanner.nextInt();
for(int index=0;index<hashset1.size();index++){
    m1 = (ModelClass) itr.next();
    if(nth == index) {
        System.out.println(m1);
        break;
    }
}

1

구현의 처음 몇 줄을 보면 java.util.HashSet다음을 볼 수 있습니다.

public class HashSet<E>
    ....
    private transient HashMap<E,Object> map;

그래서 HashSet사용 HashMapinterally 어쨌든, 어떤 수단 당신은 그냥 사용하는 경우 그 HashMap직접 일부 메모리 키와 원하는 효과를 얻을 것이다 값으로 직접 저장 같은 값을 사용합니다.


1

사용하기에 적합한 객체 는 구아바 의 Interner 입니다.

다른 불변의 형태의 경우, String.intern ()와 동등한 동작을 제공합니다. 일반적인 구현은 Interners 클래스에서 제공됩니다.

또한 concurrencyLevel 또는 사용 된 참조 유형과 같은 매우 흥미로운 레버가 있습니다 (WeakInterner보다 더 유용한 SoftInterner를 제공하지 않는다는 점에 주목할 가치가 있습니다).


0

Set의 특정 구현은 임의 액세스 일 수도 있고 아닐 수도 있기 때문 입니다.

당신은 항상 얻을 수 반복자 반복자 '를 사용하여, 설정을 통해 단계를 next()당신이 동일한 요소를 발견하면 원하는 결과를 반환하는 방법을. 이것은 구현에 관계없이 작동합니다. 구현이 랜덤 액세스가 아닌 경우 (링크 된 목록이 지원되는 Set 그림), get(E element)인터페이스 의 메소드는 콜렉션을 반복하여 리턴 할 요소를 찾아야하기 때문에 기만적 get(E element)입니다. 필요한 경우 Set이 요소로 직접 이동할 수 있습니다.

contains() 물론 구현에 따라 동일한 일을해야 할 수도 있고 아닐 수도 있지만 이름은 같은 종류의 오해를 빌려주는 것처럼 보이지 않습니다.


2
get () 메소드가하는 것은 이미 contains () 메소드에 의해 수행되고 있습니다. 포함 된 객체를 가져 와서 .equals () 메서드를 호출하지 않으면 contains ()를 구현할 수 없습니다. API 디자이너는 일부 구현에서는 속도가 느리더라도 get ()을 java.util.List에 추가하는 것에 대한 자격이없는 것 같습니다.
Bryan Rink

나는 이것이 사실이라고 생각하지 않습니다. 두 객체는 ​​같음을 통해 같을 수 있지만 ==를 통해 같을 수는 없습니다. 객체 A와 객체 B를 포함하는 세트 S와 A.equals (B)를 포함하지만 A! = B가 있고 B에 대한 참조를 얻고 자하는 경우 S.get (A)를 호출하여 참조를 얻을 수 있습니다. B, 당신이 List의 get 메소드의 의미론을 가진 get 메소드를 가지고 있다고 가정하면, S.contains (A)를 검사하는 것과는 다른 유스 케이스입니다. 이것은 컬렉션에 대한 드문 사용 사례가 아닙니다.
Tom Tresansky

0

예, HashMap그러나 ...하지만 특수한 방법으로 사용하십시오 HashMap. 의사 로 사용하려고 할 때 예상되는 함정 Set은의 "실제"요소 Map/Set와 "후보"요소, 즉 equal요소가 이미 존재합니다. 이것은 절대로 멀지 않지만 함정에서 멀어지게합니다.

class SelfMappingHashMap<V> extends HashMap<V, V>{
    @Override
    public String toString(){
        // otherwise you get lots of "... object1=object1, object2=object2..." stuff
        return keySet().toString();
    }

    @Override
    public V get( Object key ){
        throw new UnsupportedOperationException( "use tryToGetRealFromCandidate()");
    }

    @Override
    public V put( V key, V value ){
       // thorny issue here: if you were indavertently to `put`
       // a "candidate instance" with the element already in the `Map/Set`: 
       // these will obviously be considered equivalent 
       assert key.equals( value );
       return super.put( key, value );
    }

    public V tryToGetRealFromCandidate( V key ){
        return super.get(key);
    }
}

그런 다음이 작업을 수행하십시오.

SelfMappingHashMap<SomeClass> selfMap = new SelfMappingHashMap<SomeClass>();
...
SomeClass candidate = new SomeClass();
if( selfMap.contains( candidate ) ){
    SomeClass realThing = selfMap.tryToGetRealFromCandidate( candidate );
    ...
    realThing.useInSomeWay()...
}

그러나 ... 이제 candidate프로그래머가 실제로 즉시 넣지 않는 한 어떤 식 으로든 자폭 Map/Set하기 contains를 원합니다 ... 당신 은 그것이 "분석"에 candidate합류하지 않으면 그것을 사용하도록 "오염" 하고 싶습니다. Map". 아마도 SomeClass새로운 Taintable인터페이스를 구현할 수있을 것 입니다.

보다 만족스러운 솔루션은 아래와 같이 GettableSet 입니다. 그러나 이것이 작동하려면 SomeClass모든 생성자를 볼 수 없게 만들거나 래퍼 클래스를 디자인하고 사용할 수있는 디자인을 담당해야합니다.

public interface NoVisibleConstructor {
    // again, this is a "nudge" technique, in the sense that there is no known method of 
    // making an interface enforce "no visible constructor" in its implementing classes 
    // - of course when Java finally implements full multiple inheritance some reflection 
    // technique might be used...
    NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet );
};

public interface GettableSet<V extends NoVisibleConstructor> extends Set<V> {
    V getGenuineFromImpostor( V impostor ); // see below for naming
}

이행:

public class GettableHashSet<V extends NoVisibleConstructor> implements GettableSet<V> {
    private Map<V, V> map = new HashMap<V, V>();

    @Override
    public V getGenuineFromImpostor(V impostor ) {
        return map.get( impostor );
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public boolean contains(Object o) {
        return map.containsKey( o );
    }

    @Override
    public boolean add(V e) {
        assert e != null;
        V result = map.put( e,  e );
        return result != null;
    }

    @Override
    public boolean remove(Object o) {
        V result = map.remove( o );
        return result != null;
    }

    @Override
    public boolean addAll(Collection<? extends V> c) {
        // for example:
        throw new UnsupportedOperationException();
    }

    @Override
    public void clear() {
        map.clear();
    }

    // implement the other methods from Set ...
}

귀하의 NoVisibleConstructor클래스는 다음과 같이 :

class SomeClass implements NoVisibleConstructor {

    private SomeClass( Object param1, Object param2 ){
        // ...
    }

    static SomeClass getOrCreate( GettableSet<SomeClass> gettableSet, Object param1, Object param2 ) {
        SomeClass candidate = new SomeClass( param1, param2 );
        if (gettableSet.contains(candidate)) {
            // obviously this then means that the candidate "fails" (or is revealed
            // to be an "impostor" if you will).  Return the existing element:
            return gettableSet.getGenuineFromImpostor(candidate);
        }
        gettableSet.add( candidate );
        return candidate;
    }

    @Override
    public NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet ){
       // more elegant implementation-hiding: see below
    }
}

PS 그러한 NoVisibleConstructor클래스의 기술적 문제 : 그러한 클래스는 본질적 final으로 바람직하지 않을 수 있습니다. 실제로 항상 더미 매개 변수가없는 protected생성자를 추가 할 수 있습니다 .

protected SomeClass(){
    throw new UnsupportedOperationException();
}

... 적어도 서브 클래스를 컴파일 할 수 있습니다. 그런 다음 getOrCreate()서브 클래스에 다른 팩토리 메소드 를 포함해야하는지 여부를 고려해야합니다.

마지막 단계 는 세트 멤버에 대한 추상 기본 클래스 (목록의 경우 "NB"요소, 세트의 "멤버")입니다 (가능한 경우 클래스가 제어 할 수없는 랩퍼 클래스 사용 범위 , 최대 구현 숨기기를 위해 이미 기본 클래스 등이 있습니다.

public abstract class AbstractSetMember implements NoVisibleConstructor {
    @Override
    public NoVisibleConstructor
            addOrGetExisting(GettableSet<? extends NoVisibleConstructor> gettableSet) {
        AbstractSetMember member = this;
        @SuppressWarnings("unchecked") // unavoidable!
        GettableSet<AbstractSetMembers> set = (GettableSet<AbstractSetMember>) gettableSet;
        if (gettableSet.contains( member )) {
            member = set.getGenuineFromImpostor( member );
            cleanUpAfterFindingGenuine( set );
        } else {
            addNewToSet( set );
        }
        return member;
    }

    abstract public void addNewToSet(GettableSet<? extends AbstractSetMember> gettableSet );
    abstract public void cleanUpAfterFindingGenuine(GettableSet<? extends AbstractSetMember> gettableSet );
}

... 사용법은 (당신의 내부에 매우 분명하다 SomeClassstatic팩토리 메소드) :

SomeClass setMember = new SomeClass( param1, param2 ).addOrGetExisting( set );

0

해시 코드의 계약은 다음을 분명히합니다.

"Object 메소드에 따라 두 오브젝트가 동일한 경우 두 오브젝트 각각에서 hashCode 메소드를 호출하면 동일한 정수 결과가 생성되어야합니다."

그래서 당신의 가정 :

"명확하게하기 위해 equals 메소드는 재정의되었지만 필드 중 하나만 검사하지는 않습니다. 따라서 동일하다고 간주되는 두 Foo 객체는 실제로 다른 값을 가질 수 있으므로 foo를 사용할 수 없습니다."

잘못되어 계약을 위반하고 있습니다. Set 인터페이스의 "contains"메소드를 보면 다음과 같습니다.

부울 포함 (오브젝트 o);
이 세트에 지정된 요소가 포함되어있는 경우에 true를 리턴합니다. 보다 공식적으로,이 세트에 o == null? e == 널 : o.equals (e)

원하는 것을 달성하기 위해 맵을 사용하여 키를 정의하고 객체가 서로 다르거 나 같은 방식을 정의하는 키로 요소를 저장할 수 있습니다.


-2

이 상황을 해결할 수있는 빠른 도우미 방법 :

<T> T onlyItem(Collection<T> items) {
    if (items.size() != 1)
        throw new IllegalArgumentException("Collection must have single item; instead it has " + items.size());

    return items.iterator().next();
}

8
이 답변이 질문에 대답하지 않거나 어떤 식 으로든 해결하려고 시도하지 않았기 때문에 많은 찬사를 받았습니다.
David Conrad

-2

다음은 접근 방법이 될 수 있습니다

   SharedPreferences se_get = getSharedPreferences("points",MODE_PRIVATE);
   Set<String> main = se_get.getStringSet("mydata",null);
   for(int jk = 0 ; jk < main.size();jk++)
   {
      Log.i("data",String.valueOf(main.toArray()[jk]));
   }

-2

배열을 사용해보십시오 :

ObjectClass[] arrayName = SetOfObjects.toArray(new ObjectClass[setOfObjects.size()]);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.