簡體   English   中英

如何在不使用Collections.sort()的情況下獲得Java中的排序列表行為?

[英]How can I get Sorted List behavior in Java without using Collections.sort()?

我了解Java出於各種概念上的原因不具備排序列表,但是考慮到我需要擁有一個類似於Priority Queue但還允許我進行隨機訪問(可索引)的集合的情況,換句話說,我需要一個遵循特定順序的列表。 我寧願使用Collections.sort()

首選操作約束:

檢索-O(1)(基於索引的隨機訪問)
搜索-O(log n)
插入-O(log n)
刪除-O(log n)

集合上的迭代器應按排序順序為我提供所有元素(基於在數據結構實例化時提供的預定義Comparator

我寧願使用Java的內置庫來完成此操作,但也可以建議外部庫。

編輯:TreeSet不會做,因為基於索引的訪問很困難,使用包裝器集合也不是我的最佳選擇,因為刪除意味着我需要從兩個集合中刪除。

EDIT2:我無法找到indexable skip list的實現和/或文檔,這似乎有些相關,有人可以幫助我找到它嗎? 也歡迎對提議的數據結構提出或反對的任何評論。

EDIT3:雖然這可能不是最完美的答案,但是我想添加這段代碼,以便任何有類似問題但需要排序列表的人都可以使用它,如果他們覺得有用的話。

請檢查是否有錯誤(如有),並提出改進建議(尤其是對sortedSubList方法)

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;

public class SortedList<E> extends ArrayList<E> {
    private final Comparator<? super E> comparator;

    public SortedList(Comparator<? super E> comparator) {
        this.comparator = comparator;
    }

    public SortedList(int initialCapacity, Comparator<? super E> comparator) {
        super(initialCapacity);
        this.comparator = comparator;
    }

    @Override
    public boolean add(E e) {
        if (comparator == null)
            return super.add(e);
        if (e == null)
            throw new NullPointerException();
        int start = 0;
        int end = size() - 1;
        while (start <= end) {
            int mid = (start + end) / 2;
            if (comparator.compare(get(mid), e) == 0) {
                super.add(mid, e);
                return true;
            }
            if (comparator.compare(get(mid), e) < 0) {
                end = mid - 1;
            }
            else {
                start = mid + 1;
            }
        }
        super.add(start, e);
        return true;
    }

    @Override
    public boolean contains(Object o) {
        if (comparator == null)
            return super.contains(o);
        if (o == null)
            return false;
        E other = (E) o;
        int start = 0;
        int end = size() - 1;
        while (start <= end) {
            int mid = (start + end) / 2;
            if (comparator.compare(get(mid), other) == 0) {
                return true;
            }
            if (comparator.compare(get(mid), other) < 0) {
                end = mid - 1;
            }
            else {
                start = mid + 1;
            }
        }
        return false;
    }

    @Override
    public int indexOf(Object o) {
        if (comparator == null)
            return super.indexOf(o);
        if (o == null)
            throw new NullPointerException();
        E other = (E) o;
        int start = 0;
        int end = size() - 1;
        while (start <= end) {
            int mid = (start + end) / 2;
            if (comparator.compare(get(mid), other) == 0) {
                return mid;
            }
            if (comparator.compare(get(mid), other) < 0) {
                end = mid - 1;
            }
            else {
                start = mid + 1;
            }
        }
        return -(start+1);
    }

    @Override
    public void add(int index, E e) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean addAll(int index, Collection<? extends E> c) {
        throw new UnsupportedOperationException();
    }

    @Override
    public E set(int index, E e) {
        throw new UnsupportedOperationException();
    }

    public SortedList<E> sortedSubList(int fromIndex, int toIndex) {
        SortedList<E> sl = new SortedList<>(comparator);
        for (int i = fromIndex; i < toIndex; i++)
            sl.add(get(i));
        return sl;
    }
}

在同一數據結構中很難獲得O(1)索引和O(log n)插入/刪除。 O(1)索引意味着我們無法負擔對樹,列表,跳過列表或其他基於鏈接的數據結構建立索引所涉及的鏈接跟蹤,而O(log n)修改意味着我們無法負擔一半的移位每次插入時數組的元素。 我不知道是否有可能同時滿足這些要求。

如果我們放寬這些要求之一,事情就會變得容易得多。 例如,所有操作的O(log n)都可以通過可索引的跳過列表或具有節點的自平衡BST來實現,該節點跟蹤以該節點為根的子樹的大小。 但是,這些都不能建立在Java標准庫的跳過列表或BST之上,因此您可能需要安裝另一個庫或編寫自己的數據結構。

可以通過保留排序的ArrayList並使用Collections.binarySearch搜索元素或插入/刪除位置來完成O(1)索引編制,O(log n)搜索以及O(n)插入和刪除操作。 您不需要調用Collections.sort ,但仍然需要調用ArrayList的O(n)插入和刪除方法。 這可能是在Java內置工具之上構建的最簡單的選擇。 請注意,在最新的Java版本中, Collections.sort是一種自適應mergesort,將花費O(n)時間對僅最后一個元素不按排序順序排列的數組進行排序,因此您可以擺脫依賴Collections.sort 但是,這是實現細節,其他Java實現不必遵循。

如果您的主要目標是索引查找( get() )的O(1),則可以使用Arrays.binarySearch()實現自己的類,該類實現List ,並由數組支持。

retrieve: get(int)         - O(1)     - array index
search:   contains(Object) - O(log n) - binarySearch
          indexOf(Object)  - O(log n) - binarySearch
insert:   add(E)           - O(n)     - binarySearch + array shift
delete:   remove(int)      - O(n)     - array shift
          remove(Object)   - O(n)     - binarySearch + array shift

add(E)方法違反了List定義(追加),但與Collection定義一致。

以下方法應引發UnsupportedOperationException

add(int index, E element)
addAll(int index, Collection<? extends E> c)
set(int index, E element)

如果不允許重復值(這可能是一個邏輯限制),請考慮同時實現NavigableSet ,它是SortedSet

構建由ArrayList和TreeSet支持的自定義集合。 委派對ArrayList的隨機訪問和對TreeSet的搜索。 當然,這意味着每次寫操作都將非常昂貴,因為每次都必須對ArrayList進行排序。 但是讀取應該非常有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM