Javaでの順序セットの実装はありますか?


98

誰かがObjective-Cに精通している場合NSOrderedSetSetとして機能すると呼ばれるコレクションがあり、その項目にはArrayの項目としてアクセスできます。

Javaでこのようなものはありますか?

と呼ばれるコレクションがあると聞いたことがありますがLinkedHashMap、セットのようなコレクションは見つかりませんでした。


私はc ++で同様の問題に取り組んでいます。NSOrderedSetを使用して、挿入した順序で要素にアクセスできますか?
Vinay 2014年

C ++で上記の機能を取得する方法を知っていますか?つまり、SETとして機能し、配列の要素としてアクセスできますか?
Vinay 2014年

回答:


118

LinkedHashSetクラスを見てください。

Java docから

予測可能な反復順序を備えた、Setインターフェイスのハッシュテーブルとリンクリストの実装。この実装は、すべてのエントリで実行される二重リンクリストを維持するという点でHashSetとは異なります。このリンクされたリストは、要素がセットに挿入された順序(挿入順序)ある反復順序を定義します。要素がセットに再挿入されても、挿入順序は影響を受けないことに注意してください。(s.contains(e)が呼び出しの直前にtrueを返すときにs.add(e)が呼び出された場合、要素eはセットsに再挿入されます。)


どうもありがとうございました。ささいな見方をしLinkedHashMapているように見えますが、どういうわけかわかりません。
Uko


9
なぜこの回答が多くの賛成票を獲得するのですか?これは、質問に対する答えではありません。LinkedHashSet要素がどのインデックスにあるかを把握できる関数はありません。
searchengine27

31

すべてのセットにはiterator()があります。通常のHashSetの反復子は非常にランダムです。TreeSetはソート順でそれを行い、LinkedHashSet反復子は挿入順で反復します。

ただし、LinkedHashSetの要素を置き換えることはできません。1つを削除して別の要素を追加できますが、新しい要素は元の要素の代わりにはなりません。LinkedHashMapでは、既存のキーの値を置き換えても、値は元の順序のままです。

また、特定の位置に挿入することはできません。

おそらく、重複の挿入を避けるために、明示的なチェックを含むArrayListを使用する方がよいでしょう。


特定の位置に要素を設定/取得し、追加した順序で取得できるようにしたい。それLinkedHashSetを行うべき縫い目です。返信ありがとう
Uko

11

見てみましょうJavaの標準APIドキュメントを。のすぐ隣LinkedHashMapにがありLinkedHashSetます。ただし、これらの順序は挿入順序であり、要素の自然な順序ではないことに注意してください。そして、その順序でのみ反復でき、ランダムアクセスはできません(反復ステップを数えることを除く)。

およびSortedSetによって実装されたインターフェイスもあります。どちらも要素の自然順序またはでの反復を許可しますが、ランダムアクセスまたは挿入順序は許可しません。TreeSetConcurrentSkipListSetComparator

インデックスによる効率的なアクセスと設定基準の効率的な実装の両方が可能なデータ構造の場合、スキップリストが必要になりますが、Java標準APIにはその機能を備えた実装はありませんが、簡単に見つけることができます。インターネット上で。


私はあなたのコメントを誤解しているかもしれませんが、Java 1.6以降、スキップリスト(たとえば、ConcurrentSkipListSetなど)に基づいたいくつかのデフォルトコレクションが存在するという印象を受けました。
TacticalCoder

@ user988052:はい、しかしそれらはインデックスによるランダムアクセスを実装していません(スキップリストについての私の理解はそれが可能であるべきだと言っています)、これはUkoが望んでいるようです。
Michael Borgwardt 2012年

@MichaelBorgwardtのJava 6およびそれ以降のスキップリスト実装の対を含む:ConcurrentSkipListMapおよびConcurrentSkipListSet。どちらも自然順またはコンパレータに基づいてソートを維持します。それらがあなたが議論するランダムアクセスまたはエントリーの順序を提供するかどうかはわかりません。
バジルブルク2015

@BasilBourque:良い発見、そして編集に感謝します。OPはインデックスによるアクセスを望んでいたので、それを調べて考えたところ、スキップリストには実際にはその機能もないように思います...
Michael Borgwardt

5

これが正解です。LHSetとは異なり、TreeSet java.util.SortedSetを実装します。
vemv 2013

40
並べ替えと並べ替えは異なります。TreeSetは並べ替えられ、順序付けされていません
andrii

2
正確には、並べ替えは挿入順序(リストの動作)を指し、並べ替えはいくつかの基準に基づく要素の実際の順序を指します。
Cornel Masson

5

java.util.TreeSetその実装を使用してみてくださいSortedSet

ドキュメントを引用するには:

「要素は、使用されるコンストラクターに応じて、自然な順序を使用して、またはセットの作成時に提供されるコンパレーターによって順序付けられます。」

add、remove、containsには時間コストlog(n)があることに注意してください。

セットのコンテンツに配列としてアクセスする場合は、次のように変換できます。

YourType[] array = someSet.toArray(new YourType[yourSet.size()]); 

この配列は、TreeSetと同じ基準で(自然またはコンパレーターによって)ソートされます。多くの場合、これはArrays.sort()を実行する代わりに利点があります。


1
私は最初の要素を入れた場合はArrayListのEIのように発注する必要があるcとその要素a:私は同じ順番でそれらを取得したいコレクションのI反復として、caなど
Uko

1

ツリーセットは順序付けされたセットですが、アイテムインデックスを介してアクセスすることはできません。反復するか、最初/最後に移動するだけです。


treeSetを使用すると、コストが増加します。LinkedHashSetは低コストです。
カルロス

0

スキップリストの安価な実装について話している場合、大きなOの観点から、この操作のコストはどのくらいなのでしょうか。

YourType [] array = someSet.toArray(new YourType [yourSet.size()]);

つまり、常に配列全体の作成に行き詰まっているので、O(n)です。

java.util.Arrays#copyOf

1
これは、イテレーターのパフォーマンス特性size()と、基になるセットのメソッドによって異なります。反復は通常O(n)、サイズは通常、それO(1)ConcurrentSkipListSetどこにあるかを除いてですO(n)
Ian Roberts


0

Google Guavaのような双方向マップBiMapからユーティリティを取得することもできます

を使用するBiMapと、整数(ランダムインデックスアクセス用)を他のオブジェクトタイプにかなり効率的にマップできます。BiMapsは1対1なので、指定された整数には最大で1つの要素が関連付けられ、要素には1つの整数が関連付けられます。それは巧み2つのによって支えられていますHashTable、それはほぼ倍のメモリを使用していますので、インスタンスが、それはカスタムより多くの方が効率的ですListので、加工限り、contains()一定の時間です(アイテムは、それがすでに存在するかどうかを確認するために追加されたときに呼び出されました)等平行フレンドリーな操作HashSetのは、一方Listの実装では、LOT遅いです。


0

同様の問題がありました。順序付けされたセットはまったく必要ありませんでしたが、高速なindexOf/のリストがもっと必要でしたcontains。そこには何も見つからなかったので、自分で実装しました。これがコードで、との両方Setを実装していますが、Listすべての一括リスト操作がArrayListバージョンほど高速であるとは限りません。

免責事項:テストされていません

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.Collection;
import java.util.Comparator;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import static java.util.Objects.requireNonNull;

/**
 * An ArrayList that keeps an index of its content so that contains()/indexOf() are fast. Duplicate entries are
 * ignored as most other java Set's do.
 */
public class IndexedArraySet<E> extends ArrayList<E> implements Set<E> {

    public IndexedArraySet() { super(); }

    public IndexedArraySet(Iterable<E> c) {
        super();
        addAll(c);
    }

    private HashMap<E, Integer> indexMap = new HashMap<>();

    private void reindex() {
        indexMap.clear();
        int idx = 0;
        for (E item: this) {
            addToIndex(item, idx++);
        }
    }

    private E addToIndex(E e, int idx) {
        indexMap.putIfAbsent(requireNonNull(e), idx);
        return e;
    }

    @Override
    public boolean add(E e) {
        if(indexMap.putIfAbsent(requireNonNull(e), size()) != null) return false;
        super.add(e);
        return true;
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        return addAll((Iterable<? extends E>) c);
    }
    public boolean addAll(Iterable<? extends E> c) {
        boolean rv = false;
        for (E item: c) {
            rv |= add(item);
        }
        return rv;
    }

    @Override
    public boolean contains(Object e) {
        return indexMap.containsKey(e);
    }

    @Override

    public int indexOf(Object e) {
        if (e == null) return -1;
        Integer i = indexMap.get(e);
        return (i == null) ? -1 : i;
    }

    @Override
    public int lastIndexOf(Object e) {
        return indexOf(e);
    }

    @Override @SuppressWarnings("unchecked")
    public Object clone() {
        IndexedArraySet clone = (IndexedArraySet) super.clone();
        clone.indexMap = (HashMap) indexMap.clone();
        return clone;
    }

    @Override
    public void add(int idx, E e) {
        if(indexMap.putIfAbsent(requireNonNull(e), -1) != null) return;
        super.add(idx, e);
        reindex();
    }

    @Override
    public boolean remove(Object e) {
        boolean rv;
        try { rv = super.remove(e); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public void clear() {
        super.clear();
        indexMap.clear();
    }

    @Override
    public boolean addAll(int idx, Collection<? extends E> c) {
        boolean rv;
        try {
            for(E item : c) {
                // check uniqueness
                addToIndex(item, -1);
            }
            rv = super.addAll(idx, c);
        } finally {
            reindex();
        }
        return rv;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        boolean rv;
        try { rv = super.removeAll(c); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        boolean rv;
        try { rv = super.retainAll(c); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public boolean removeIf(Predicate<? super E> filter) {
        boolean rv;
        try { rv = super.removeIf(filter); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public void replaceAll(final UnaryOperator<E> operator) {
        indexMap.clear();
        try {
            int duplicates = 0;
            for (int i = 0; i < size(); i++) {
                E newval = requireNonNull(operator.apply(this.get(i)));
                if(indexMap.putIfAbsent(newval, i-duplicates) == null) {
                    super.set(i-duplicates, newval);
                } else {
                    duplicates++;
                }
            }
            removeRange(size()-duplicates, size());
        } catch (Exception ex) {
            // If there's an exception the indexMap will be inconsistent
            reindex();
            throw ex;
        }

    }

    @Override
    public void sort(Comparator<? super E> c) {
        try { super.sort(c); }
        finally { reindex(); }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.