Circular 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);
}
Если индекс равен -1, он должен получить элемент с индексом ArrayList.size() - 1, а если index - ArrayList.size(), он должен получить элемент с индексом 0.
Самый простой способ добиться этого, который пришел мне на ум, - это просто расширить ArrayList из пакета java.util и просто переопределить индекс get (int), поэтому он не бросает IndexOutOfBoundsException для двух указанных выше индексов, но меняет их на то, что я хотеть. Он будет генерировать IndexOutOfBoundsException для любого другого индекса, который выходит за рамки.
Однако, поскольку elementData (index) получает доступ к
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.
Ответы
Ответ 1
Вы не можете получить из ArrayList и переопределить метод get (int index) по этим строкам:
@Override
public E get(int index)
{
if(index < 0)
index = index + size();
return super.get(index);
}
Что мне не хватает?
Обратите внимание, что эта реализация не сворачивала бы произвольные индексы в ваш допустимый диапазон индексов, а только позволяла вам правильно адресовать ваш список как с левой, так и с правой стороны (с положительными и отрицательными индексами соответственно, немного как на Python).
Ответ 2
Вы можете расширить класс 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.
Ответ 3
То, что вы описали, в основном получает модуль нужного вам индекса и доступ к этому элементу в списке.
Вы можете сделать следующее с композицией над наследованием:
- Создайте класс оболочки для интерфейса
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);
}
}
BEWARE
- однако это небезопасно для многопоточных сред!
- будьте осторожны со всеми экземплярами исходного списка - если вы его мутируете, экземпляр ModList тоже будет мутировать.
Ответ 4
Выбранный ответ не обрабатывает случай, когда индекс является отрицательным числом с очень большой величиной, а размер списка мал i.e.
Размер = > 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());
}
}
Ответ 5
Кто-нибудь знает это расширение AbstractList: com.sun.appserv.management.util.misc.CircularList<T>
. Взгляните на это. Это решение сообщества GlassFish java.net. Он должен быть мощным, потому что он использовался при планировании потоков внутри контейнера GlassFish.