web-dev-qa-db-fra.com

Circular ArrayList (extension de ArrayList)

Mon programme a donc besoin d’un type de ArrayList circulaire.

La seule chose circulaire à ce sujet doit être la méthode get (int index), c'est l'original:

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

Si index est -1, il doit obtenir l'élément d'index ArrayList.size () - 1 et si index est ArrayList.size (), il doit obtenir l'élément d'indice 0.

Le moyen le plus simple d’atteindre cet objectif, qui m’est venu à l’esprit, consiste simplement à étendre ArrayList à partir du package Java.util et à redéfinir simplement get (int index) afin qu’il ne lève pas IndexOutOfBoundsException pour les deux index ci-dessus, mais les remplace par ce que je veux. Il déclencherait IndexOutOfBoundsException pour tout autre index hors limites.

Cependant, depuis elementData (index) access a

private transient Object[] elementData;

Je ne peux pas le faire fonctionner, parce que ma classe ne le voit pas car c'est privé.

De plus, je ne veux pas utiliser de bibliothèques externes pour cela, simplement parce que je pense qu’aucune ne correspond à mes besoins, car je ne veux pas d’un vrai circularArray, mais seulement une partie de ses fonctionnalités, le reste étant de la liste de tableaux normale.

J'ai donc deux questions:

Comment puis-je faire ce travail? Y a-t-il un moyen de le faire sans copier toute la classe ArrayList avec AbstractCollection, Collection et Iterable dans mon programme? Cela semble être une mauvaise conception même pour moi.

Si je peux le faire fonctionner, y at-il autre chose que je devrais surveiller? Si j'apporte les modifications décrites ci-dessus, cela changerait-il le comportement de la classe uniquement comme je le souhaite, ou pourrait-il y avoir d'autres changements de comportement indésirables?

EDIT: .__ Merci pour la réponse, voici ce que j'ai fait:

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

Il s’enroulera autour de ArrayList, mais par un seul. Je veux qu'il jette une exception si j'essaie d'accéder à tout autre élément que le premier et le dernier avec quoi que ce soit sauf leurs index ArrayList habituels.

14
Karlovsky120

Ne pouvez-vous pas dériver de ArrayList et substituer la méthode get (int index) le long de ces lignes:

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

    return super.get(index);
}

Qu'est-ce que je rate?

Notez que cette implémentation ne plierait pas des index arbitraires dans votre plage d'index valide, mais vous permettrait seulement d'adresser correctement votre liste des côtés gauche et droit (avec des index positifs et négatifs respectivement, un peu comme en Python).

8
HerrB

Vous pouvez étendre la classe ArrayList pour modifier les fonctionnalités de la méthode get sans avoir à accéder au champ elementData:

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

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

La méthode super.get effectuera toujours les vérifications de plage (mais celles-ci n'échoueront jamais).

Vous devez savoir que cela peut donner aux index instables ArrayList. Si la taille de la liste change, tous les index en dehors de la plage normale changeront. Par exemple, si vous avez une liste ['a','b','c','d','e'], alors get(7) renverra c. Si vous faites ensuite add('f'), alors get(7) renverra soudainement b, car get fonctionnera désormais modulo 6 au lieu de modulo 5.

26
Ghostkeeper

Ce que vous avez décrit consiste essentiellement à obtenir le module de l'index souhaité et à accéder à cet élément dans une liste.

Vous pouvez faire ce qui suit avec la composition sur l'héritage:

  • Créez une classe wrapper pour l'interface List<T>, appelons-la ListWrapper maintenant
    • ajouter un constructeur acceptant une instance de liste
    • laissez l'instance de liste être protégée et nommez-la wrapped
  • Étendre la classe wrapper

Pourquoi toutes ces conneries? Ceci est indépendant de la mise en œuvre. Un jour, vous voudrez peut-être utiliser cette commodité pour une autre implémentation. Ensuite, vous devrez dupliquer le code, et l'enfer commence. Si vous avez également besoin d'une 3ème implémentation et que vous ajoutez ensuite une toute nouvelle fonctionnalité, vous êtes condamné.

Avec une classe wrapper entre:

  • vous pouvez avoir toutes les classes implémentant l'interface List pour avoir votre propre fonctionnalité
  • vous serez en mesure de changer la classe d'emballage en un seul endroit
  • vous pourrez ajouter de nouvelles fonctionnalités en un seul endroit.

N'oubliez pas que nous écrivons des programmes qui devront être maintenables!

Classe d'emballage

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
}

Maintenant la vraie nouvelle classe

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

}

IL FAUT SE M&EACUTE;FIER

  • ce n'est cependant pas sans danger pour les environnements multithread!
  • faites attention à toutes les instances de la liste d'origine - si vous faites une mutation, l'instance de ModList mue aussi
11
ppeterka

La réponse choisie ne gère pas le cas où l’indice est un nombre négatif avec une très grande magnitude et la taille de la liste est petite, c.-à-d.

Taille => 10 Index => -1000000

Voici une implémentation qui devrait gérer toutes les tailles et tous les index

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

}
2
Stuart Clark

Est-ce que quelqu'un connaît cette extension AbstractList: com.Sun.appserv.management.util.misc.CircularList<T>. Jetez un coup d'oeil. C'est la solution de communauté GlassFish Java.net. Il devrait être puissant, car il est utilisé dans la planification des threads dans le conteneur GlassFish.

0
red1kissi