source

세트에서 요소 가져오기

factcode 2022. 8. 16. 23:18
반응형

세트에서 요소 가져오기

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.

정확한 질문에 답하기 위해 " 안 하겠어?Set다른 요소와 동일한 요소를 얻기 위한 작업을 제공합니까?"라는 질문에 대한 답변은 다음과 같습니다. 컬렉션 프레임워크의 설계자가 매우 미래 지향적이지 않았기 때문입니다.사례를 ( "추상화 한 항목을 .get()★★★★★★ 。

"그럼 요소를 얻는 방법"이라는 암묵적인 질문으로 넘어가겠습니다.제 생각에 가장 좋은 해결책은Map<E,E>Set<E>, 요소를 매핑합니다.이렇게 하면 "set"에서 요소를 효율적으로 검색할 수 있습니다. 왜냐하면 get() 메서드는Map는 효율적인 해시 테이블 또는 트리 알고리즘을 사용하여 요소를 검색합니다.한 '이행', '이행', '이행', ''을 쓸 수 .Set이 됩니다.get(), " "Map.

제 의견으로는 다음과 같은 답변이 있습니다.

"이미 동등한 오브젝트가 있기 때문에 요소를 얻을 필요가 없습니다.": 질문에서 이미 나타난 바와 같이 주장은 틀렸습니다.동일한 두 개체는 여전히 개체 평등과 관련이 없는 다른 상태를 가질 수 있습니다.는 이 입니다.Set 「, 「, 「, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, ,, , ,,

것라고 하는 것은, 큰 으로는 「반복하다」라고 하는 의미)에서는 입니다.Set효율적으로 쿼리할 수 있는 해시 맵 또는 트리로 구성됩니다). 마세요! 에서 이으로써 심각한 .이러한 접근방식을 사용하여 실제 시스템에서 심각한 성능 문제를 경험해 왔습니다. 에 실종된 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★get()메서드는 작업하는 것이 다소 번거롭지는 않지만, 대부분의 프로그래머는 의미에 대해 생각하지 않고 선형 검색 방식을 사용할 것입니다.

을 사용법 a.Map이 사용 예에 더 적합합니다.


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

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

동등한 오브젝트가 있으면 왜 세트에 있는 오브젝트가 필요합니까?는 경우에는 '같다'는 뜻이 됩니다.Map더 나은 선택이 될 거야

어쨌든, 다음과 같이 처리됩니다.

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

Java 8 에서는, 이것은 1개의 라이너가 됩니다.

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

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

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

David Ogren이 올바르게 기술한 바와 같이 동등하지 않은 객체가 "동일"하도록 동등 구현을 재정의하면 유지관리 문제가 쉽게 발생할 수 있습니다.

그리고 (많은 사람들이 제안한 것처럼) 지도를 명시적으로 대체하기 위해 imho를 사용하면 코드가 덜 우아해집니다.

세트에 포함된 요소의 원래 인스턴스에 액세스하는 것이 목표인 경우(사용 사례를 올바르게 이해했는지 확인) 다른 가능한 해결 방법이 있습니다.


저는 개인적으로 자바와 함께 클라이언트 서버 비디오 게임을 개발하면서 같은 요구를 가지고 있었습니다.제 경우, 각 클라이언트는 서버에 저장된 컴포넌트의 복사본을 가지고 있었는데, 문제는 클라이언트가 서버의 오브젝트를 수정해야 할 때마다 발생했습니다.

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

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

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

그러나 유사한 사용 사례에서 Map을 직접 사용하는 것은 여전히 부적절했다.어떤 사람은 지도를 사용하는 것이 더 복잡하다고 주장할 수 있다.

이러한 이유로 저는 Map을 "투명하게" 사용할 수 있는 MagicSet이라는 라이브러리를 개발자에게 구현했습니다.

https://github.com/ricpacca/magicset


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

또한 MagicSet은 세트로 정확하게 사용할 수 있지만 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>: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()은 NoSuchElement를 슬로우하므로 주의해 주십시오.예외 또는 옵션 항목을 조작할 수 있습니다.

로 변환한 ""을 사용합니다.get

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

이유:

비교 수단을 제공하는 데 있어 세트가 유용한 역할을 하는 것 같습니다.중복된 요소를 저장하지 않도록 설계되었습니다.

이러한 의도/설계 때문에 저장된 객체에 대한 참조를 가져오고 변환하면 Set의 설계 의도가 방해되어 예기치 않은 동작을 일으킬 수 있습니다.

JavaDocs에서

가변 객체가 세트 요소로 사용되는 경우 매우 주의해야 합니다.개체가 집합의 요소인 동안 개체 값이 동일한 비교에 영향을 미치는 방식으로 변경되는 경우 집합의 동작이 지정되지 않습니다.

방법:

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

mySet.stream()
.filter(object -> object.property.equals(myProperty))
.findFirst().get();
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);

1개의 get만 실행하면 모든 요소를 루프하기 때문에 퍼포먼스가 저하되지만 큰 세트에서 여러 개의 검색을 수행할 경우 그 차이를 알 수 있습니다.

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

알고 있습니다. 이미 오래 전에 질문과 답변이 있었습니다만, 관심이 있는 분은 HashMap에서 지원하는 커스텀 세트 클래스를 다음에 제시하겠습니다.

http://pastebin.com/Qv6S91n9

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

그거 해봤어!!Guava를 사용하는 경우 지도에 빠르게 변환하는 방법은 다음과 같습니다.

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

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;

★★★★★★★★★★★★★★★★★.HashSetHashMap, '어쨌든'을 하면 '어쨌든'이 .HashMap키와 같은 값을 직접 사용하면 원하는 효과를 얻을 수 있고 메모리도 절약할 수 있습니다.

적절한 오브젝트는 guava의 Interner인 것 같습니다.

기타 불변의 유형에 대해 String.intern()과 동등한 동작을 제공합니다.일반적인 구현은 Interners 클래스에서 이용할 수 있습니다.

또한 concurency Level이나 사용되는 참조 유형 등 몇 가지 매우 흥미로운 레버도 있습니다(WeakInterner보다 유용한 SoftInterner는 제공하지 않습니다).

특정 세트의 실장은 랜덤액세스일 수도 있고 아닐 수도 있기 때문입니다.

반복기를 사용하여 언제든지 반복기를 입수하고 세트를 단계별로 진행할 수 있습니다.next() 후 결과를 반환합니다.이것은 실장에 관계없이 동작합니다. 세트는, 「」( 「」)입니다.get(E element)의 메서드는이는 입니다.반환할 요소를 찾기 위해서는 수집을 반복해야 하기 때문입니다.get(E element)세트(Set)가 직접 요소(Element)에 뛰어들어 얻을 수 있다는 것을 암시하는 것 같습니다.

contains()물론 구현에 따라서는 같은 일을 해야 할 수도 있고 하지 않을 수도 있지만, 그 이름이 같은 종류의 오해를 불러일으키지는 않는 것 같습니다.

,, 용을 사용합니다.HashMap하지만 특별한 방법으로: 내가 예측한 함정은, 이 함정을 사용하려고 할 때,HashMap Set'아까운'이 생길 수 있는 .Map/Set "sublic"" , "sublic" 요소, "sublic" 요소, "sublic" 요소, "sublic" 요소, "sublic" 요소, "sublic" 요소, "sublic" , "sublic" 요소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헤마아마 당신은SomeClassTaintable인터페이스입니다.

보다 만족스러운 솔루션은 다음과 같은 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 "element", 세트에 대해서는 "member")입니다.

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

은 매우...).사용방법은 매우 명확합니다(예:SomeClass의 »static'이것'은 다음과 같습니다.

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

해시 코드의 계약에는, 다음의 사항이 명시되어 있습니다.

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

그렇다면 다음과 같이 가정합니다.

"구체적으로 말하면 equals 메서드는 덮어쓰지만 필드 중 하나만 체크하고 모든 필드는 체크하지 않습니다.따라서 같은 것으로 간주되는 두 Foo 객체는 실제로 다른 값을 가질 수 있습니다.그래서 foo만 사용할 수 없습니다.

잘못되었고 당신은 계약을 위반하고 있습니다.Set interface의 "contains" 메서드를 살펴보면 다음과 같습니다.

contains(o), boolean contains(오브젝트 o),
이 집합에 지정된 요소가 포함되어 있으면 true를 반환합니다.더 형식적으로, 는이== o라고 하는 e"가 포함되어 있는 합니다. 예를 들어 == == == e? o.e(e)는 true를 반환합니다.

원하는 것을 달성하려면 맵을 사용하여 키를 정의하고 오브젝트가 서로 어떻게 다르거나 동일한지 정의하는 키를 사용하여 요소를 저장할 수 있습니다.

'가 때 할수은 다음과 .NavigableSet a (a)TreeSet

public static <E> E get(NavigableSet<E> set, E key) {
    return set.tailSet(key, true).floor(key);
}

일이 좀 더 까다롭다HashSet그리고 그 후손들은LinkedHashSet:

import java.util.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {
    private static final Field mapField;
    private static final Method hashMethod;
    private static final Method getNodeMethod;
    private static final Field keyField;
    static {
        try {
            mapField = HashSet.class.getDeclaredField("map");
            mapField.setAccessible(true);
            hashMethod = HashMap.class.getDeclaredMethod("hash", Object.class);
            hashMethod.setAccessible(true);
            getNodeMethod = HashMap.class.getDeclaredMethod("getNode",
                    Integer.TYPE, Object.class);
            getNodeMethod.setAccessible(true);
            keyField = Class.forName("java.util.HashMap$Node").getDeclaredField("key");
            keyField.setAccessible(true);
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }

    public static <E> E get(HashSet<E> set, E key) {
        try {
            Object map = mapField.get(set);
            Object hash = hashMethod.invoke(null, key);
            Object node = getNodeMethod.invoke(map, hash, key);
            if (node == null)
                return null;
            @SuppressWarnings("unchecked")
            E result = (E)keyField.get(node);
            return result;
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }

    public static <E> E get(NavigableSet<E> set, E key) {
        return set.tailSet(key, true).floor(key);
    }

    public static void main(String[] args) {
        HashSet<Integer> s = new HashSet<>();
//      HashSet<Integer> s = new LinkedHashSet<>();
//      TreeSet<Integer> s = new TreeSet<>();
        for (int i = 0; i < 100_000; i++)
            s.add(i);
        Integer key = java.awt.event.KeyEvent.VK_FIND;
        Integer hidden = get(s, key);
        System.out.println(key);
        System.out.println(hidden);
        System.out.println(key.equals(hidden));
        System.out.println(key == hidden);
    }
}

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

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

배열을 사용해 보십시오.

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

다음과 같은 방법이 있습니다.

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

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

반응형