java.util.Setにget(int index)がないのはなぜですか?


237

正当な理由があると私は確信していjava.util.Setますがget(int Index)、誰かがインターフェイスやその他の同様のget()方法が欠けている理由を誰かに説明してもらえますか?

セットは物を入れるのに最適のようですが、そこから1つのアイテムを取得するためのエレガントな方法が見つかりません。

最初のアイテムが必要なことがわかっている場合はを使用できますがset.iterator().next()、それ以外の場合は、特定のインデックスのアイテムを取得するために配列にキャストする必要があるようです。

セットからデータを取得する適切な方法は何ですか?(イテレータを使用する以外)

それがAPIから除外されているという事実は、これを行わないことには十分な理由があることを意味します-誰かが私を啓発してくれませんか?

編集: ここでいくつかの非常に素晴らしい答え、そしていくつかは「より多くのコンテキスト」と言っています。特定のシナリオはdbUnitテストで、クエリから返されたセットには1つのアイテムしかないと合理的に主張でき、そのアイテムにアクセスしようとしました。

ただし、問題はより集中しているため、シナリオがなくてもより有効です。

セットとリストの違いは何ですか

以下の素晴らしい答えをありがとう。


1
インデックスによってセットから要素を取得するのはなぜですか?セットをソートされた配列として使用しようとしていますか?
MSN

ここでの特定のインスタンスは、休止状態の呼び出しから返されたセットに対するdbUnitテストです。私のテストでは、設定に使用したIDataSetが原因で、返されたオブジェクトが特定の順序であると(私がアサートしているため)想定するのは妥当です。これは一般的ではないケースですが、APIについての私の好奇心につながります。
Marty Pitt、

1
特定の順序で追加しても、カスタムのSet実装を使用している場合を除いて、それらがそのまま残るわけではありません。
マイケルマイヤーズ

1
「最初のアイテムが必要なことがわかっている場合は、set.iterator()。next()を使用できます」-この行は実際には意味がありません。あなたは本当に「私が最初のアイテムが欲しいのを知っているなら、最初のアイテムの実装の定義によって、私はできる...」と言っています。セット自体は順序付けされていないため、インデックス付きアクセスは意味がありません。ArrayListSetがあった場合、それはもっと理にかなっています( "List"にキャストして幸せになるだけです)。おそらく、あなたは質問に対してより多くのコンテキストを与えることができますか?
jsight 2009

セットは順不同ではありません!特定の実装がありますが、一部の実装は特定の方法で明示的に順序付けられています。
reinierpost 2015

回答:


176

セットには順序がないため。一部の実装(特にjava.util.SortedSetインターフェースを実装するもの)は行いますが、これはセットの一般的なプロパティではありません。

この方法でセットを使用する場合は、代わりにリストを使用することを検討してください。


10
@matt b:いいえ、彼はそれを考慮すべきだと思います。思考は良いです。;)
マイケルマイヤーズ

10
それを考慮して、それを実行してください。
ジョー・フィリップス

21
「検討」は正しい表現です。考えられる問題は2つあります。(a)他のものを使用する必要があるときにセットを使用している、または(b)サポートしていないが別の方法で実行できるセットを使用しようとしている。これらのうちどれが当てはまるかを検討することは良いことです。
kenj0418 2009

6
より簡単な答えは、ソートされたセットを使用することです。(私は、セットを選択する際に一意性が役割を果たすと想定しています)。しかし、私には質問があり、SortedSetが順序付けされているため、APIにgetメソッドがないのはなぜですか。
uncaught_exceptions

5
@HDave:いいえ、データ構造の複数の実装がプロパティを共有するという事実は、それをデータ構造自体のプロパティにするわけではありません。Listの3つの一般的に使用される実装(ArrayListとVector)の2つはランダムアクセスですが、ランダムアクセスはListのプロパティにはなりません。
マイケル・マイヤーズ

74

実際、これは、オブジェクトリレーショナルマッピングを使用するJavaEEアプリケーション(Hibernateなど)を作成する際に繰り返し発生する問題です。そして、ここで回答したすべての人々から、アンドレアスピーターソンだけが本当の問題を理解し、それに正しい答えを出しました。JavaにはUniqueListがありません!(または、それをOrderedSetまたはIndexedSetと呼ぶこともできます)。

マックスウィングはこのユースケース(順序付けされた一意のデータが必要)に言及し、SortedSetを提案しましたが、これはMarty Pittが本当に必要とするものではありません。

この「IndexedSet」はSortedSetと同じではありません-SortedSetでは、要素はコンパレータを使用して(または「自然な」順序を使用して)ソートされます。

ただし、その代わりに、要素が挿入されたのと同じ順序で要素が返されることが保証されるため、LinkedHashSet(他の人も提案)または(存在しない) "ArrayListSet"にさらに近くなります。

ただし、LinkedHashSetは実装であり、インターフェースではありません。IndexedSet(またはListSet、OrderedSet、UniqueList)インターフェイスが必要です。これにより、プログラマーは、特定の順序があり、重複のない要素のコレクションが必要であることを指定し、任意の実装(Hibernateによって提供される実装など)でインスタンス化できます。

JDKはオープンソースなので、おそらくこのインターフェースは最終的にJava 7に含まれるでしょう...


3
それに関しては素晴らしい答えですが、その間、私たちは何をしますか?
HDave

もちろんそうだ。以前はhibernateでmanytomanyおよびonetomany ORMとしてリストを使用していました。3つ以上の関連エンティティを含む左結合クエリで例外がスローされたときに、問題(または欠陥)が発生しました。詳細については、ここを参照してください(jroller.com/eyallupu/entry/…)。この問題を回避するには、ORMマッピングコレクションとしてsetを使用する必要があります。しかし正直なところ、setはプログラミングでのアクセスや、順序付けコレクションが必要な場合には不便です。私たちが本当に必要なのは、Sorin Postelnicuが言ったように「indexedset」、SORTおよびUNIQUE
horaceman '21

2
Apache Commons CollectionsにはListOrderedSet、7年前にOPに必要なものが(そして今日私が必要としていた)あります。
ポール

@Paul:それは確かに本当に良さそうなものです。残念ながら、それでも3つの欠点があります。1)クラスであり、インターフェースではありません。2)JDKにはありません。3)Hibernateクエリが返すものではありません。
Sorin Postelnicu 2016年

ええ、でもこれら3つの主要な欠点以外は完璧です!:)振り返ってみると、あなたの答えではなく質問にコメントを投稿する必要がありました-私はキーオフしWhat is needed is an IndexedSet (or ListSet, or OrderedSet, or UniqueList)...て無視しました...interface。申し訳ありません!
Paul

29

mmyersの回答で言及されていない点を1つ追加するだけです。

最初のアイテムが必要なことがわかっている場合は、set.iterator()。next()を使用できますが、それ以外の場合は、特定のインデックスでアイテムを取得するために配列にキャストする必要があるようです。

セットからデータを取得する適切な方法は何ですか?(イテレータを使用する以外)

また、SortedSetインターフェースについてよく理解する必要があります(最も一般的な実装はですTreeSet)。

SortedSetはSetです(つまり、要素は一意です)。要素の自然な順序付けによって、またはsomeを使用して順序付けされたままになりますComparator。メソッドfirst()last()メソッドを使用して、最初と最後のアイテムに簡単にアクセスできます。A SortedSetは、コレクションを複製せずに、特定の方法で順序付けしてコレクションを保持する必要がある場合に時々役に立ちます。

編集:要素が挿入順で保持されているセットが必要な場合(リストのように)、を見てくださいLinkedHashSet


私はLinkedHashSetが好きです。しかし、はい、これは言及に値します。+1
マイケルマイヤーズ

おかげで、私は答えを少し微調整しました。(TreeSetのいくつかの側面がLinkedHashSetの側面と混同されていたようです。)
Jonik 2009

25

この種のことは、いつセットを使用すべきか、いつリストを使用すべきかという疑問につながります。通常、アドバイスは次のとおりです。

  1. 順序付けられたデータが必要な場合は、リストを使用してください
  2. 一意のデータが必要な場合は、セットを使用します
  3. 両方が必要な場合は、SortedSet(コンパレーターによって順序付けられたデータの場合)またはOrderedSet / UniqueList(挿入によって順序付けられたデータの場合)のいずれかを使用します。残念ながら、Java APIにはまだOrderedSet / UniqueListがありません。

よくある4番目のケースは、どちらも必要ない場合です。この場合、リストを使用するプログラマーもいれば、セットを使用するプログラマーもいます。個人的には、順序を付けずにリストとして設定するのは非常に有害だと思います-それは本当に他のすべての獣だからです。セットの一意性やセットの等価性などが必要でない限り、常にリストを優先してください。


2
不特定の場合は、Collection <T>またはIterable <T>を受け入れ、リストとして初期化します。
Andreas Petersson、

これはバッグまたはマルチセットになります。しかし、Javaはそれらをサポートしていません。Collection <T>を直接使用する必要があると言われています。
メカニカルカタツムリ

4.一意でないデータが必要であり、順序を気にしない。セットは使用できません。リスト、バッグ、またはマルチセットが機能します。
Andrew Gallasch 2015年

17

誰かがこのように正確に綴ったかどうかはわかりませんが、次のことを理解する必要があります。

セットには「最初の」要素はありません。

他の人が言ったように、セットには順序がないからです。セットは、特に順序付けを含まない数学的概念です。

もちろん、コンピュータはメモリに順序付けされていないもののリストを実際に保持することはできません。注文が必要です。内部的には、配列またはリンクリストなどです。しかし、それが何であるかは本当にわかりませんし、実際には最初の要素もありません。「最初に」出てくる要素は偶然そのように出て来て、次に最初ではないかもしれません。特定の最初の要素を「保証」するための措置を講じたとしても、たまたまSetの1つの特定の実装に正しく適用されただけなので、偶然にそれが出てきています。別の実装は、あなたがしたことではそのように機能しない可能性があります。そして実際には、自分が使用していると思っているだけでなく、使用している実装もわからない場合があります。

人々はこのすべてに遭遇します。THE。時間。RDBMSシステムで理解していない。RDBMSクエリは一連のレコードを返します。これは数学のセットと同じタイプです。項目の順序付けられていないコレクションです。この場合のみ、項目はレコードです。ORDER BY句を使用しない限り、RDBMSクエリの結果には順序が保証されていませんが、常にそうであると想定し、データまたはコードの形状がわずかに変化してクエリオプティマイザが機能するようにトリガーされたときにいつかトリップします。別の方法で、突然、期待どおりの結果が得られません。これらは通常、クエリの結果の順序が保証されていないことが前もって説明されたときに、データベースクラスで(またはドキュメントやチュートリアルを読んで)注意を払っていなかった人々です。


もちろん、順序は通常、コードが本稼働に入るとすぐに変化します。遅すぎると、インデックスを追加してクエリを高速化します。これでコードは高速に実行されますが、間違った答えを出します。そして、運が良ければ、3〜4日間は誰も気づきません。運が悪いと、1か月間誰も気付かない...
TMN

私は彼がそれを逃したとは思わない(多分彼は記法でずさんな)。彼はセットの最初の要素を望んでおらず、セットの任意の要素を望んでいます。以来、あなたは彼に任意の要素を与えることができSetていますIterable
Elazar Leibovich、2011

インデックスによるget(index)について話しています。同等のget(Object)はどうですか?
Kumar Manish 2017

10

一部のデータ構造が標準のJavaコレクションから欠落しています。

バッグ(セットのようですが、要素を複数回含めることができます)

UniqueList(順序付きリスト、各要素を1回だけ含めることができます)

この場合、uniquelistが必要になるようです

柔軟なデータ構造が必要な場合は、Googleコレクションに興味があるかもしれません


1
Guvaは「UniqueList」を提供していますか?
Mike Rylander 2013

いいえ。ただし、同様のプロパティを持つjava.util.LinkedHashSetを使用できます。
Andreas Petersson

7

そうです、Setコレクションの定義により、Setの要素は順序付けされていません。したがって、インデックスからアクセスすることはできません。

しかし、なぜパラメーターとしてインデックスを提供するのではなく、get(object)メソッドではなく、探しているオブジェクトと等しいオブジェクトがないのでしょうか。このようにして、equalメソッドで使用されている属性を知るだけで、Set内の要素のデータにアクセスできます。


7

セット内のインデックスによって多数のランダムアクセスを行う場合は、その要素の配列ビューを取得できます。

Object[] arrayView = mySet.toArray();
//do whatever you need with arrayView[i]

ただし、主な欠点が2つあります。

  1. セット全体の配列を作成する必要があるため、メモリ効率が良くありません。
  2. セットが変更されると、ビューは廃止されます。

5

これは、Setが一意性を保証するだけで、最適なアクセスまたは使用パターンについては何も言われていないためです。つまり、セットはリストまたはマップであり、それぞれ非常に異なる検索特性を持っています。


5

セットで数値インデックスを使用するために考えられる唯一の理由は、反復のためです。そのためには、

for(A a : set) { 
   visit(a); 
}

真実ではありません、ランダム要素にアクセスするのはどうですか?
ジェレミー・サルウェン2009

ハ、ハ。良い点:)しかし、それは誤用される可能性が高いでしょう、私は確信しています。
ヒューゴ

3

インデックスを介してアクセスできる並べ替えセットが実際に必要な状況に遭遇しました(他の投稿者と同じですが、インデックスを使って並べ替えられていないセットにアクセスしても意味がありません)。例としては、子を並べ替えたいツリーがあり、子の重複は許可されていません。

それらを表示するためにインデックスを介してアクセスする必要があり、設定された属性が重複を効率的に排除するのに役立ちました。

java.utilまたはgoogleコレクションで適切なコレクションが見つからないため、自分で実装するのは簡単でした。基本的な考え方は、インデックスを介したアクセスが必要な場合にSortedSetをラップしてリストを作成することです(そしてSortedSetが変更されるとリストを忘れます)。もちろん、これはラップされたSortedSetを変更し、リストへのアクセスがコレクションの有効期間中に分離されている場合にのみ効率的に機能します。それ以外の場合は、頻繁にソートされる、つまり遅すぎるリストのように動作します。

子の数が多いため、Collections.sortでソートしたリストよりもパフォーマンスが大幅に向上しました。


2

インデックスを介してアクセスできるのは、2つの基本的なデータ構造のみです。

  • 配列データ構造はO(1)get(int index)操作を実現するために時間の複雑さを伴うインデックスを介してアクセスできます。
  • LinkedListデータ構造は、インデックスを介してアクセスすることもできますが、操作O(n)を実現するための時間は複雑になりget(int index)ます。

Javaでは、配列データ構造ArrayListを使用して実装されます。

しながら、設定データ構造は、通常、を介して実現することができるハッシュテーブル/ハッシュマップまたはBalancedTreeデータ構造、高速要素が存在するかどうかを検出し、存在しない要素を追加するため、通常よく実装セットは達成することができO(1)、時間複雑度のcontains動作を制御します。Javaでは、SetのHashSet最も一般的に使用される実装であり、API を呼び出すことで実装され、リンクリストとの個別のチェーンを使用して実装されますHashMapHashMap ArrayLinkedListの)との。

Setは異なるデータ構造を介して実装できるため、そのためのget(int index)メソッドはありません。


フィンガーツリー(HaskellのData.Sequence.lookup関数を参照)は、インデックスを介してアクセスすることもできます(中央O(1)付近の端O(log n)近く、より正確にO(min(log(k), log(n-k))))。また、バイナリツリーも同様にアクセスできます(HaskellのData.Set.lookupIndex関数を参照)。したがって、「インデックス経由でアクセスできるのは2つの基本的なデータ構造のみであることに注意してください」という最初の主張は正しくありません。
セミコロン

1

Set インターフェースを使用する理由 getインデックスタイプの呼び出しがない first()やlast()などのさらに基本的なものがない理由は、あいまいな操作であるため、危険な操作になる可能性があるためです。メソッドがSetを返し、first()メソッドを呼び出すと、ジェネリックSetが順序を保証しない場合、期待される結果は何ですか?結果のオブジェクトは、メソッドの呼び出しごとに非常に異なる可能性があります。そうでない場合は、使用しているライブラリがその下の実装を変更し、すべてのコードが壊れていることがわかるまで、誤った安心感に陥る可能性があります。特に理由はありません。

ここにリストされている回避策に関する提案は良いです。インデックス付きアクセスが必要な場合は、リストを使用してください。ジェネリックセットでイテレータまたはtoArrayを使用する場合は注意が必要です。これは、a)順序付けが保証されておらず、b)後続の呼び出しや基になる実装によって順序付けが変更されないという保証がないためです。間に何かが必要な場合は、SortedSetまたはLinkedHashSetが必要です。

// Setインターフェースにget-random-elementがあればいいのに。


1

java.util.Set注文されていないアイテムのコレクションです。Setにget(int index)がある場合は意味がありません。Setにはインデックスがなく、値を推測することしかできないためです。

これが本当に必要な場合は、Setからランダムな要素を取得するメソッドをコーディングします。


0

できるよ new ArrayList<T>(set).get(index)


これはセットのリストを返し、get(index)はセットを返します。むしろ、私は使用しました: new ArrayList<T>(t).get(0) インデックスによってセットから特定の要素を取得するという考えには正当な反対があると思います。ただし、サイズ1のセットの場合、セットにonly()メンバー関数があり、セット内の唯一の要素に簡単にアクセスできるようにすると便利です。これにより、前述の、new ArrayListまたはfor (Foo foo : foos) { return foo; }
Doug Moscropが

0

セットがソートされることを気にしない場合は、indexed-tree-mapプロジェクトを調べてみてください。

拡張されたTreeSet / TreeMapは、インデックスによる要素へのアクセス、または要素のインデックスの取得を提供します。また、実装は、RBツリーのノードの重みの更新に基づいています。したがって、ここではリストによる繰り返しやバックアップはありません。


0

Setはインターフェースであり、その実装クラスの一部はHashSet、TreeSet、およびLinkedHashSetです。内部でHashMapを使用して値を格納します。HashMapは順序を保持しないため、インデックスで値を取得することはできません。

HashMapはキーと値のペアを格納しますが、Setは格納しないため、SetがHashMapをどのように使用しているかを考えている必要があります。有効な質問。Setで要素を追加すると、内部的に、キーがSetに入力する要素であり、値がダミー定数であるHashMapが維持されます。以下は、add関数の内部実装です。したがって、HashMapのすべてのキーは同じ定数値を持ちます。

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

すべてSetのsの実装はHashMap、内部で値を格納するために使用しており、その主張を実証できますTreeSetか?
greybeard

1
the keys in the HashMap will have the same constant value キーHashMapになるにマップ1と同じ不変Object
老い


-3

セットの要素を取得するには、次のいずれかを使用します。

public T getElement(Set<T> set, T element) {
T result = null;
if (set instanceof TreeSet<?>) {
    T floor = ((TreeSet<T>) set).floor(element);
    if (floor != null && floor.equals(element))
    result = floor;
} else {
    boolean found = false;
    for (Iterator<T> it = set.iterator(); !found && it.hasNext();) {
    if (true) {
        T current = it.next();
        if (current.equals(element)) {
        result = current;
        found = true;
        }
    }
    }
}
return result;
}

関数は質問が求めたものではありません。値ではなくインデックスが必要です。とにかくあなたの機能は何をしていますか?内部の要素と等しい場合は、単に要素を返すように見えます。これは何をしますか()が含まないのですか?
Janus Troelsen、2012

どこがT定義されていますか?なんでif (true)
クォンタム、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.