【问题标题】:Java combination of Set and List interfacesJava 结合 Set 和 List 接口
【发布时间】:2011-12-08 12:15:54
【问题描述】:

我有一个数据结构,我目前正在为此使用ArrayList。我意识到在这个结构中我不希望出现任何重复。我的第一个想法是使用某种形式的集合,但是顺序也很重要。经过一番谷歌搜索和收集文档后,我发现 LinkedHashSet 几乎可以完成这项工作。不幸的是,保留顺序的主要原因之一是因为我使用 ArrayList 的 get(int index) 方法进行随机访问,而我看不到任何解决方法。

更简洁 - 我需要一个保持顺序并允许随机访问的集合。到目前为止,我看过的所有类都没有提供此功能。有谁知道提供这个的课程,还是我必须自己做?如果是后一种情况,在创建这样一个人们都知道的结构时是否有任何陷阱?

(或者,一种从 ArrayList 或类似结构中检查和删除重复项的快速简便的方法就足够了)

编辑:为清楚起见,重要的是元素添加到列表中的顺序,而不是它们相互比较的方式

【问题讨论】:

  • 使用 TreeMap.
  • 您可以在类型和整数之间使用双向映射。 commons.apache.org/collections/api-3.1/org/apache/commons/… 这样既可以保证唯一性,又可以快速查找。不过,您将需要一个用于新整数键的全局计数器。
  • @Maurício:这行不通。 Map 允许重复值。限制仅在键上。

标签: java list collections set


【解决方案1】:

SetUniqueList 来自 commons-collections:

List<Foo> uniqueList = SetUniqueList.decorate(new ArrayList<Foo>());

(不幸的是,commons-collections 仍然不支持泛型,因此您必须在此处禁止显示警告)

【讨论】:

  • 它使用内部 Set 来进行存在性检查,因此在速度方面它是一个足够好的解决方案,即使它会花费一些额外的内存。
【解决方案2】:

我只是扩展ArrayList

public class SetList<E> extends ArrayList<E> {

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

    @Override
    public void add(int index, E e) {
        if (!contains(e)) {
            super.add(index, e);
        }
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        return addAll(size(), c);
    }

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

}

注意add() 方法符合the contract

确保此集合包含指定的元素(可选操作)。如果此集合因调用而更改,则返回 true。 (如果此集合不允许重复且已包含指定元素,则返回 false。)

【讨论】:

  • 如果客户将此类型用作 List,他们是否可以插入重复项,从而破坏消除重复项的 Set 合同?
  • 那将是非常无效的:ArrayList.contains() 进行顺序搜索;也就是说,如果您有一百万个元素,并且需要再添加一个,则必须进行一百万次比较。
【解决方案3】:

如何创建一个 AbstractList 的子类,将 ArrayList 作为其后备存储,覆盖大多数方法以将它们委托给后备存储,并覆盖 add() 以拒绝重复项?

class NoDupesList<E> extends AbstractList<E> {
    private final List<E> backing;

    NoDupesList() {
        backing = new ArrayList<E>();
    }

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

    // ...

    public boolean contains(Object o) {
        return backing.contains(o);
    }

    public boolean add(E e) {
        if (contains(e))
            throw new IllegalArgumentException("duplicates disallowed: " + e):

        return backing.add(e);
    }
 }

【讨论】:

  • 集合不抛出异常。他们替换了元素。
  • 如果您要走这条路,并且内存效率不是最重要的,您可以同时保留ArrayListHashSet 作为后备存储。如果你走这条路,只有删除操作——remove(int)remove(Object) 等——将是 O(n)。
  • Duh :P 好吧,我很厚,我可以扩展 ArrayList 和 ovveride add() 来拒绝重复,我什至不需要后备列表。这个答案向我指出了这一点。
  • 如果您使用Guava,扩展ForwardingList 可能有助于最大限度地减少样板文件。
  • add() 方法不符合该方法的约定。它不应该在重复时抛出异常,而只返回false。请注意,总共有 4 个add 方法需要实现。
【解决方案4】:

您可以同时使用 ArrayList 和 HashMap:

import java.util.*;

class AS<T>{

    private HashMap<T, Integer> m = new HashMap<T, Integer>();
    private ArrayList<T> a = new ArrayList<T>();

    public void add(T object){
        if (!m.containsKey(object)){
            m.put(object, a.size());
            a.add(object);
        }
    }
    public void remove(T object){
        Integer i = m.get(object);
        if (i!=null){
            a.remove(i.intValue());
            m.remove(object);
        }
    }
    public void remove(int index){
        m.remove(a.get(index));
        a.remove(index);
    }
    public T get(int index){
        return a.get(index);
    }

    public String toString(){return a.toString();}
}

【讨论】:

  • 如果集合实现List接口会更好
猜你喜欢
  • 2018-12-16
  • 2014-05-25
  • 1970-01-01
  • 1970-01-01
  • 2011-02-13
  • 2013-08-19
  • 1970-01-01
  • 1970-01-01
  • 2014-01-16
相关资源
最近更新 更多