圆形ArrayList(扩展ArrayList)

Kar*_*120 16 java arraylist extending circular-list

所以我的程序需要一种圆形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);
    }
Run Code Online (Sandbox Code Playgroud)

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

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

但是,由于elementData(索引)访问a

private transient Object[] elementData;
Run Code Online (Sandbox Code Playgroud)

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

此外,我不想为此使用任何外部库,只是因为我认为没有一个适合我的需求,因为我不想要一个真正的circularArray,但只是它的一部分功能,其余部分是常规的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

它将环绕ArrayList,但只包含一个.我希望它抛出一个异常,如果我尝试访问任何其他元素,但第一个和最后一个除了常规的ArrayList索引之外的任何东西.

Gho*_*per 27

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

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

    @Override
    public E get(int index) {
        return super.get(index % size());
    }
}
Run Code Online (Sandbox Code Playgroud)

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

您应该知道这样做可以给ArrayList提供不稳定的索引.如果列表的大小发生变化,则正常范围之外的所有索引都将发生变化.例如,如果您有一个列表['a','b','c','d','e'],那么get(7)将返回c.如果你这样做add('f'),那么get(7)会突然返回b,因为get现在将以模6运算而不是模5.

  • 不要扩展这些类.你将被绑定到一个实现.如果你想在LinkedList中也有这个功能,会发生什么?此外,%运算符返回负输入的负数. (6认同)
  • 我想知道,为什么你需要Math.abs? (2认同)

ppe*_*rka 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
}
Run Code Online (Sandbox Code Playgroud)

现在是真正的新课

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);
    }

}
Run Code Online (Sandbox Code Playgroud)

谨防

  • 但是这对于多线程环境来说并不安全!
  • 小心原始列表的所有实例 - 如果发生变异的是,ModList情况下会产生变异太

  • 注意:这被称为"装饰者"模式. (2认同)

Her*_*rrB 9

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

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

    return super.get(index);
}
Run Code Online (Sandbox Code Playgroud)

我错过了什么?

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