圆形ArrayList(扩展ArrayList)

所以我的程序需要一种圆形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(索引)访问a

 private transient Object[] elementData; 

我无法使它工作,因为我的class级没有看到它,因为它是私人的。

此外,我不想为此使用任何外部库,只是因为我认为没有一个适合我的需求,因为我不想要一个真正的circularArray,但只是它的一部分function,其余部分是常规的ArrayList。

所以我有两个问题:

我怎样才能做到这一点? 有没有办法做到这一点,而无需将整个ArrayList类与AbstractCollection,Collection和Iterable一起复制到我的程序中? 这对我来说似乎是糟糕的设计。

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

编辑:谢谢你的回答,这就是我所做的:

 import java.util.ArrayList; public class CircularArrayList extends ArrayList { 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,但只包含一个。 我希望它抛出一个exception,如果我尝试访问任何其他元素,但第一个和最后一个除了常规ArrayList索引之外的任何东西。

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

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

我错过了什么?

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

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

 public class CircularList extends ArrayList { @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运行。

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

您可以使用组合而不是inheritance来执行以下操作:

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

为什么所有这些废话? 这是实现不可知的。 有一天,您可能希望在另一个实现中使用此便利性。 然后你将不得不重复代码,地狱开始了。 如果您还需要第三个实现,然后只添加一小部分新function,那么您将注定失败。

在两者之间使用包装类:

  • 您可以让所有实现List接口的类具有您自己的function
  • 你将能够在一个地方更改包装类
  • 您将能够在一个地方添加新function。

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

包装类

 public abstract class ListWrapper implements List { protected final List wrapped; public ListWrapper(List 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 extends ListWrapper { public ModList(List 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); } } 

谨防

  • 但是这对于multithreading环境来说并不安全!
  • 小心原始列表的所有实例 - 如果你改变它,ModList实例也会变异

所选择的答案不处理索引是具有非常大的幅度的负数并且列表的大小很小的情况

大小=> 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 extends ArrayList { public CircularList() { super(); } public CircularList(int initialCapacity) { super(initialCapacity); } public CircularList(Collection 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()); } } 

有谁知道这个AbstractList扩展: com.sun.appserv.management.util.misc.CircularList 。 看看吧。 这是GlassFish java.net社区解决方案。 它应该很强大,因为它在GlassFish Container中的Thread Scheduling中使用。