【问题标题】:Circular ArrayList (extending ArrayList)循环ArrayList(扩展ArrayList)
【发布时间】:2013-09-10 16:09:07
【问题描述】:

所以我的程序需要一种循环 ArrayList。

唯一循环的就是 get(int index) 方法,这是原来的:

    /**
     * Returns the element at the specified position in this list.
     *
     * @param  index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */ 
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

如果 index 为 -1,它应该获取索引为 ArrayList.size()-1 的元素,如果 index 为 ArrayList.size(),它应该获取索引为 0 的元素。

我想到的最简单的实现方法是简单地从 java.util 包中扩展 ArrayList 并覆盖 get(int index),这样它就不会为上面的两个索引抛出 IndexOutOfBoundsException,而是将它们更改为我的想。对于任何其他超出范围的索引,它将引发 IndexOutOfBoundsException。

但是,由于 elementData(index) 访问了一个

private transient Object[] elementData;

我无法让它工作,因为我的班级看不到它,因为它是私有的。

另外,我不想为此使用任何外部库,只是因为我认为没有适合我需要的库,因为我不想要一个真正的圆形数组,而只想要它的一部分功能,其余的它属于常规的 ArrayList。

所以我有两个问题:

我怎样才能做到这一点?有没有办法在不将整个 ArrayList 类以及 AbstractCollection、Collection 和 Iterable 复制到我的程序中的情况下做到这一点?即使对我来说,这似乎也是糟糕的设计。

如果我能以某种方式让它发挥作用,我还有什么需要注意的吗?如果我进行上述更改,是否会仅按照我希望的方式更改类的行为,还是会出现任何其他不希望的行为更改?

编辑: 感谢您的回答,这是我所做的:

import java.util.ArrayList;

public class CircularArrayList<E> extends ArrayList<E>
{
    private static final long serialVersionUID = 1L;

    public E get(int index)
    {
        if (index == -1)
        {
            index = size()-1;
        }

        else if (index == size())
        {
            index = 0;
        }

        return super.get(index);
    }
}

它将环绕 ArrayList,但仅环绕一个。如果我尝试使用除常规 ArrayList 索引之外的任何其他元素访问除第一个和最后一个元素之外的任何其他元素,我希望它抛出异常。

【问题讨论】:

  • 您是否尝试过仅使用将索引转换为有效值的函数?喜欢index = index % list.size();,后跟if (index &lt; 0) index = list.size() + index;

标签: java arraylist extending circular-list


【解决方案1】:

您可以扩展 ArrayList 类来更改get 方法的功能,而无需访问elementData 字段:

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

    @Override
    public E get(int index) {
        return super.get(index % size());
    }
}

super.get 方法仍将执行范围检查(但这些检查永远不会失败)。

您应该知道,这样做可能会给 ArrayList 带来不稳定的索引。如果列表的大小发生变化,那么所有超出正常范围的索引都会发生变化。例如,如果您有一个列表['a','b','c','d','e'],那么get(7) 将返回c。如果您随后执行add('f'),那么get(7) 将突然返回b,因为get 现在将使用模6 而不是模5。

【讨论】:

  • 永远不要扩展这些类。您将被绑定到一个实现。如果你想在 LinkedList 中也有这个功能会发生什么?此外,% 运算符为负输入返回负数。
  • 您对负指数是正确的。但由于此扩展仅使用通过 List 接口 (get) 可用的方法,因此它将适用于 List 的任何实现。您只需要将其扩展的类更改为 LinkedList 或其他任何内容。
  • 如果你同时需要 ArrayList 和 LinkedList 以及 javolution 的 FastList 呢?您将需要 3 个单独的课程。只需稍作改动或新功能:3 倍容易出错。
  • 好点。我在一年多前发布了这个。也许我认为它是负指数所必需的,但是模数应该总是给出 0 到 n 之间的结果,所以它不应该有任何影响。我会删除它。
  • @Ghostkeeper 检查两次:模为负输入给出负结果...-5%3==-2
【解决方案2】:

您不能从 ArrayList 派生并按照这些思路覆盖 get(int index) 方法吗:

@Override
public E get(int index)
{
    if(index < 0)
        index = index + size();

    return super.get(index);
}

我错过了什么?

请注意,此实现不会将任意索引折叠到您的有效索引范围内,而只允许您从左侧和右侧正确寻址列表(分别使用正索引和负索引,有点像 Python 中的)。

【讨论】:

  • 我完全忘记了“超级”。我已经将我决定做的事情添加到我的答案中。这应该有效,对吧?并且不会有其他由此引起的行为变化?
  • 永远不要扩展这些类。您将被绑定到一个实现。如果你想在 LinkedList 中也有这个功能会发生什么?此外,这适用于范围 [-sizeOfList;sizeOfList] 并且不适用于此范围之外的任何值。
  • 我不需要它以这种方式工作。我只需要将换行重叠一个,而不是列表大小或无限列表大小......这甚至会很糟糕,因为我不想能够以这种方式访问​​列表,并且这样做可能会会抛出异常,从而更容易修复错误。
  • index > size 或 index
【解决方案3】:

您所描述的基本上是获取所需索引的模数,并访问列表中的该元素。

您可以通过组合而不是继承来执行以下操作:

  • 为接口List&lt;T&gt;创建一个包装类,我们现在称之为ListWrapper
    • 添加一个接受 List 实例的构造函数
    • 让List实例受到保护,命名为wrapped
  • 扩展包装类

为什么要做这些废话?这与实现无关。有一天,您可能希望在另一个实现中使用这种便利。然后你将不得不复制代码,地狱开始了。如果你也需要第三个实现,然后只添加一点点新功能,那你就完蛋了。

中间有一个包装类:

  • 您可以让所有实现 List 接口的类拥有自己的功能
  • 您将能够在一处更改包装类
  • 您可以在一处添加新功能。

请记住,我们正在编写必须可维护的程序!

包装类

public abstract class ListWrapper<T> implements List<T> {
    protected final List<T> wrapped;

    public ListWrapper(List<T> wrapped) {
        this.wrapped = wrapped;
    }

    public T get(int index) {
        return wrapped.get(index);
    }

    //omitting the other wrapper methods, for sake of brevity.
    //Note: you still have to add them.
    // Eclipse: Source menu, Generate Delegate methods does the trick nicely
}

现在是真正的新课程

public class ModList<T> extends ListWrapper<T> {

    public ModList(List<T> list) {
        super(list);
    }

    @Override
    public T get(int index) {
        int listSize = wrapped.size();
        int indexToGet = index % listSize;

        //this might happen to be negative
        indexToGet = (indexToGet < 0) ? indexToGet+listSize : indexToGet;
        return wrapped.get(indexToGet);
    }

}

注意

  • 但是,这对于多线程环境并不安全!
  • 注意原始列表的所有实例 - 如果您对其进行变异,ModList 实例也会发生变异

【讨论】:

  • 对于我的程序,我很确定我不需要这个功能,但它是关于类扩展的好建议。不过,我仍然可以在其他地方使用它...
  • 很好的答案,但我相信 ":" 后面的 else 情况丢失了,它应该是 &lt; 而不是 &gt; indexToGet=indexToGet&gt;0?indexToGet+listSize; 应该是:indexToGet=(indexToGet&lt;0)?indexToGet+listSize:indexToGet;
【解决方案4】:

选择的答案不处理索引是一个非常大的负数并且列表的大小很小的情况,即

尺寸 => 10 指数 => -1000000

这是一个应该处理所有大小和索引的实现

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

/**
 * A list the loops round to the first element when {@link CircularList#get(int)} is called with an
 * index that is greater than the max index of the list and vice versa.
 *
 * @author Stuart Clark
 */
public class CircularList<E> extends ArrayList<E> {

  public CircularList() {
    super();
  }

  public CircularList(int initialCapacity) {
    super(initialCapacity);
  }

  public CircularList(Collection<? extends E> c) {
    super(c);
  }

  @Override
  public E get(int index) {
    if (isEmpty()) {
      throw new IndexOutOfBoundsException("The list is empty");
    }

    while (index < 0) {
      index = size() + index;
    }

    return super.get(index % size());
  }

}

【讨论】:

    【解决方案5】:

    有谁知道这个 AbstractList 扩展名:com.sun.appserv.management.util.misc.CircularList&lt;T&gt;。看看它。这是 GlassFish java.net 社区解决方案。它应该很强大,因为它用于 GlassFish Container 内的线程调度。

    【讨论】:

      猜你喜欢
      • 2017-10-08
      • 2012-07-25
      • 1970-01-01
      • 1970-01-01
      • 2014-06-10
      • 1970-01-01
      • 2018-05-16
      • 2011-12-27
      • 2013-05-19
      相关资源
      最近更新 更多