Java'da neden SortedList yok?


503

Java'da SortedSetve SortedMaparayüzleri vardır. Her ikisi de Java Koleksiyonlar çerçevesine aittir ve öğelere erişmek için sıralı bir yol sağlar.

Ancak, benim anlayışımda SortedListJava yoktur . java.util.Collections.sort()Bir listeyi sıralamak için kullanabilirsiniz .

Neden böyle tasarlandýđýna dair bir fikrin var mý?



6
listenin ortasına bir öğe eklerken beklenen sonuç nedir?
bestsss

4
@bestsss java.util.List arabirimini uygulamayan bir SortedList sınıfına sahip olmak tamamen mümkün olacaktır. Soruyu, istenen işlevselliği destekleyen veri yapısının neden bulunmadığı sorgusu olarak okuyun. Adlandırma gibi önemsiz ayrıntılardan rahatsız olmayın.
Alderath

@Alderath, bunun olağan yapısı, siparişi desteklemek için ekstra önceki / sonraki bağlantılar içeren bir ağaç (kırmızı / siyah, avl veya btree). Önceki / sonraki bağlantıları w / kırmızı / siyah benzer bir yapı kullanıyorum. Yine de oldukça niş bir kullanımdır. Ağaç hem sıralı hem de kesici uç sırasına göre çevrilebilir, O (logn) bul / içerir, ancak get (int) O (n) 'dir. Niş uygulanabilirliği göz önüne alındığında, ihtiyaç duymaları halinde geliştiricilere bırakıldığını varsayabilirim.
bestsss

3
"Neden" sorusuna cevap vermemekle birlikte, TreeSet için en basit geçici çözüm hiçbir zaman sıfır döndürmeyen bir Karşılaştırıcı kullanmaktır int diff = this.score - that.score; return (diff == 0) ? 1 : diff; .
earcam

Yanıtlar:


682

Liste yineleyicileri, her şeyden önce listenin öğelerini listenin iç düzeninde almanızı garanti eder ( ekleme siparişi olarak da bilinir ). Daha spesifik olarak, öğeleri eklediğiniz sırayla veya listeyi nasıl manipüle ettiğinizle ilgilidir. Sıralama, veri yapısının manipülasyonu olarak görülebilir ve listeyi sıralamanın birkaç yolu vardır.

Şahsen gördüğüm şekilde, fayda sırasına göre yolları sıralayacağım :

1. Bunun yerine Setveya Bagkoleksiyonları kullanmayı düşünün

NOT: Bu seçeneği en üste koydum çünkü normalde yine de yapmak istediğiniz şey budur.

Sıralanmış bir küme koleksiyonu ekleme sırasında otomatik olarak sıralar , yani koleksiyona öğeler eklerken sıralamayı yapar. Ayrıca manuel olarak sıralamanız gerekmediği anlamına gelir.

Ayrıca, yinelenen öğeler hakkında endişelenmenize (veya sahip olmanıza) gerek olmadığından eminseniz, TreeSet<T>bunun yerine bunu kullanabilirsiniz . Bir listeden beklediğiniz gibi uygular SortedSetve NavigableSetarayüzler yapar ve çalışır:

TreeSet<String> set = new TreeSet<String>();
set.add("lol");
set.add("cat");
// automatically sorts natural order when adding

for (String s : set) {
    System.out.println(s);
}
// Prints out "cat" and "lol"

Doğal sıralamayı istemiyorsanız, a alan yapıcı parametresini kullanabilirsiniz Comparator<T>.

Alternatif olarak, kullanabileceğiniz MULTISETS (olarak da bilinir Çanta ) , bu ise Setbunun yerine, yinelenen öğe izin verdiğini ve bunların üçüncü parti uygulama vardır. En önemlisi gelen Guava kütüphaneleri bir var TreeMultisetgibi bir çok çalışır ki TreeSet.

2. ile listenizi sıralayın Collections.sort()

Yukarıda belirtildiği gibi, Lists'nin sınıflandırılması veri yapısının bir manipülasyonudur. Bu nedenle, çeşitli şekillerde sıralanacak olan "gerçeğin bir kaynağına" ihtiyaç duyduğunuz durumlar için, manuel olarak sıralamanın yolu budur.

Listenizi java.util.Collections.sort()yöntemle sıralayabilirsiniz . İşte nasıl bir kod örneği:

List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");

Collections.sort(strings);
for (String s : strings) {
    System.out.println(s);
}
// Prints out "cat" and "lol"

Karşılaştırıcıları kullanma

Bir net yararı kullanabilir olmasıdır Comparatoriçinde sortyöntemle. Java da bazı uygulamaları sağlar Comparatorgibi Collatoryerel duyarlı sıralama dizeleri için kullanışlıdır. İşte bir örnek:

Collator usCollator = Collator.getInstance(Locale.US);
usCollator.setStrength(Collator.PRIMARY); // ignores casing

Collections.sort(strings, usCollator);

Eşzamanlı ortamlarda sıralama

sortToplama örneği manipüle edileceğinden ve bunun yerine değişmez koleksiyonlar kullanmayı düşünmeniz gerektiğinden , yöntemi kullanmanın eşzamanlı ortamlarda kolay olmadığını unutmayın . Bu, Guava'nın Orderingsınıfta sağladığı bir şeydir ve basit bir tek astarlıdır:

List<string> sorted = Ordering.natural().sortedCopy(strings);

3. ile listenizi sarın java.util.PriorityQueue

Java'da sıralı bir liste olmasa da, muhtemelen sizin için de işe yarayacak sıralı bir kuyruk var. Öyle java.util.PriorityQueuesınıfı.

Nico Haase yorumlarda buna cevap veren ilgili bir soru ile bağlantı kurdu .

Sıralı bir koleksiyonda büyük olasılıkla dahili veri yapısını değiştirmek istemezsiniz, bu nedenle PriorityQueue Liste arayüzünü uygulamaz (çünkü bu size öğelerine doğrudan erişim sağlar).

Üzerinde Caveat PriorityQueueiterasyon

PriorityQueueSınıfının uyguladığı Iterable<E>ve Collection<E>her zamanki gibi iterated böylece arabirimleri. Ancak, yineleyicinin öğeleri sıralı olarak döndüreceği garanti edilmez. Bunun yerine (Alderath'ın yorumlarda belirttiği gibi) poll()boşalana kadar kuyruğa ihtiyacınız var .

Herhangi bir koleksiyonu alan yapıcı aracılığıyla bir listeyi öncelik sırasına dönüştürebileceğinizi unutmayın :

List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");

PriorityQueue<String> sortedStrings = new PriorityQueue(strings);
while(!sortedStrings.isEmpty()) {
    System.out.println(sortedStrings.poll());
}
// Prints out "cat" and "lol"

4. Kendi SortedListsınıfınızı yazın

NOT: Bunu yapmak zorunda değilsiniz.

Her yeni öğe eklediğinizde sıralanan kendi List sınıfınızı yazabilirsiniz. Bu, uygulamanıza bağlı olarak hesaplamayı ağırlaştırabilir ve iki ana nedenden dolayı bunu bir egzersiz olarak yapmak istemiyorsanız anlamsızdır :

  1. Yöntemler, öğenin kullanıcının belirttiği dizinde bulunmasını sağlaması gerektiğinden , List<E>arabirimin sahip olduğu sözleşmeyi ihlal eder add.
  2. Neden tekerleği yeniden icat ettiniz? Yukarıdaki ilk noktada belirtildiği gibi TreeSet veya Multisets kullanmalısınız.

Ancak, bunu bir alıştırma olarak yapmak istiyorsanız, başlamak için bir kod örneği, AbstractListsoyut sınıfı kullanır :

public class SortedList<E> extends AbstractList<E> {

    private ArrayList<E> internalList = new ArrayList<E>();

    // Note that add(E e) in AbstractList is calling this one
    @Override 
    public void add(int position, E e) {
        internalList.add(e);
        Collections.sort(internalList, null);
    }

    @Override
    public E get(int i) {
        return internalList.get(i);
    }

    @Override
    public int size() {
        return internalList.size();
    }

}

İhtiyacınız olan yöntemleri geçersiz kılmadıysanız, gelen varsayılan uygulamaların AbstractListatacağını unutmayın UnsupportedOperationException.


12
+1. Imo, bu en çok oylanan cevaptan daha yapıcı. Gerçi iki küçük dezavantajı ile geliyor. PriorityQueue rastgele erişimi desteklemiyor. Peek (elementIndex) yapamazsınız. Yani yapamazsınız örneğin Integer maxVal = prioQueue.peek(prioQueue.size() - 1);. İkinci olarak, PriorityQueue'yu basitçe sıralı bir liste olarak kullanmak istiyorsanız PriorityQueue, kodda bu SortedListtür bir veri yapısı varsa, görmekten daha az sezgisel gelecektir .
Alderath

12
Ve birisinin yorumlarda bağlantılandırdığı diğer soruya baktıktan sonra, bir başka büyük dezavantaj , PriorityQueue'nun yineleyicisinin belirli bir sırayla öğeleri döndüreceği garanti edilmemesidir. Yani, bir şeyi gözden geçirmedikçe, örneğin PriorityQueue'daki tüm nesneleri yazdırmak için tek yol, boş olana kadar kuyruğu tekrar tekrar yoklamaktır. Bana göre bu sınır çizgisinin geciktiğini hissediyor. Bir PriorityQueue içindeki nesneleri iki kez yazdırmak için, önce PriorityQueue'yu kopyalamanız ve ardından orijinal PriorityQueue'daki tüm nesneleri yoklamanız () sonra da kopyadaki tüm nesneleri pol () l yapmanız gerekir.
Alderath

1
Hmm ... haklısın Alderath. Öğeleri beklenen sırada almak için PriorityQueue yineleyicisini kullanamazsınız. Cevabımı düzenlemem gerekiyor gibi görünüyor.
2012'de

7
Öncelik kuyruğu sadece bir yığın, sadece tepeye erişebilirsiniz, imo sorunun cevabına ait değil.
bestsss

1
Ayrıca Collections.sort(), bir Comparatornesneyi kullanarak sıralamak için kullanılan karşılaştırma işlevini tanımlamanıza izin verir .
Mike 'Pomax' Kamermans

71

Çünkü Liste kavramı, otomatik olarak sıralanmış bir koleksiyon kavramıyla uyumsuzdur. Listenin amacı, çağrı yapıldıktan sonra list.add(7, elem)bir çağrının list.get(7)geri dönmesidir elem. Otomatik olarak sıralanan bir listeyle öğe keyfi bir konuma gelebilir.


26

Tüm listeler, öğelerin eklendiği sıraya göre ("FIFO sıralaması") "sıralandığından", bunları kullanarak öğelerin doğal sıralaması da dahil olmak üzere başka bir sıraya "başvurabilirsiniz" java.util.Collections.sort().

DÜZENLE:

Veri yapıları ilginç olanlara dayandırıldığında listeler, öğelerin yerleştirildiği sıradır.

Setler bu bilgilere sahip değildir.

Zaman ekleyerek sipariş vermek için tuşunu kullanın List. Başka ölçütlere göre sipariş vermek istiyorsanız kullanın SortedSet.


22
Set kopyalara izin vermiyor
bestsss

10
Bunun çok iyi bir cevap olduğunu düşünmüyorum. Elbette, java API'daki listelerin, öğelerin ne zaman / nasıl eklendiğine göre belirlenen belirli bir sıralaması vardır. Bununla birlikte, ekleme yöntemine / zamanına bağlı bir şekilde sıralanan listelerin mevcudiyeti, sıralamanın başka bir yolla (örneğin, bir Karşılaştırıcı tarafından) belirlendiği diğer veri yapılarının olmasını engellemez. Temel olarak OP, veri yapısının eşit elemanların birden çok oluşumuna izin vermesi dışında neden bir SortedSet'e eşdeğer bir veri yapısı olmadığını soruyor.
Alderath

5
Bu yüzden takip sorum şu olurdu: " Neden bir SortedSet gibi çalışan ancak birden çok eşit öğe içerebilen bir veri yapısı yok? " (Ve lütfen cevap vermeyin " çünkü Setler yalnızca bir öğe içerebilir ")
Alderath

@Alderath: Bkz. Stackoverflow.com/questions/416266/sorted-collection-in-java . Kısaca: Guava'nın TreeMultiset'ini kullanın
Janus Troelsen

4
"Sıralı" ifadesini çift tırnak içine alsanız bile listeler "sıralanmaz". Onlar emredilir.
Koray Tugay

21

Küme ve Harita doğrusal olmayan veri yapısıdır. Liste doğrusal veri yapısıdır.

resim açıklamasını buraya girin


Ağaç veri yapısı SortedSetve SortedMaparayüzleri uygular TreeSetve TreeMapsırasıyla kullanılan kullanarak Kırmızı-Siyah ağaç uygulama algoritması. Bu nedenle, yinelenen öğe (veya durumunda anahtarlar Map) olmadığından emin olun.

  • List zaten düzenli bir toplama ve dizin tabanlı veri yapısını korur, ağaçlar dizin tabanlı veri yapıları değildir.
  • Tree tanım gereği kopyalar içeremez.
  • İçinde Listkopyalar olabilir, bu yüzden TreeList(yani hayır SortedList) yoktur .
  • Liste öğeleri ekleme sırasına göre tutar. Listeyi sıralamak istiyorsak kullanmak zorundayız java.util.Collections.sort(). Öğelerinin doğal düzenine göre belirtilen listeyi artan düzende sıralar.

14

JavaFX SortedList

Biraz zaman alsa da, Java 8'in bir sıralaması var List. http://docs.oracle.com/javase/8/javafx/api/javafx/collections/transformation/SortedList.html

Javadoc'larda görebileceğiniz gibi, bir ObservableList üzerinde sıralı bir görünüm sağlaması amaçlanan JavaFX koleksiyonlarının bir parçasıdır .

Güncelleme: Java 11 ile JavaFX araç takımının JDK'nın dışına taşındığını ve artık ayrı bir kütüphane olduğunu unutmayın. JavaFX 11 indirilebilir bir SDK olarak veya MavenCentral'dan edinilebilir. Bkz. Https://openjfx.io


2
Ne yazık ki, bu SortedList normal bir liste gibi çalışmaz - örneğin, varsayılan bir yapıcıya sahip değildir (ne anlama gelirse gelsin, bir ObservableList kullanarak inşa etmeniz gerekir ...)
Erel Segal-Halevi

JavaFX SortedList açıkça GUI-Bileşenleri kullanımı içindir ve yükü nedeniyle sıralanan nesnelerin bir Listesi olması için uygun DEĞİLDİR. Ayrıca, GUI projede kullanılmasa bile tüm FX Modülüne başvurmak anlamına gelir.
COBRA.cH

1
@ COBRA.cH Evet, bu doğru. Daha performanslı bir Sıralama Listesi, muhtemelen bir TreeMap'in etrafındaki, anahtarın kopyalarını saymak için bir tamsayı kullanılan ince bir paket olabilir. Ayrıca hiçbir zaman 0 döndürmeyen bir karşılaştırıcıya sahip bir TreeSet de kullanabilirsiniz.
swpalmer

Neden aşağı oylar? Soru, JDK kitaplıklarında sıralı bir liste bulunmadığı varsayımıyla başlar. Bu Yanıt bu varsayımı düzeltir. Bu belirli listenin uygulanmasını beğenip beğenmemeniz veya düşürmemeniz düşük oylama için bir neden değildir. Gelmez Bu cevap tavsiye sınıfı, sadece varlığını işaret eder.
Basil Bourque

10

Yeni gelenler için, Nisan 2015 itibariyle Android artık destek kitaplığında özel olarak çalışmak üzere tasarlanmış bir SortedList sınıfına sahiptir RecyclerView. İşte bununla ilgili blog yazısı .


1
Bu yorum sırasında Androids SortedList'in RecyclerView ile onItemMoved () işlevselliği için desteğe sahip olmadığını belirtmek gerekir. Daha az verimli olan kendi SortedList'imi yazmak, sınırlamaları aşmak için yapmam gereken şeydi.
Matthew

4

Diğer bir nokta, kesici uç işlemlerinin zaman karmaşıklığıdır. Liste ekleme için, O (1) karmaşıklığı beklenir. Ancak bu, sıralanmış bir listeyle garanti edilemedi.

Ve en önemli nokta, listelerin öğeleri hakkında hiçbir şey varsaymamasıdır. Örneğin, uygulamayan equalsveya olmayan şeylerin listelerini oluşturabilirsiniz compare.


list gir / sil / bul / içerir, ancak w / get (int) listeniz olmayabilir.
bestsss

3
Son nokta gerçekten iyi bir açıklama değil. Bir yapabilir SortedSetuygulamak olmayan şeyleri Comparable. Bu TreeSet yapıcısına bakın .
Alderath

1
@Alderath - belki benim ifadem çok zayıftı. Ancak gözlem, Ağaçların Kümeleri ve anahtarlarının unsurlarının en azından eşitlik için karşılaştırılabilir olması gerektiğini, oysa liste öğelerinin buna gerek olmadığını savunuyor. Kümeler ve Ağaçlar için sıralama / eşitlik ilişkisinin bir Karşılaştırıcıda veya başka bir yerde uygulanıp uygulanmadığı önemsizdir - ancak bir taneye ihtiyacınız vardır.
Ingo

Listeler yok O (1) garanti ekleme onlar O (1) garanti, erişim . bigocheatsheet.com
Matt Quigley

1
@Betlista haklısın! Yorumumu güncelleyemiyorum, ancak Java Listarayüzü yöntemleriyle ilgili performans özelliklerini garanti etmiyor.
Matt Quigley

3

Bu gibi düşünün: Listarayüzü gibi yöntemleri vardır add(int index, E element), set(int index, E element). Sözleşme, X konumuna bir öğe eklediğinizde, önce öğe eklemez veya kaldırmazsanız onu orada bulacağınızdır.

Herhangi bir liste uygulaması, öğeleri dizine dayalı bir sıra dışında depolarsa, yukarıdaki liste yöntemleri bir anlam ifade etmez.


2

Liste API'sındaki ilk satır, sıralı bir koleksiyon olduğunu (sekans olarak da bilinir) söylüyor. Listeyi sıralarsanız, siparişi koruyamazsınız, bu nedenle Java'da TreeList yoktur.
API'nın dediği gibi Java Listesi, Sekanstan ilham aldı ve sekans özelliklerini görün http://en.wikipedia.org/wiki/Sequence_(mathematics )

Bu listeyi sıralayamayacağınız anlamına gelmez, ancak Java tanımına sıkı sıkıya bağlıdır ve varsayılan olarak listelerin sıralı sürümlerini sağlamaz.



2

Elemanları sıralamanın bir yolunu arıyorsanız, ancak bunlara indekse verimli bir şekilde erişebiliyorsanız, aşağıdakileri yapabilirsiniz:

  1. Depolama için rastgele bir erişim listesi kullanın (ör. ArrayList)
  2. Her zaman sıralandığından emin olun

Ardından bir öğe eklemek veya kaldırmak Collections.binarySearchiçin, ekleme / kaldırma dizinini alabilirsiniz. Listeniz rasgele erişim sağladığından, belirlenen dizinle listeyi etkili bir şekilde değiştirebilirsiniz.

Misal:

/**
 * @deprecated
 *      Only for demonstration purposes. Implementation is incomplete and does not 
 *      handle invalid arguments.
 */
@Deprecated
public class SortingList<E extends Comparable<E>> {
    private ArrayList<E> delegate;

    public SortingList() {
        delegate = new ArrayList<>();
    }

    public void add(E e) {
        int insertionIndex = Collections.binarySearch(delegate, e);

        // < 0 if element is not in the list, see Collections.binarySearch
        if (insertionIndex < 0) {
            insertionIndex = -(insertionIndex + 1);
        }
        else {
            // Insertion index is index of existing element, to add new element 
            // behind it increase index
            insertionIndex++;
        }

        delegate.add(insertionIndex, e);
    }

    public void remove(E e) {
        int index = Collections.binarySearch(delegate, e);
        delegate.remove(index);
    }

    public E get(int index) {
        return delegate.get(index);
    }
}

1

Dizine alınmış ağaç haritasını kullanmayı düşünün . Gelişmiş bir JDK'nın, dizine göre öğeye erişim ve bir öğenin dizinini yineleme veya ağacı yedekleyen gizli listeler olmadan bulma sağlayan gelişmiş bir TreeSet'dir. Algoritma, her değişiklik olduğunda değişen düğümlerin ağırlıklarının güncellenmesine dayanmaktadır.

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.