Programing

세트에서 요소 얻기

lottogame 2020. 3. 19. 08:15
반응형

세트에서 요소 얻기


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.


요소가 같으면 요소를 가져올 필요가 없습니다. 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);
    }
}

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

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

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

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

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


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

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

같은 물건을 가지고 있다면 왜 세트의 물건이 필요합니까? 키만으로 "동일" 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);

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

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

등호 구현을 재정의하면 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
}

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

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

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


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

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

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


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);

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


왜:

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

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

로부터 JavaDoc을

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

어떻게:

Streams가 소개되었으므로 다음을 수행 할 수 있습니다.

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

이러한 목적으로 Java HashMap 객체를 사용하는 것이 좋습니다. http://download.oracle.com/javase/1,5.0/docs/api/java/util/HashMap.html


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

http://pastebin.com/Qv6S91n9

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


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

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

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);   
 }
 }
}

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;
    }
}

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

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

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


사용하기에 적절한 객체 인 guava Interner 인 것 같습니다.

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

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


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);
    }
}

출력 :
항목 하나, 둘


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

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

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


예, 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 );

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

<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();
}

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

   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]));
   }

배열을 사용해보십시오 :

ObjectClass[] arrayName = SetOfObjects.toArray(new ObjectClass[setOfObjects.size()]);

참고 URL : https://stackoverflow.com/questions/7283338/getting-an-element-from-a-set

반응형