誰かがObjective-Cに精通している場合、 NSOrderedSet
と呼ばれるコレクションがあり、Setとして機能し、そのアイテムには次のようにアクセスできますArrayの配列。
Javaにはこのようなものがありますか?
LinkedHashMap
と呼ばれるコレクションがあると聞いたことがありますが、セット用にそのようなものは見つかりませんでした。
LinkedHashSet classを見てください
すべてのセットにはiterator()があります。通常のHashSetの反復子は非常にランダムで、TreeSetはソート順で実行します。 LinkedHashSet 反復子は挿入順で反復します。
ただし、LinkedHashSetの要素を置き換えることはできません。あるものを削除して別の要素を追加することはできますが、新しい要素は元の場所にありません。 LinkedHashMapでは、既存のキーの値を置き換えることができますが、値は元の順序のままです。
また、特定の位置に挿入することはできません。
たぶん、重複の挿入を避けるために、明示的なチェックでArrayListを使用した方が良いでしょう。
Java標準APIドキュメント をご覧ください。 LinkedHashMap
のすぐ隣に LinkedHashSet
があります。ただし、それらの順序は挿入順序であり、要素の自然な順序ではないことに注意してください。そして、その順序でのみ反復でき、ランダムアクセスはできません(反復ステップをカウントする場合を除く)。
インターフェイスもあります SortedSet
TreeSet
および ConcurrentSkipListSet
によって実装されています。どちらも、要素の 自然順序 または Comparator
での反復を許可しますが、ランダムアクセスまたは挿入の順序は許可しません。
インデックスによる効率的なアクセスと、設定基準の効率的な実装の両方が可能なデータ構造の場合、 スキップリスト が必要ですが、Javaにはその機能を実装するものはありません。 _標準API。ただし、インターネットで簡単に見つけることができます。
Java.util.TreeSet
を実装する SortedSet
を使用してみてください。
ドキュメントを引用するには:
「要素は、使用されるコンストラクタに応じて、自然な順序付けを使用して、またはセット作成時に提供されるコンパレータによって順序付けられます」
追加、削除、および包含には時間コストlog(n)があることに注意してください。
セットのコンテンツに配列としてアクセスする場合は、次のようにして変換できます。
YourType[] array = someSet.toArray(new YourType[yourSet.size()]);
この配列は、TreeSetと同じ基準(自然またはコンパレーター)でソートされ、多くの場合、Arrays.sort()を実行する代わりに利点があります。
treeset は順序付けられたセットですが、アイテムインデックスを介してアクセスすることはできません。繰り返しまたは先頭/末尾に移動するだけです。
BiMap
from Google Guava のような双方向マップからユーティリティを取得することもできます
BiMap
を使用すると、整数(ランダムインデックスアクセス用)を他のオブジェクトタイプに非常に効率的にマップできます。 BiMap
sは1対1であるため、指定された整数には最大で1つの要素が関連付けられ、要素には整数が1つ関連付けられます。 2つのHashTable
インスタンスによって巧妙に支えられているため、ほぼ2倍のメモリを使用しますが、_contains()
(アイテムが追加されたときに呼び出されて既にチェックされているため、カスタムのList
が存在します)は、HashSet
のような一定時間で並列処理に適した操作ですが、List
の実装はLOTより低速です。
スキップリストの安価な実装について話している場合、この操作のコストは何ですか?
YourType [] array = someSet.toArray(new YourType [yourSet.size()]);
つまり、常に配列全体の作成にこだわるので、O(n)です。
Java.util.Arrays#copyOf
IndexedTreeSet from indexed-tree-map プロジェクトは、この機能を提供します(インデックスによるリストのようなアクセスを備えた順序付け/ソートされたセット)。
同様の問題がありました。順序付けられたセットは必要ありませんでしたが、高速の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(); }
}
}