代码之家  ›  专栏  ›  技术社区  ›  Karlovsky120

循环数组列表(扩展数组列表)

  •  20
  • Karlovsky120  · 技术社区  · 12 年前

    所以我的程序需要一种类型的循环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);
        }
    

    如果索引是-1,它应该获得索引为ArrayList.size()-1的元素;如果索引是ArrayList.size(),它应该得到索引为0的元素。

    我想到的实现这一点的最简单方法是简单地从java.util包扩展ArrayList,并覆盖get(int索引),这样它就不会对上面的两个索引抛出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索引),我希望它抛出一个异常。

    5 回复  |  直到 12 年前
        1
  •  34
  •   Ghostkeeper    11 年前

    您可以扩展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') 然后 得到(7) 会突然回来 b 因为 收到 现在将以模6工作,而不是以模5工作。

        2
  •  12
  •   HerrB    12 年前

    难道你不能从ArrayList派生并沿着以下几行重写get(int index)方法吗:

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

    我错过了什么?

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

        3
  •  11
  •   mbmast    9 年前

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

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

    • 为接口创建包装类 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实例也会改变
        4
  •  2
  •   Stuart Clark    9 年前

    所选择的答案无法处理索引是一个幅度非常大的负数,并且列表的大小很小的情况,即。

    尺寸=>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
  •  0
  •   red1kissi    12 年前

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