如何在不使用Collections.sort()的情况下在Java中获取排序列表行为?

时间:2015-10-24 08:48:43

标签: java collections sortedcollection

据我所知,由于各种概念原因,Java没有排序列表,但考虑到我需要有一个类似优先级队列的集合,但也允许我随机访问(可索引),换句话说,我需要一个遵循特定顺序的List。我希望 来使用Collections.sort()

优先操作限制:

检索 - O(1)(基于索引的随机访问)
search - O(log n)
insert - O(log n)
删除 - O(log n)

集合上的迭代器应该为我提供 排序顺序 中的所有元素(基于在实例化数据结构期间提供的预定义Comparator

我更愿意使用Java的内置库来实现这一目标,但也可以随意建议使用外部库。

编辑: TreeSet不会像基于索引的访问那样困难,使用包装器集合也不是我的最佳选择,因为删除意味着我需要从两个集合中删除。

EDIT2:我无法找到indexable skip list的实现和/或文档,这似乎有点相关,有人可以帮我找到它吗?对于所提议的数据结构的任何评论或欢迎也是受欢迎的。

EDIT3:虽然这可能不是最完美的答案,但我想添加我编写的这段代码,以便任何对排序列表的需求有类似问题的人如果发现它有用就可以使用它。 / p>

检查错误(如果有),并建议改进(特别是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;
    }
}

3 个答案:

答案 0 :(得分:1)

在同一数据结构中很难得到O(1)索引和O(log n)插入/删除。 O(1)索引意味着我们无法承担索引树,列表,跳过列表或其他基于链接的数据结构所涉及的链接跟踪,而O(log n)修改意味着我们无法转移一半每次插入时数组的元素。我不知道是否有可能同时满足这些要求。

如果我们放松其中一项要求,事情就变得容易了。例如,所有操作的O(log n)可以通过indexable skip list或自平衡BST实现,其中节点跟踪以节点为根的子树的大小。但是,这些都不能构建在Java标准库中的跳过列表或BST之上,因此您可能需要安装另一个库或编写自己的数据结构。

O(1)索引,O(log n)搜索和O(n)插入和删除可以通过保持排序的ArrayList并使用Collections.binarySearch来搜索元素或插入/删除位置来完成。您永远不需要调用Collections.sort,但仍需要调用ArrayList的O(n)插入和删除方法。这可能是构建Java内置工具之上的最简单选择。请注意,对于最近的Java版本,Collections.sort是一个自适应合并输出,需要O(n)时间对数组进行排序,其中只有最后一个元素不在排序顺序中,因此您可能会依赖于{ {1}}。但是,这是替代Java实现不必遵循的实现细节。

答案 1 :(得分:1)

如果你的主要目标是索引查找的O(1)(get()),那么你可以使用List实现你自己的实现Arrays.binarySearch()的类,由数组支持。< / p>

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)

如果不允许重复值,这可能是一个逻辑限制,请考虑实施NavigableSetSortedSet

答案 2 :(得分:0)

构建由ArrayList和TreeSet支持的自定义集合。委托对ArrayList的随机访问和对TreeSet的搜索。当然,这意味着每次写入操作都非常昂贵,因为每次都必须对ArrayList进行排序。但读取应该非常有效。