ListModel, ce n'est pas sale, parlons-en.

ListModel, ce n'est pas sale, parlons-en. - Java - Programmation

Marsh Posté le 23-12-2003 à 09:35:42    

Je suis dans une application swing, et je suis confronté à des problèmes qui concernent tous ceux qui font du swing, je vais donc partager.
 
Première chose, les listes dont le éléments sont triés;
J'ai actuellement une implémentation fondée sur un TreeMap :

Code :
  1. public class MemberSet extends AbstractListModel {
  2.  private SortedSet model;
  3.  // accessor used for indexed access.
  4.  private Iterator indexedAccessIterator = null;
  5.  /**
  6.   * next aivable index from the iterator.
  7.   */
  8.  private int nextIndex = 0;
  9.  private void reinitIterator() {
  10.   indexedAccessIterator = null;
  11.   nextIndex = 0;
  12.  }
  13.  /**
  14.   * @param membersComparator
  15.   */
  16.  public MemberSet(Comparator membersComparator) {
  17.   model = new TreeSet(membersComparator);
  18.  }
  19.  // ListModel methods
  20.  public int getSize() {
  21.   // Return the model size
  22.   return model.size();
  23.  }
  24.  public Object getElementAt(int index) {
  25.   Object result = null;
  26.   if (indexedAccessIterator == null || nextIndex > index) {
  27.    indexedAccessIterator = model.iterator();
  28.    nextIndex = 0;
  29.   }
  30.   for (; nextIndex <= index; nextIndex++) {
  31.    result = indexedAccessIterator.next();
  32.   }
  33.   return result;
  34.  }
  35.  public boolean add(Member element) {
  36.   boolean added = model.add(element);
  37.   if (added) {
  38.    reinitIterator();
  39.    fireContentsChanged(this, 0, getSize());
  40.   }
  41.   return added;
  42.  }
  43.  public synchronized void clear() {
  44.   model.clear();
  45.   reinitIterator();
  46.   fireContentsChanged(this, 0, getSize());
  47.  }
  48.  public synchronized boolean remove(Member member) {
  49.   boolean removed = model.remove(member);
  50.   if (removed) {
  51.    reinitIterator();
  52.    fireContentsChanged(this, 0, getSize());
  53.   }
  54.   return removed;
  55.  }
  56.  public Iterator iterator() {
  57.   return Collections.unmodifiableSortedSet(model).iterator();
  58.  }
  59. }

Elle est spécialisée pour des "Member" mais bon vous avez compris le principe.
La vue (ListUI) accède aux éléments, par leur indice, avec mon TreeSet dans une main et mon int dans l'autre, il faut convertir, la solution la plus simple consiste à parcourir un itérateur du set en comptant, quand on arrive au rang voulu, on renvoie la valeur de l'itérateur. Pour faire baisser un poil la complexité, et comme je sais que les indice sont accédés par plage contigües (j'ai testé) dans l'ordre croissant, je garde l'itérateur pour pas repartir de zéro à chaque requête.
si vous avez une idée pour l'améliorer, dites.
Il est possible d'avoir une complexité encore plus faible, mais ça nécessite de refaire toute la classe TreeSet pour y rajouter l'indexation, si quelqu'un de compétent en algo et en qualité veut bien faire ça en open-source, nous lui en serions tous reconnaissants.
 
Dans un esprit de partage, vous qu'avez-vous comme ListModel avec des services intéressant ?
 
 
Voiloù, j'aimerais qu'on arrive un peu à partager sur ce point (et peut-être des points connexes, tel les TableModels).
 
edit : enlevage d'une connerie, comme quoi se laver les dents c'est utile : ça fait réfléchir.


Message édité par nraynaud le 23-12-2003 à 09:44:52

---------------
trainoo.com, c'est fini
Reply

Marsh Posté le 23-12-2003 à 09:35:42   

Reply

Marsh Posté le 23-12-2003 à 11:05:13    

Ca a pas l'air très utile comme truc.
Le fait que ce soit trié n'apporte pas grand chose alors que si on avait un accès aléatoire à temps constant on pourrait faire de la recherche dichotomique.

Reply

Marsh Posté le 23-12-2003 à 13:51:24    

J'ai été confronté au meme problème avec TableModel.
J'ai été obligé de faire une methode getObjectAtIndex(int i) dans ma liste d'objets faites sur un TreeSet. Cette méthode est complétement inutile en dehors de son contexte et est complétement absurde ais c'est la seule facon que j'ai trouvé.
Je fais aussi un parcours avec un Iterator mais c'est crade.
 
C'est le moyen le plus simple d'utiliser sans avoir a faire sa propre classe de liste ou tree et refaire tous les algo de parcours et de tris.

Reply

Marsh Posté le 23-12-2003 à 14:20:27    

Supprimé, fausse bonne-idée.
 
 
Le ListModel ne doit être mis-à-jour *que* dans la tâche de répartition des événements de swing.


Message édité par nraynaud le 24-12-2003 à 23:32:58

---------------
trainoo.com, c'est fini
Reply

Marsh Posté le 23-12-2003 à 18:31:45    

moi j'avait fait ca pour trié les elements :
 
 

Code :
  1. /**
  2. * A Model that sorts its elements
  3. */
  4.     class SortedModel extends DefaultListModel {
  5.      private boolean added;
  6.    
  7.         public synchronized void addElement(Object obj) {
  8.          int result;
  9.          added=false;
  10.          String element = obj.toString();
  11.             for (int i = 0; i < size(); i++) {
  12.              result=collator.compare(((UserItem)elementAt(i)).getNick(),element);
  13.              if ( result > 0) {
  14.               insertElementAt(obj,i);
  15.               added=true;
  16.               break;
  17.              }else if(result==0){ //on a un doublons
  18.               added=true;
  19.               break;
  20.              }
  21.             }
  22.             if(!added)
  23.              super.addElement(obj);         
  24.         }
  25.     }

Reply

Marsh Posté le 26-12-2003 à 02:35:02    

nraynaud a écrit :

Supprimé, fausse bonne-idée.
 
 
Le ListModel ne doit être mis-à-jour *que* dans la tâche de répartition des événements de swing.


Peux tu développer stp, je ne comprends pas très bien.
L'accès n'est pas une mise a jour  :heink:

Reply

Marsh Posté le 26-12-2003 à 07:47:45    

yo c spi a écrit :


Peux tu développer stp, je ne comprends pas très bien.
L'accès n'est pas une mise a jour  :heink:  

Imaginons qu'il y a une tâche qui met et enlève des éléments aléatoirement en permanence, une belle adaptation pour retarder les évènements dans la tâche de répartition des événements swing comme j'avais fait avant de comprendre que c'est peine perdue.
Pour peindre la liste, le ListUI fait la chose suivante (par défaut) :  
 - parcourir le ListModel pour chercher la plage de valeurs visibles (si on a pas mis de prototype)
 - pour chaque valeur de la plage visible, obtenir un renderer et le peindre.
 
le parcourt se fait de la manière suivante :
 - lire le nombre de valeurs dans le ListModel
 - parcourir avec un int ces valeur (avec getElementAt())
 
Bien entendu, rien de tout ça ne "bloque" (avec synchronized) le ListModel, ce qui veut dire que si la tâche de mise-à-jour retire une valeur avant la fin du parcourt de la tâche de dessin, un ArrayOutOfBoundException va se lever, exactement ce qui m'est arrivé quand je me croyais malin avec mon ListModel synchronisé pour la notification des événements de mise-à-jour, mais pas des autres (tel un déplacement de la scrollbar, redimentionnement de la fenêtre etc.).


---------------
trainoo.com, c'est fini
Reply

Marsh Posté le 01-01-2004 à 15:31:14    

Code :
  1. /**
  2.  *  
  3.  * @author nraynaud
  4.  *
  5.  * This is the channel members collection, it losely implements Set interface.
  6.  * The model is based on a TreeSet.
  7.  * Aiming at performance, consecutive in-order accesses are cached by an iterator.
  8.  *  
  9.  */
  10. public class MemberListModel extends AbstractListModel {
  11.  /**
  12.   * the set, for sorted access to members.  
  13.   */
  14.  private TreeSet model;
  15.  private Comparator comparator;
  16.  /**
  17.   * iterator used for indexed access.
  18.   */
  19.  private Iterator indexedAccessIterator = null;
  20.  /**
  21.   * next aivable index from the iterator.
  22.   */
  23.  private int nextIndex = 0;
  24.  /**
  25.   * last iterated object.
  26.   */
  27.  private Object previousMember = null;
  28.  private int indexAccess = 0;
  29.  private int missedIterator = 0;
  30.  private int avoidedReset = 0;
  31.  private void reinitIterator() {
  32.   previousMember = null;
  33.   indexedAccessIterator = null;
  34.   nextIndex = 0;
  35.  }
  36.  /**
  37.   * @param membersComparator
  38.   */
  39.  public MemberListModel(Comparator membersComparator) {
  40.   comparator = membersComparator;
  41.   model = new TreeSet(membersComparator);
  42.  }
  43.  // ListModel methods
  44.  public int getSize() {
  45.   return model.size();
  46.  }
  47.  public Object getElementAt(int index) {
  48.   assert SwingUtilities.isEventDispatchThread();
  49.   indexAccess++;
  50.   Object result = null;
  51.   //unlikely to araise but avoids iterator reset when reading twice the same index.
  52.   if (index == nextIndex - 1 && previousMember != null) {
  53.    result = previousMember;
  54.    avoidedReset++;
  55.   } else {
  56.    //see if reading is in-order
  57.    if (indexedAccessIterator == null || nextIndex > index) {
  58.     indexedAccessIterator = model.iterator();
  59.     nextIndex = 0;
  60.     missedIterator++;
  61.    }
  62.    //go to the requested index.
  63.    for (; nextIndex <= index; nextIndex++)
  64.     result = indexedAccessIterator.next();
  65.   }
  66.   // asserting that the result is the same than the obvious one.
  67.   assert result == model.toArray()[index];
  68.   previousMember = result;
  69.   System.out.println(
  70.    "missed :"
  71.     + missedIterator
  72.     + "/"
  73.     + indexAccess
  74.     + " avoided resets : "
  75.     + avoidedReset);
  76.   return result;
  77.  }
  78.  /**
  79.   * Inserts the given collection into the members collection.
  80.   * @param collection the collection to add.
  81.   * @throws NullPointerException if an element of the given colection is null
  82.   * @throws ClassCastException if an element of th passed collection is not instance of Member.  
  83.   */
  84.  public void addAll(Collection collection) {
  85.   assert SwingUtilities.isEventDispatchThread();
  86.   for (Iterator i = collection.iterator(); i.hasNext();) {
  87.    Member member = (Member)i.next();
  88.    model.add(member);
  89.   }
  90.   reinitIterator();
  91.   fireContentsChanged(this, 0, getSize());
  92.  }
  93.  /**
  94.   * adds the given member to the collection.
  95.   *  
  96.   * @param element the member to add to the collection
  97.   * @return true if the member was added.
  98.   * @throws NullPointerException if element is null.
  99.   */
  100.  public boolean add(Member member) {
  101.   assert SwingUtilities.isEventDispatchThread();
  102.   boolean added = model.add(member);
  103.   if (added) {
  104.    int min = 0;
  105.    if (previousMember != null
  106.     && comparator.compare(member, previousMember) >= 0)
  107.     min = nextIndex - 1;
  108.    reinitIterator();
  109.    assert min == 0
  110.     || comparator.compare(model.toArray()[min], member) <= 0;
  111.    fireContentsChanged(this, min, getSize());
  112.   }
  113.   return added;
  114.  }
  115.  /**
  116.   * Clears the collection.
  117.   *
  118.   */
  119.  public void clear() {
  120.   assert SwingUtilities.isEventDispatchThread();
  121.   model.clear();
  122.   reinitIterator();
  123.   fireContentsChanged(this, 0, getSize());
  124.  }
  125.  /**
  126.   * Removes the given member.
  127.   * @param member the member to remove
  128.   * @return true if member was removed, false otherwise
  129.   */
  130.  public boolean remove(Member member) {
  131.   assert SwingUtilities.isEventDispatchThread();
  132.   boolean removed = model.remove(member);
  133.   if (removed) {
  134.    int min = 0;
  135.    if (previousMember != null
  136.     && comparator.compare(member, previousMember) >= 0)
  137.     min = nextIndex - 1;
  138.    reinitIterator();
  139.    assert min == 0
  140.     || comparator.compare(model.toArray()[min], member) <= 0;
  141.    fireContentsChanged(this, min, getSize());
  142.   }
  143.   return removed;
  144.  }
  145. }


 
Le même avec une légère amélioration et des chiffres :
missed :255/28820 avoided resets : 183
 
il y a eu 28820 appels à getElementAt(), 255 ont donné lieu à un reset de l'iterateur (insertion/retrait d'éléments ou accès dans le désordre) et 183 reset ont été évités par le cache de la dernière valeur tirée de l'itérateur (c'est utile lorsqu'on scrolle vers le bas de la liste).
 
Je pense que c'est plutôt pas mal.


---------------
trainoo.com, c'est fini
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed