Orada yinelenmeyen bir Liste uygulaması var mı?


87

Biliyorum SortedSetama benim durumumda uygulayan bir şeye ihtiyacım var List, değil Set. Öyleyse orada, API'de veya başka bir yerde bir uygulama var mı?

Kendimi uygulamak zor olmamalı, ama neden önce buradaki insanlara sormayacağımı düşündüm.


1
Neden List'i uygulaması gerekiyor? Kümeler, listeler gibi yinelenebilir, bu yüzden alma yönteminin Listeyi başka bir nedenle zorladığını düşünüyorum.
Rob

@Rob Doğru, bu harici bir talep ve veri yapısı birden fazla Liste içeriyor.
Yuval

Kullanıcı bir LİSTE istiyorsa, SET arayüzünde bulunmayan LIST arayüzü yöntemlerine ihtiyaç duyduğu açıktır ...
marcolopes

Yanıtlar:


94

Standart kitaplıkta bunu yapmak için Java koleksiyonu yoktur. LinkedHashSet<E>sıralamayı a'ya benzer şekilde korur List, bu nedenle kümenizi bir Listolarak kullanmak istediğinizde bir kümeye sararsanız, istediğiniz Listanlam bilgisini elde edersiniz.

Alternatif olarak, Commons Koleksiyonları (veya commons-collections4genel sürüm için) Listzaten istediğinizi yapan bir şeye sahiptir : SetUniqueList/ SetUniqueList<E>.


6
Commons sınıfı tam olarak ihtiyacım olan şey, ancak patronum sonunda bunu kendim uygulamamı söyledi. Yine de 10x!
Yuval

5
Ah peki, tekerleği yeniden icat etmek gibisi yok! Yine de ihtiyacın tekrar ortaya çıkıp çıkmadığını şimdi bileceksin. collections15, etrafta dolaşmak için oldukça faydalı bir şey; Özellikle MultiMap'ler, kişinin kendini çok fazla uyguladığı bir şeyin acısını hafifletir.
Calum

19
@skaffman: O aslında bir aptal değil, ama bazen ... iyi, tuhaf hareketler yapıyor. Her neyse, ürüne böcek sokmayacağım. Bugünün piyasasında, işimden memnunum ve demek istediğimi anlarsanız, kapıları çarpıp köprüleri yakmak istemiyorum.
Yuval

3
SetUniqueList parametreleştirilmiş türe sahip olmadığında oldukça şaşırdım.
emeraldhieu

2
Jeffrey: Mobil platformlarda sistem genellikle kullanılmayan sınıfları kaldırır, ancak elbette, bu "normal" çözümlerden birine girmemenizin birçok nedeni vardır. Her zaman yapılacak bazı değiş-tokuşlar vardır ve hiçbir çözüm tüm durumları çözemez.
Calum

14

İşte yaptığım şey ve işe yarıyor.

Çalışmam gerektiğini varsayarsak, yaptığım ArrayListilk şey yeni bir şey yaratmaktı LinkedHashMap.

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

Sonra yeni öğemi LinkedHashSet. LinkedHasSetYeni öğe bir kopya ise add yöntemi değiştirmez ve yanlış döndürür. Yani bu ArrayList,.

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

Bu, kopyaların bir dizi listesine eklenmesini önlemenin basit ve zarif bir yoludur. İsterseniz, onu sarmalayabilir ve add yöntemini ArrayList. addAllÖğeler arasında döngü oluşturarak ve add yöntemini çağırarak uğraşmayı unutmayın .


1
Evet, bence bunun için en iyi çözüm bu, Bağlantılı değil, basitçe normal bir HashSet de kullanabilirsiniz ve sonra listenizi istediğiniz gibi kullanabilirsiniz, ayrıca bazı durumlarda ne yapacağınıza karar verebilirsiniz, örneğin belirli bir dizinden önce bir listeye bir öğe eklediğinizde, çoğaltılan öğeyi bu konuma taşımak isteyip istemediğinize karar verebilirsiniz.
gyurix

Buradaki en iyi çözüm ... UniqueList sınıf kodumu
göndereceğim

Bu, BFS Grafik algoritmamda benim için çalıştı. Çünkü bir Kuyruğa (LinkedList), eğer zaten yoksa, eklediğim bazı düğümlerim vardı.
Jeancarlo Fontalvo

11

İşte sonunda yaptığım şey. Umarım bunun bir başkasına yardımı olur.

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
Dikkatli olun - LinkedList.contains (), Listede bir nesnenin bulunup bulunmadığını belirlemek için tüm listeyi taramalıdır. Bu, büyük bir Listeye nesneler eklediğinizde, her ekleme işlemi için (en kötü durumda) Listenin tamamının tarandığı anlamına gelir. Bu yavaş olabilir.
matt b

8
Ayrıca, addAll geçersiz kılmanız, koleksiyonda addAll () 'a iletilen kopyaları kontrol etmez.
matt b

@mattb O zaman bu sorunu nasıl çözersiniz: Android'de nesneleri bir liste öğesi görünümüne bağlarken, öğenin görünüm bağdaştırıcısındaki konumu bize verilir. Kümelerin indeksi olmadığından, listeyi kullanırken nesnenin var olup olmadığını kontrol etmenin tek yolu, yineleme yapmak ve var olan bir kopyayı aramaktır.
TheRealChx101

6

Neden bir kümeyi bir listeyle sarmalıyorsunuz, şöyle sıralayın:

new ArrayList( new LinkedHashSet() )

Bu, gerçek bir Koleksiyon ustası olan biri için diğer uygulamayı bırakır ;-)


4
Bu kurucu, Kümenin içeriğini sarmalamak yerine yeni Listeye kopyalar.
Calum

@Calum, bu doğru, ancak bir Listeye kopyaları eklememe konusunda endişelenmek yerine, nesnelerini bir Set'e ekleyebilir (ve Set'in kopyaları filtrelemekten endişelenmesine izin verebilir) ve sadece bu Seti bir Listeye aktarırken sarabilir. dış yöntem.
matt b

4
Bu, bir kümeyi bir listeye kopyalar, ancak iyi bilinen bir sıralamaya sahip değilsiniz. Ama asıl soru bu.
Janning

4

Dhiller'ın cevabını ciddiye almalısınız:

  1. Nesnelerinizi kopyasız bir Listeye ekleme konusunda endişelenmek yerine, onları doğası gereği kopyaları filtreleyecek bir Set'e (herhangi bir uygulama) ekleyin.
  2. Liste gerektiren yöntemi çağırmanız gerektiğinde, onu a new ArrayList(set)(veya a new LinkedList(set), her neyse) içine sarın .

Bence yayınladığınız çözümün NoDuplicatesList, çoğunlukla contains()yöntemle ilgili bazı sorunları olduğunu düşünüyorum , ayrıca sınıfınız, addAll()yönteminize aktarılan Koleksiyondaki kopyaları kontrol etmiyor .


Bu contains () sorunlarını öğrenmek isterim. AddAll () 'a gelince, verilen koleksiyonun bir kopyasını oluşturuyorum ve zaten' this'de bulunan tüm nesneleri kaldırıyorum. Bu, kopyaları nasıl ele almaz?
Yuval

Sınıf gönderinize yaptığım yorumda belirttiğim gibi, contains () nesnenin listede yer alıp almadığını bulmak için tüm listeyi (en kötü durumda) taramalıdır. 1 milyon öğe listeniz varsa ve tek tek 10 eklerseniz, (en kötü durumda) on milyondan fazla öğe taranır.
matt b

AddAll () 'ye gelince, addAll'a aktarılan Koleksiyonun kendisi kopyalar içeriyorsa, algılanmazlar. Örneğin: listeniz {A, B, C, D} parametre listesi {B, D, E, E, E}. Parametrenin bir kopyasını oluşturursunuz ve removeAll'den sonra {E, E, E} içerir.
matt b

Prosedür boyunca NoDuplicatesList kullandığım ve addAll () parametresi olarak başka bir NoDuplicatesList alması gerektiğinden, addAll () sorunu benim için gerçekten alakalı değil. Contains () performansını iyileştirmek için ne önerirsiniz?
Yuval

3

Bunun gibi bir şeye ihtiyacım vardı, bu yüzden ortak koleksiyonlara gittim ve kullandım SetUniqueList, ancak bazı performans testleri çalıştırdığımda, a kullanmak Setve Arraykullanarak bir elde etmek istersem, duruma göre optimize edilmediğini gördüm .Set.toArray() yöntemi .

SetUniqueTestAldı 1 kez: 20 çapraz sonra doldurmak ve 100.000 dizeleri büyük bir anlaşma farkıdır diğer uygulama, karşılaştırarak.

Performansta dert varsa yani, ben kullanmayı tavsiye Set ve Array alın kullanmak yerine SetUniqueListgerçekten mantığını gerekmiyorsa SetUniqueList, o zaman başka çözümler kontrol etmek gerekir ...

Test kodu ana yöntemi :

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

}

Saygılarımızla, Mohammed Sleem


1

NOT: alt liste uygulamasını dikkate almaz .

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

Toplama arayüzleri için dokümantasyon diyor ki:

Set - yinelenen öğeler içeremeyen bir koleksiyon.
Liste - sıralı bir koleksiyon (bazen bir sıra olarak adlandırılır). Listeler yinelenen öğeler içerebilir.

Dolayısıyla, kopyaları istemiyorsanız, muhtemelen bir liste kullanmamalısınız.


Bir Liste uygulamasına ihtiyacım olduğunu özellikle belirttim. İnan bana, bir sebebi var.
Yuval

Bunun nedeni, bir Listeyi parametre olarak alan (Koleksiyon yerine) bir API ile etkileşimde bulunmanız mı? Bu başa çıkmak zorunda olmak biraz can sıkıcı
matt b

Aslında API bir Map <AccountType, Map <AccountType, List <Account> >> alır, bu da düzinelerce ila yüzlerce listeye yakın bir yerde tutulması anlamına gelir ... bah.
Yuval

Eleman-olasılık çiftleri ile olasılık fonksiyonları oluşturmak, yinelenen elemanların sadece birleştirilebilmesine rağmen, yinelemeleri olmayabilir.
Al G Johnston

-1

içinde addyöntemle, neden kullanmayan HashSet.add()yerine çiftleri kontrol etmek HashSet.consist(). kopya yoksa HashSet.add()geri dönecektir .truefalse


Nedir HashSet#consist()?
naXa

-1

Aklımın ucunda, listeler kopyalara izin veriyor. Devralınan yöntemleri çağırmadan önce kontrol etmek için hızlı bir şekilde a uygulayabilir UniqueArrayListve tüm add/ insertişlevlerini geçersiz kılabilirsiniz contains(). Kişisel kullanım için, yalnızca addkullandığınız yöntemi uygulayabilir ve gelecekteki programcıların listeyi farklı bir şekilde kullanmaya çalışması durumunda bir istisna atmak için diğerlerini geçersiz kılabilirsiniz.


Kimse daha iyi bir şey önermediyse bu fikre geri dönmeye hazırdım (ki sonunda bunu yapmak zorunda kaldım) = 8-) Yukarıdaki kendi cevabıma bakın.
Yuval

-3

Kendi küçük kütüphanemde şu şekilde kendi UniqueList'imi oluşturdum:

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

Şuna benzeyen bir TestCollections sınıfım var:

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

İyi çalışıyor. Yaptığı tek şey, zaten yoksa ve iade edilebilir bir Dizi Listesi ve bir nesne dizisi varsa, bir kümeye ekleme yapmaktır.


Evet, List arayüzünü uygulamak için ona biraz daha yöntem eklemelisiniz.
gyurix
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.