2013-09-06 2 views
10

Итак, моя программа нуждается в типе кругового массива ArrayList.Circular ArrayList (расширение ArrayList)

только круговая вещь об этом должно быть методом прибудет (индекс INT), это оригинал:

/** 
    * 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.

Простейший способ достичь этого, который пришел мне на ум, - это просто расширить ArrayList из пакета java.util и просто переопределить get (int index), поэтому он не бросает IndexOutOfBoundsException для двух указанных выше индексов, но меняет их на то, что я хочу. Он будет генерировать IndexOutOfBoundsException для любого другого индекса, который выходит за рамки.

Однако, поскольку elementData (индекс) получить доступ к

private transient Object[] elementData; 

Я не могу заставить его работать, потому что мой класс не видит его, так как это личное.

Кроме того, я не хочу использовать какие-либо внешние библиотеки для этого, просто потому, что я думаю, что нет таких, которые бы соответствовали моим потребностям, так как я не хочу реального кругового массива, а только часть его функциональности, отдыхаю из этого является регулярным ArrayList.

Поэтому у меня есть два вопроса:

Как я могу сделать эту работу? Есть ли способ сделать это, не копируя весь класс ArrayList вместе с AbstractCollection, Collection и Iterable в мою программу? Это кажется плохим дизайном даже для меня.

Если я могу как-то заставить его работать, есть ли что-нибудь еще, на что я должен смотреть? Если я сделаю описанные выше изменения, изменит ли поведение класса только то, как я его хочу, или могут возникнуть какие-либо другие нежелательные изменения поведения?

EDIT: Спасибо за ответ, вот что я сделал:

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.

+2

вы пробовали просто использовать функцию, которая переводит ваш индекс в действительное значение? Подобно 'index = index% list.size();' затем следует 'if (index <0) index = list.size() + index;'. – SamYonnou

ответ

6

не могли бы вы вывести из ArrayList и переопределить метод Командлет Get (интермедиат индекс) вдоль тех, линии:

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

    return super.get(index); 
} 

Что мне не хватает?

Обратите внимание, что эта реализация не сбрасывала бы произвольные индексы в ваш допустимый диапазон индексов, а только позволяла вам правильно адресовать ваш список как с левой, так и с правой стороны (с положительными и отрицательными индексами, соответственно, как в Python).

+0

Я полностью забыл о« супер ». Я добавил то, что решил сделать с моим ответом. Это должно работать, правильно? И не будет никаких других изменений поведения, вызванных этим? – Karlovsky120

+0

Никогда не расширяйте эти классы. Вы будете привязаны к одной реализации. Что произойдет, если вы хотите использовать эту функцию в LinkedList? Кроме того, это работает для диапазона [-sizeOfList; sizeOfList] и не будет работать ни для каких значений за пределами этого диапазона. – ppeterka

+0

Мне все равно не нужно так работать. Мне просто нужно, чтобы обертка перекрывалась одним, а не размером списка или неопределенными размерами списка ... Это было бы даже плохо, потому что я не хочу иметь доступ к списку таким образом, и это может сделать будет генерировать исключения, облегчая исправление ошибок. – Karlovsky120

10

То, что вы описали, в основном получает модуль нужного вам индекса и доступ к этому элементу в списке.

Вы можете сделать следующее с составом по наследству:

  • Создания класса-оболочки для интерфейса List<T>, давайте назовем его ListWrapper Теперь
    • добавить конструктор принимающего экземпляр списка
    • пусть экземпляр списка, и назовите его на wrapped
  • Расширить класс обертки

Почему все это дерьмо? Это агностик реализации. В один прекрасный день вы можете использовать это удобство для другой реализации. Тогда вам придется дублировать код, и начинается ад. Если вам нужна и третья реализация, а затем добавьте только один крошечный бит новых функций, вы обречены.

С класса-оболочки между ними:

  • вы можете иметь все классы, реализующие интерфейс List, чтобы иметь свой собственный functinality
  • вы будете иметь возможность изменить класс обертку в одном месте
  • вы сможете добавлять новые функции в одном месте.

Помните, что мы пишем программы, которые необходимо обслуживать!

класс Wrapper

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

} 

BEWARE

  • это, однако, не является безопасным для многопоточных средах!
  • быть осторожным о всех случаях исходного списка - если вы мутировать, что экземпляр ModList мутирует слишком
+0

Для моей программы я уверен, что мне не понадобятся эта функциональность, но это хороший совет по поводу расширения класса. Я все еще могу использовать его, хотя ... – Karlovsky120

+2

note: Это называется шаблоном «Decorator». – njzk2

+1

Отличный ответ, но я считаю, что случай else после «:» отсутствует, и он должен быть '<' вместо '>' 'indexToGet = indexToGet> 0? IndexToGet + listSize;' должен быть: 'indexToGet = (indexToGet < 0)? IndexToGet + listSize: indexToGet; ' – dsantaolalla

22

Вы можете расширить класс 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'), то get(7) вдруг вернется b, потому что get теперь будет работать по модулю 6 вместо модуля 5.

+4

Никогда не расширяйте эти классы. Вы будете привязаны к одной реализации. Что произойдет, если вы хотите использовать эту функцию в LinkedList? Кроме того, оператор% возвращает НЕГАТИВНЫЕ номера для отрицательных входов. – ppeterka

+0

Вы правы в отношении отрицательных индексов. Но поскольку это расширение использует только методы, доступные через интерфейс 'List' (' get'), он будет работать с любой реализацией 'List'. Вам нужно только изменить класс, который он расширяет, на «LinkedList» или что-то еще. – Ghostkeeper

+1

И если вам это нужно для ArrayList и LinkedList, а также для FastList javolution в тоже время? Вам понадобятся 3 отдельных класса. И просто небольшое изменение, или новая функция: 3 раза подвержена ошибкам. – ppeterka

0

Кто-нибудь знает это AbstractList расширения: com.sun.appserv.management.util.misc.CircularList<T>. Взгляните на это. Это решение сообщества GlassFish java.net. Он должен быть мощным, потому что он используется при планировании потоков внутри GlassFish Container.

1

Выбранный ответ не обрабатывает случай, когда индекс является отрицательным числом с очень большой величиной и размером списка мал, т.е.

Size => 10 Index => -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()); 
    } 

} 
Смежные вопросы