重複のないリストの実装はありますか?


86

私は知っているSortedSetが、私の場合は私が実装していることに何か必要ListとしませんSet。それで、APIまたは他の場所に実装がありますか?

自分で実装するのは難しいことではありませんが、最初にここの人々に聞いてみませんか?


1
なぜリストを実装する必要があるのですか?セットはリストのように反復可能であるため、受信メソッドが他の理由でリストを強制していると思います。
ロブ

@Robそうです、それは外部の要求であり、データ構造には複数のリストが含まれています。
Yuval

ユーザーがリストを望んでいるならば、それは明らかだというニーズUM SETインタフェース存在しないLISTインタフェースのメソッド...
marcolopes

回答:


92

これを行うためのJavaコレクションは標準ライブラリにありません。ただし、LinkedHashSet<E>と同様に順序を保持するListため、セットをListとして使用するときにセットをaでラップすると、List必要なセマンティクスが得られます。

あるいは、Commons Collections(またはcommons-collections4、汎用バージョンの場合)には、Listすでに必要なことを実行するがあります:SetUniqueList/ SetUniqueList<E>


5
Commonsクラスはまさに私が必要としているものですが、上司から、最終的には自分で実装するように言われました。とにかく10倍!
Yuval

5
ああ、車輪の再発明に勝るものはありません!とにかく、必要が再び発生するかどうかは今すぐわかります。collections15は、キックするのに非常に便利なものです。特にMultiMapsは、自分自身を多く実装することになる何かの苦痛を和らげます。
Calum

19
@skaffman:彼は実際にはばかではありませんが、時々彼は...まあ、奇妙な動きをします。とにかく、私は製品にバグを導入するつもりはありません。今日の市場では、私は自分の仕事に満足しており、私の主張がわかれば、ドアを閉めたり橋を燃やしたりすることは考えていません。
Yuval

3
SetUniqueListにパラメーター化された型がない場合、私は非常に驚いています。
emeraldhieu 2012年

2
ジェフリー:モバイルプラットフォームでは、システムは通常、未使用のクラスを削除しますが、確かに、これらの「通常の」ソリューションの1つを実行しない理由はたくさんあります。常にトレードオフが必要であり、すべてのケースを解決できるソリューションはありません。
calum 2013

14

これが私がしたことであり、それは機能します。

私がArrayList最初にしたことで作業する必要があると仮定すると、新しいが作成されましたLinkedHashMap

LinkedHashSet<E> hashSet = new LinkedHashSet<E>()

次に、新しい要素をに追加しようとしLinkedHashSetます。addメソッドはを変更せずLinkedHasSet、新しい要素が重複している場合はfalseを返します。したがって、これは、に追加する前にテストできる条件になりArrayListます。

if (hashSet.add(E)) arrayList.add(E);

これは、重複が配列リストに追加されるのを防ぐためのシンプルでエレガントな方法です。必要に応じて、それをカプセル化し、を拡張するクラスのaddメソッドをオーバーライドできますArrayListaddAll要素をループしてaddメソッドを呼び出すことで対処することを忘れないでください。


1
ええ、これが最善の解決策だと思います。リンクではなく通常のHashSetを使用することもできます。その後、リストを好きなように使用できます。また、次のような状況で何をするかを決めることもできます。リスト内の特定のインデックスの前に要素を追加すると、複製されたアイテムをこの位置に移動するかどうかを決定できます。
gyurix 2015年

ここでの最良の解決策...私のUniqueListクラスコードを投稿します
marcolopes 2016年

これは、私のBFSグラフアルゴリズムでうまくいきました。私は、彼らがすでにいなかっただけであれば、私はキュー(LinkedListの)に追加することをいくつかのノードを持っていたので。
Jeancarlo Fontalvo

11

それで、これが私が最終的にしたことです。これが他の誰かに役立つことを願っています。

class NoDuplicatesList<E> extends LinkedList<E> {
    @Override
    public boolean add(E e) {
        if (this.contains(e)) {
            return false;
        }
        else {
            return super.add(e);
        }
    }

    @Override
    public boolean addAll(Collection<? extends E> collection) {
        Collection<E> copy = new LinkedList<E>(collection);
        copy.removeAll(this);
        return super.addAll(copy);
    }

    @Override
    public boolean addAll(int index, Collection<? extends E> collection) {
        Collection<E> copy = new LinkedList<E>(collection);
        copy.removeAll(this);
        return super.addAll(index, copy);
    }

    @Override
    public void add(int index, E element) {
        if (this.contains(element)) {
            return;
        }
        else {
            super.add(index, element);
        }
    }
}   

10
注意-LinkedList.contains()は、オブジェクトがリストに含まれているかどうかを判断するために、リスト全体をスキャンする必要があります。これは、大きなリストにオブジェクトを追加するときに、追加操作ごとにリスト全体がスキャンされることを意味します(最悪の場合)。これは最終的に遅くなる可能性があります。
マットb

8
また、addAllオーバーライドは、addAll()に渡されるコレクション内の重複をチェックしません。
マットb

@mattbこの問題をどのように解決しますか:Androidでは、オブジェクトをリストアイテムビューにバインドすると、ビューアダプター内のアイテムの位置が指定されます。セットにはインデックスがないため、リストを使用するときにオブジェクトが存在するかどうかを確認する唯一の方法は、繰り返し処理して既存のコピーを探すことです。
TheRealChx1 0119

6

セットをリストでカプセル化して、次のように並べ替えてみませんか。

new ArrayList( new LinkedHashSet() )

これは、コレクションの本当のマスターである誰かのために他の実装を残します;-)


4
このコンストラクターは、セットの内容をラップするのではなく、新しいリストにコピーします。
Calum

@Calum、それは正しいですが、リストに重複を追加しないことを心配する代わりに、彼はオブジェクトをセットに追加し(そして、セットに重複を除外することを心配させる)、それをに渡すときにそのセットをリストでラップすることができます外部メソッド。
マットb

4
これによりセットがリストにコピーされますが、よく知られた順序はありません。しかし、これが問題のすべてです。
2012年

4

ディラーの答えを真剣に検討する必要があります。

  1. オブジェクトを重複のないリストに追加することを心配する代わりに、オブジェクトをセット(任意の実装)に追加します。これにより、本来、重複が除外されます。
  2. リストを必要とするメソッドを呼び出す必要がある場合は、それをnew ArrayList(set)(または、new LinkedList(set)など)でラップします。

で投稿したソリューションにはNoDuplicatesList、主にcontains()メソッドにいくつかの問題があると思います。さらに、クラスは、addAll()メソッドに渡されたコレクション内の重複のチェックを処理しません。


これらのcontains()の問題について知りたいです。addAll()に関しては、指定されたコレクションのコピーを作成し、すでに「this」にあるすべてのオブジェクトを削除します。それはどのように重複を処理しませんか?
Yuval

クラスの投稿へのコメントで述べたように、contains()はリスト全体をスキャンして(最悪の場合)、オブジェクトがリストに含まれているかどうかを確認する必要があります。100万個のアイテムのリストがあり、それを個別に10個追加すると、(最悪の場合)1,000万個を超えるアイテムがスキャンされます。
マットb

addAll()に関しては、addAllに渡されたコレクション自体に重複が含まれている場合、それらは検出されません。例:リスト{A、B、C、D}パラメータリスト{B、D、E、E、E}。パラメータのコピーを作成し、removeAllの後に{E、E、E}が含まれます。
マットb

addAll()の問題は、プロシージャ全体でNoDuplicatesListを使用しているため、実際には関係ありません。addAll()は、パラメータとして別のNoDuplicatesListを受け取る必要があります。contains()のパフォーマンスを向上させるために何を提案しますか?
Yuval

3

そのようなものが必要だったので、commonsコレクションアクセスしてを使用しましたがSetUniqueList、パフォーマンステストを実行したところSetArrayを使用してSet.toArray()メソッドを使用して取得したい場合と比較して、最適化されていないようです。

SetUniqueTest取った20:1の時間を記入してから横断する10万ストリングス大した差であり、他の実装に比較します。

したがって、パフォーマンスが心配な場合は、のロジックが本当に必要でない限り、を使用する代わりにSet and Get a Arrayを使用することをお勧めします。その場合は、他のソリューションを確認する必要があります...SetUniqueListSetUniqueList

コードのメインメソッドのテスト

public static void main(String[] args) {


SetUniqueList pq = SetUniqueList.decorate(new ArrayList());
Set s = new TreeSet();

long t1 = 0L;
long t2 = 0L;
String t;


t1 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
    pq.add("a" + Math.random());
}
while (!pq.isEmpty()) {
    t = (String) pq.remove(0);
}
t1 = System.nanoTime() - t1;

t2 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
    s.add("a" + Math.random());
}

s.clear();
String[] d = (String[]) s.toArray(new String[0]);
s.clear();
for (int i = 0; i < d.length; i++) {
    t = d[i];

}
t2 = System.nanoTime() - t2;

System.out.println((double)t1/1000/1000/1000); //seconds
System.out.println((double)t2/1000/1000/1000); //seconds
System.out.println(((double) t1) / t2);        //comparing results

}

よろしく、 モハメッドスリーム


1

注:subListの実装は考慮されていません。

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

public class UniqueList<T> extends ArrayList<T> {

    private static final long serialVersionUID = 1L;

    /** Unique elements SET */
    private final Set<T> set=new HashSet();

    /** Used by addAll methods */
    private Collection<T> addUnique(Collection<? extends T> col) {
        Collection<T> unique=new ArrayList();
        for(T e: col){
            if (set.add(e)) unique.add(e);
        }
        return unique;
    }

    @Override
    public boolean add(T e) {
        return set.add(e) ? super.add(e) : false;
    }

    @Override
    public boolean addAll(Collection<? extends T> col) {
        return super.addAll(addUnique(col));
    }

    @Override
    public void add(int index, T e) {
        if (set.add(e)) super.add(index, e);
    }

    @Override
    public boolean addAll(int index, Collection<? extends T> col) {
        return super.addAll(index, addUnique(col));
    }

}

0

コレクションインターフェイスドキュメントには次のように書かれています

セット—重複する要素を含めることができないコレクション。
リスト—順序付けられたコレクション(シーケンスと呼ばれることもあります)。リストには重複する要素を含めることができます。

したがって、重複が必要ない場合は、リストを使用しないでください。


Listの実装が必要だと具体的に述べました。私を信じてください、理由があります。
Yuval

(コレクションではなく)リストをパラメーターとして使用するAPIを操作しているためですか?対処しなければならないのは少し面倒です
matt b

実際、APIはMap <AccountType、Map <AccountType、List <Account >>>を取ります。これは、数十から数百のリストの近くに保持することを意味します... bah。
Yuval

要素と確率のペアを使用して確率関数を作成するには、重複する要素をマージすることはできますが、重複を含めることはできません。
アルGジョンストン

-1

add方法、なぜ使用していませんHashSet.add()代わりの重複をチェックしますHashSet.consist()。 重複がない場合HashSet.add()は戻ります。truefalse


HashSet#consist()ですか?
naXa 2017

-1

私の頭の上から、リストは重複を許可します。継承されたメソッドを呼び出す前に、UniqueArrayListをすばやく実装し、すべてのadd/insert関数をオーバーライドしてチェックcontains()することができます。個人的な使用のadd場合は、使用するメソッドのみを実装し、他のメソッドをオーバーライドして、将来のプログラマーが別の方法でリストを使用しようとした場合に備えて例外をスローすることができます。


誰もより良いものを提案しなかった場合、私はこのアイデアにフォールバックする準備ができていました(最終的にはそうしなければなりませんでした)= 8-)上記の私自身の答えを参照してください。
Yuval

-3

私はこのような自分の小さなライブラリで自分のUniqueListを作成しました:

package com.bprog.collections;//my own little set of useful utilities and classes

import java.util.HashSet;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Jonathan
*/
public class UniqueList {

private HashSet masterSet = new HashSet();
private ArrayList growableUniques;
private Object[] returnable;

public UniqueList() {
    growableUniques = new ArrayList();
}

public UniqueList(int size) {
    growableUniques = new ArrayList(size);
}

public void add(Object thing) {
    if (!masterSet.contains(thing)) {
        masterSet.add(thing);
        growableUniques.add(thing);
    }
}

/**
 * Casts to an ArrayList of unique values
 * @return 
 */
public List getList(){
    return growableUniques;
}

public Object get(int index) {
    return growableUniques.get(index);
}

public Object[] toObjectArray() {
    int size = growableUniques.size();
    returnable = new Object[size];
    for (int i = 0; i < size; i++) {
        returnable[i] = growableUniques.get(i);
    }
    return returnable;
    }
}

次のようなTestCollectionsクラスがあります。

package com.bprog.collections;
import com.bprog.out.Out;
/**
*
* @author Jonathan
*/
public class TestCollections {
    public static void main(String[] args){
        UniqueList ul = new UniqueList();
        ul.add("Test");
        ul.add("Test");
        ul.add("Not a copy");
        ul.add("Test"); 
        //should only contain two things
        Object[] content = ul.toObjectArray();
        Out.pl("Array Content",content);
    }
}

正常に動作します。セットにまだ含まれておらず、戻り可能なArraylistとオブジェクト配列がある場合は、セットに追加するだけです。


ええ、リストインターフェイスを実装するためにもう少しメソッドを追加する必要があります。
gyurix 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.