19

所以我的程序需要一种循环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 索引之外的任何其他元素访问除第一个和最后一个元素之外的任何其他元素,我希望它抛出异常。

4

5 回答 5

33

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

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

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

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

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

于 2013-09-06T14:16:52.727 回答
12

你不能从 ArrayList 派生并沿着这些线覆盖 get(int index) 方法:

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

    return super.get(index);
}

我错过了什么?

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

于 2013-09-06T14:15:46.473 回答
11

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

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

  • 为接口创建一个包装类List<T>,我们现在称它为 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 实例也会改变
于 2013-09-06T14:12:55.233 回答
2

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

大小 => 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());
  }

}
于 2017-01-26T19:21:43.997 回答
0

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

于 2014-05-23T18:57:49.030 回答