2013-09-06 29 views
10

Tak więc mój program potrzebuje typu okrągłej tablicy ArrayList.Circular ArrayList (rozszerzenie ArrayList)

Tylko okrągły rzeczą musi być metoda get (int index), jest to oryginalny:

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

Jeśli indeks jest -1 powinien dostać element z indeksu ArrayList.size() - 1 i jeśli indeks to ArrayList.size(), powinien otrzymać element o indeksie 0.

Najprostszym sposobem osiągnięcia tego, co przyszło mi do głowy, jest po prostu rozszerzenie ArrayList z pakietu java.util i po prostu przesłonięcie get (int index), więc nie rzuca IndexOutOfBoundsException dla dwóch indeksów powyżej, ale zmień je na to, co chcę. Wyrzuciłoby wyjątek IndexOutOfBoundsException dla każdego innego indeksu, który jest poza zakresem.

Jednak od elementData (indeks) dostęp do

private transient Object[] elementData; 

Nie mogę pracować, bo moja klasa nie widzi go, ponieważ jest prywatne.

Poza tym nie chcę używać do tego żadnych zewnętrznych bibliotek, ponieważ uważam, że nie ma w nim nic, co odpowiadałoby moim potrzebom, ponieważ nie chcę prawdziwej okrągłej tablicy, ale tylko części jej funkcjonalności, odpoczynku jest to zwykła tablica ArrayList.

Mam więc dwa pytania:

Jak mogę to wykonać? Czy istnieje sposób, aby to zrobić bez kopiowania całej klasy ArrayList wraz z AbstractCollection, Collection i Iterable do mojego programu? To wydaje mi się złym projektem nawet dla mnie.

Jeśli mogę jakoś sprawić, żeby działało, czy jest coś jeszcze, czego powinienem uważać? Jeśli dokonam opisanych powyżej zmian, czy to zmieniłoby zachowanie klasy tylko tak, jak tego chcę, czy też może nastąpiły jakieś niepożądane zmiany w zachowaniu?

EDIT: Dzięki za odpowiedź, oto co zrobiłem:

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

Będzie owinąć wokół ArrayList, ale tylko o jeden. Chcę, aby rzucił wyjątek, jeśli próbuję uzyskać dostęp do dowolnego innego elementu oprócz pierwszego i ostatniego z czymkolwiek poza zwykłymi indeksami ArrayList.

+2

Czy próbowałeś tylko przy użyciu funkcji, która przekłada swój indeks do ważnej wartości? Podobnie jak "index = index% list.size();", po którym następuje 'if (index <0) index = list.size() + index;'. – SamYonnou

Odpowiedz

6

nie można czerpać z ArrayList i zastąpić metodę get (int index) wzdłuż tych linie:

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

    return super.get(index); 
} 

Czego mi brakuje?

Należy zauważyć, że ta implementacja nie spowoduje złożenia dowolnych indeksów w prawidłowym zakresie indeksów, ale pozwoli jedynie poprawnie adresować listę z lewej i prawej strony (odpowiednio z indeksami dodatnimi i ujemnymi, podobnie jak w języku Python).

+0

Całkowicie zapomniałem o" super ". Dodałem, co postanowiłem zrobić w mojej odpowiedzi. To powinno zadziałać, prawda? I nie będzie żadnych innych zmian spowodowanych przez to? – Karlovsky120

+0

Nigdy nie rozszerzaj tych klas. Będziesz związany z jedną implementacją. Co się stanie, jeśli chcesz mieć tę funkcję również w LinkedList? Działa to również dla zakresu [-sizeOfList; sizeOfList] i nie będzie działać dla żadnych wartości poza tym zakresem. – ppeterka

+0

Ja i tak nie potrzebuję tego do pracy. Po prostu potrzebuję, aby nakładka pokrywała się z jednym, a nie z rozmiarem listy lub nieokreślonymi rozmiarami listy ... Byłoby to nawet złe, ponieważ nie chcę mieć dostępu do listy w ten sposób, i robić to może wyrzucałby wyjątki, ułatwiając naprawianie błędów. – Karlovsky120

10

To, co opisano, polega na uzyskaniu modułu żądanego indeksu i uzyskaniu dostępu do tego elementu na liście.

Można wykonać następujące czynności kompozycją nad dziedziczenia:

  • utworzyć klasy otoki dla interfejsu List<T>, nazwijmy to ListWrapper teraz
    • dodać konstruktora zaakceptowaniem wystąpienie listy
    • let instancja List jest chroniona i należy ją nazwać na wrapped
  • Rozszerz klasę opakowania

Dlaczego to wszystko bzdura? To jest agnostyka implementacji. Któregoś dnia możesz chcieć skorzystać z tej wygody przy innej implementacji. Wtedy będziesz musiał skopiować kod, a piekło się zacznie. Jeśli potrzebujesz też trzeciej implementacji, a następnie dodasz tylko jedną niewielką część nowej funkcjonalności, jesteś skazany na zagładę.

Z klasy otoki w pomiędzy:

  • można mieć wszystkich klas implementujących interfejs List mieć własną functinality
  • będziesz mógł zmienić klasy otoki w jednym miejscu
  • będziesz mógł dodawać nowe funkcje w jednym miejscu.

Pamiętaj, że piszemy programy, które będą musiały być utrzymywane!

klasa 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 
} 

Teraz prawdziwa nowa klasa

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

} 

UWAGA

  • to jednak nie jest bezpieczne dla Multithread Ed środowiska!
  • uważać na wszystkich wystąpień na pierwotnej liście - jeśli mutować, że instancja Modlist będzie mutować zbyt
+0

Dla mojego programu, jestem prawie pewien, że nie będę potrzebował tej funkcjonalności, ale jest to dobra rada dotycząca rozszerzania klasy. Nadal mogę go używać wszędzie, gdzie ... – Karlovsky120

+2

uwaga: To jest znane jako wzór "Dekoratora". – njzk2

+1

Świetna odpowiedź, ale uważam, że w innym przypadku po ":" brakuje i powinno to być '<' zamiast '>' 'indexToGet = indexToGet> 0? IndexToGet + listSize;' powinno być: 'indexToGet = (indexToGet < 0)? IndexToGet + listSize: indexToGet; ' – dsantaolalla

22

można rozszerzyć klasę ArrayList do zmiany funkcjonalności sposobu get, bez konieczności uzyskania dostępu do pola elementData:

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

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

Sposób super.get będzie jeszcze wykonać testy zakresu (ale te nigdy nie zawiedzie).

Należy mieć świadomość, że w ten sposób można uzyskać niestabilne indeksy ArrayList. Jeśli zmieni się rozmiar listy, wszystkie indeksy poza normalnym zakresem ulegną zmianie. Na przykład, jeśli masz listę ['a','b','c','d','e'], wtedy get(7) zwróci c. Jeśli następnie zrobić add('f'), następnie get(7) nagle powrócić b, ponieważ get zostanie modulo 6 pracuje zamiast modulo 5.

+4

Nigdy nie rozszerzaj tych klas. Będziesz związany z jedną implementacją. Co się stanie, jeśli chcesz mieć tę funkcję również w LinkedList? Również operator% zwraca numery NEGATYWNE dla wejść ujemnych. – ppeterka

+0

Masz rację co do negatywnych wskaźników. Ale ponieważ to rozszerzenie używa tylko metod dostępnych przez interfejs 'List' (' get'), będzie działać z każdą implementacją 'List'. Musisz tylko zmienić rozszerzoną klasę, na 'LinkedList' lub coś podobnego. – Ghostkeeper

+1

A jeśli potrzebujesz tego również dla ArrayList i LinkedList oraz FastList na javolution? Będziesz potrzebował 3 oddzielnych klas. I tylko niewielka zmiana lub nowa funkcja: 3 razy bardziej podatna na błędy. – ppeterka

0

Czy ktoś zna ten AbstractList rozszerzenia: com.sun.appserv.management.util.misc.CircularList<T>. Spójrz na to. To rozwiązanie społecznościowe GlassFish java.net. Powinien być potężny, ponieważ jest używany w planowaniu wątków wewnątrz kontenera GlassFish.

1

Wybrany odpowiedź nie obsługuje przypadek, gdy indeks jest ujemna liczba o bardzo dużej skali i wielkości liście jest mały IE

Size => 10 Index => -1000000

Oto implementacji, które powinny obsługiwać wszystkie rozmiary i indeksów

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

}