简体   繁体   English

圆形ArrayList(扩展ArrayList)

[英]Circular ArrayList (extending ArrayList)

So my program has a need of a type of circular ArrayList. 所以我的程序需要一种圆形ArrayList。

Only circular thing about it has to be the get(int index) method, this is the original: 关于它的只有圆形的东西必须是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);
    }

If index is -1 it should get the element with index ArrayList.size()-1 and if index is ArrayList.size(), it should get the element with index 0. 如果index为-1,则应该获取索引为ArrayList.size() - 1的元素,如果index为ArrayList.size(),则应获取索引为0的元素。

Simplest way of achieveing this which came to my mind is simply extending ArrayList from the java.util package and just overriding the get(int index) so it does not throw IndexOutOfBoundsException for the two indexes above, but change them to what I want. 我想到的最简单的实现方法就是简单地从java.util包中扩展ArrayList,然后重写get(int index),这样它就不会为上面的两个索引抛出IndexOutOfBoundsException,而是将它们改为我想要的。 It would throw IndexOutOfBoundsException for any other index that is out of bounds. 它会为任何其他超出范围的索引抛出IndexOutOfBoundsException。

However, since elementData(index) access a 但是,由于elementData(索引)访问a

private transient Object[] elementData;

I cannot make it work, because my class doesn't see it since it's private. 我无法使它工作,因为我的班级没有看到它,因为它是私人的。

Also, I don't want to use any external libraries for this, simply because I think there are none that suit my needs, since I don't want a real circularArray, but only a part of it's functionality, rest of it being of the regular ArrayList. 此外,我不想为此使用任何外部库,只是因为我认为没有一个适合我的需求,因为我不想要一个真正的circularArray,但只是它的一部分功能,其余部分是常规的ArrayList。

So I have two questions: 所以我有两个问题:

How can I make this work? 我怎样才能做到这一点? Is there a way to do it without copying the whole ArrayList class along with AbstractCollection, Collection and Iterable into my program? 有没有办法做到这一点,而无需将整个ArrayList类与AbstractCollection,Collection和Iterable一起复制到我的程序中? That seems like bad design even to me. 这对我来说似乎是糟糕的设计。

If I can somehow make it work, is there anything else I should watch for? 如果我能以某种方式使它发挥作用,还有什么我应该注意的吗? If I make the changes described above, would that change the behaviour of the class only the way I want it to, or could there be any other undesired behaviour changes? 如果我进行上述更改,是否会以我希望的方式更改类的行为,还是会有任何其他不需要的行为更改?

EDIT: Thanks for the answer, here's what I've done: 编辑:谢谢你的回答,这就是我所做的:

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

It will wrap around the ArrayList, but only by one. 它将环绕ArrayList,但只包含一个。 I want it to throw an exception if I try to access any other element but the first and the last with anything except their regular ArrayList indexes. 如果我尝试访问除了常规ArrayList索引之外的任何其他元素,除了第一个和最后一个元素,我希望它抛出一个异常。

You can extend the ArrayList class to change the functionality of the get method, without the need to access the elementData field: 您可以扩展ArrayList类以更改get方法的功能,而无需访问elementData字段:

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

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

The super.get method will still perform the range checks (but those will never fail). super.get方法仍将执行范围检查(但这些检查永远不会失败)。

You should be aware that doing this can give the ArrayList unstable indices. 您应该知道这样做可以给ArrayList提供不稳定的索引。 If the size of the list changes, then all indices outside of the normal range will change. 如果列表的大小发生变化,则正常范围之外的所有索引都将发生变化。 For instance, if you have a list ['a','b','c','d','e'] , then get(7) will return c . 例如,如果你有一个列表['a','b','c','d','e'] ,那么get(7)将返回c If you then do add('f') , then get(7) will suddenly return b , because get will now be working modulo 6 instead of modulo 5. 如果你然后add('f') ,那么get(7)将突然返回b ,因为get现在将以模6而不是模5运行。

What you described is basically getting the modulus of the index you want, and accessing that element in a list. 您所描述的基本上是获取所需索引的模数,并在列表中访问该元素。

You could do the following with composition over inheritance: 您可以使用继承的组合执行以下操作:

  • Create a wrapper class for the interface List<T> , let's call it ListWrapper now 为接口List<T>创建一个包装类,让我们现在称它为ListWrapper
    • add a constructor accepting instance of List 添加一个接受List实例的构造函数
    • let the List instance be protected, and name it to wrapped 让List实例受到保护,并将其命名为wrapped
  • Extend the wrapper class 扩展包装类

Why do all this crap? 为什么所有这些废话? This is implementation agnostic. 这是实现不可知的。 One day, you might want to use this convenience on another implementation. 有一天,您可能希望在另一个实现上使用这种便利。 Then you'll have to duplicate code, and hell begins. 然后你将不得不重复代码,地狱开始了。 If you need a 3rd implementation too, and then add just one tiny bit of new functionality, you are doomed. 如果您还需要第三个实现,然后只添加一小部分新功能,那么您将注定失败。

With a wrapper class in between: 两者之间有一个包装类:

  • you can have all classes implementing the List interface to have your own functinality 您可以让所有实现List接口的类具有您自己的功能
  • you'll be able to change the wrapper class in one place 你将能够在一个地方更改包装类
  • you'll be able to add new functionality in one place. 您将能够在一个地方添加新功能。

Remember, we are writing programs that will have to be maintainable! 请记住,我们正在编写必须可维护的程序!

Wrapper class 包装类

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
}

Now the real new class 现在是真正的新课

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

}

BEWARE 谨防

  • this however is not safe for multithreaded environments! 但是这对于多线程环境来说并不安全!
  • be careful about all the instances of the original list - if you mutate that, the ModList instance will mutate too 小心原始列表的所有实例 - 如果你改变它,ModList实例也会变异

Can't you derive from ArrayList and override the the get(int index) method along those lines: 你不能从ArrayList派生并覆盖这些行的get(int index)方法:

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

    return super.get(index);
}

What am I missing? 我错过了什么?

Note that this implementation would not fold arbitrary indices into your valid index range but only allow you to properly address your list from both the left and right sides (with positive and negative indices respectively, a bit like in Python). 请注意,此实现不会将任意索引折叠到您的有效索引范围内,而只允许您从左侧和右侧正确地寻址您的列表(分别使用正索引和负索引,有点像Python)。

The chosen answer doesn't handle the case where the index is a negative number with a very large magnitude and the size of the list is small ie 所选择的答案不处理索引是具有非常大的幅度的负数并且列表的大小很小的情况

Size => 10 Index => -1000000 大小=> 10指数=> -1000000

Here is an implementation that should handle all sizes and indexes 这是一个应该处理所有大小和索引的实现

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

}

Does anyone know this AbstractList extension : com.sun.appserv.management.util.misc.CircularList<T> . 有谁知道这个AbstractList扩展: com.sun.appserv.management.util.misc.CircularList<T> Take a look at it. 看看吧。 It's GlassFish java.net community solution. 这是GlassFish java.net社区解决方案。 It should be powerful because it's used in Thread Scheduling inside GlassFish Container. 它应该很强大,因为它在GlassFish Container中的Thread Scheduling中使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM