280 likes | 530 Views
Objets Java pour la RI. Extraction d’Information dans les Textes. Les collections en Java. L’interface List. Une liste = une collection d’éléments ordonnés Doublons possibles Exemples : ArrayList <E> (un tableau redimensionnable) LinkedList <E> (pour faire du FIFO / file)
E N D
Objets Javapour la RI Extraction d’Informationdans les Textes
L’interface List • Une liste = une collection d’éléments ordonnés • Doublons possibles • Exemples : • ArrayList<E> (un tableau redimensionnable) • LinkedList<E> (pour faire du FIFO / file) • ArrayDeque<E> (pour faire du FIFO et LIFO)
Les files prioritaires • Exemples: • PriorityQueue<E> • PriorityBlockingQueue<E> ("thread-safe") • Permettent de créer des files en plaçant les éléments prioritaires en tête (grâce à un Comparator<E>)
L’interface Set • Une collection d’éléments sans doublons • Exemples : • HashSet<E> (rangement par le hashcode, ordre non conservé) • LinkedHashSet<E> (rangement par le hashcodetout en conservant l’ordre d’insertion) • TreeSet<E> (ensemble ordonné par un Comparator<E>)
L’interface Map • Dictionnaire qui relie des clés et des valeurs. • Les clés ne peuvent pas être dupliquées. • Exemples : • HashMap<K, V> (rangement par le hashcode, ordre non conservé) • LinkedHashMap<K, V> (rangement par le hashcodetout en conservant l’ordre d’insertion) • TreeMap<K, V> (ensemble ordonné par un Comparator<E>)
Et un dictionnaire bi-directionnel ? • On aimerait avoir un dictionnaire qui marche dans les deux sens • Quel est l’identifiant de ce mot ? • À quel mot correspond cet identifiant ? • Ça n’existe pas dans l’API native de java • Interface BidiMapde org.apache.commons.collections • À faire soi-même (voir plus loin)
Les objets Java • L’API Java fournit énormément de classes d’objets qui couvrent de nombreux besoins essentiels (notamment les collections) • Ces classes fournissent également de nombreuses méthodes pour faciliter la manipulation des objets • On a souvent tendance à utiliser ces classes par facilité • Les surcoûts possibles si on n’y prête pas garde: • Le temps de calcul (exemple : contains + add, contains + get, etc…) • La mémoire • En raison des « frais généraux » (overheads) imposés par la gestion des objets • Exemple : entier pour conserver la taille d’une collection, etc.
Objets java et overheads : exemples • Double: 24 octets • (données = 33 % de la taille de l’objet) • NB : Ces valeurs (et toutes les suivantes) peuvent varier selon l’architecture • Boolean: 16 octets (!)
Objets java et overheads : exemples • char[2] : 24 octets • String : • Dépend beaucoup des versions et des architectures • Faites le test à la maison : • Créez 100 000 instances vides de String • Regardez la mémoire consommée • Faites la même chose avec 100 000 instances de String de 20 caractères
Objets java et overheads : exemples • TreeMap Pour 100 entrées d’un TreeMap<Double, Double> : - 8,6 Ko - 82 % d’overheads pour chaque entrée
Objets java et overheads : exemples • double[100] – double[100] • 1632 octets • 2 % d’overheads • Le TreeMap<Double, Double> permet en plus : • D’avoir un tableau redimensionnable • De garantir l’ordre pendant la mise à jour Toujours se demander :Est-ce vraiment utile ?
Dictionnaire : besoins • Un lien bi-directionnel entre identifiant et terme • Quel est l’identifiant de ce mot ? • À quel mot correspond cet identifiant ? • Indispensable pour gérer efficacement un index Pourquoi ? • La solution de facilité : • HashMap<String, Integer> • HashMap<Integer, String>
Qu’est-ce qu’une HashMap ? • HashMap<K, V> • Capacité initiale de 2n (avec par défaut n = 4 : 16 éléments) table = new Entry[2^n] Une « Entry » est une liste chaînée de paires <K, V> • Ajout d’une paire <k, v> : h = hashcode(k) i = couper h pour qu’il rentre dans n bits entries = table[i] si entries == null on crée une nouvelle liste chaînée avec k dedans Sinon (collision) on ajoute k à entries sile nombre d’entrées dépasse un certain seuil on les recopie dans un tableau deux fois plus grand
Qu’est-ce qu’une HashMap ? • Recherche d’une valeur pour la clé k : h = hashcode(k) i = couper h pour qu’il rentre dans n bits entries = table[i] On parcourt entries jusqu’à trouver la clé recherchée On renvoie la valeur correspondante
Les overheads du dictionnaire • Avec deux HashMap • Pour un vocabulaire de 100 000 mots : • Nombre d’objets = au moins ( 1 + 100 000 + 100 000 + 100 000 ) × 2 = plus de 600 000 overheads 2 sens HashMap Integer Entry String
Alternative • On sait qu’on aura environ n mots • Capacité fixe de n table = new String[n] • Ajout d’un mot mot : h = hashcode(k) id = couper h pour qu’il rentre dans log2 (n) bits chaîne = table[id] si chaîne != nullmais chaîne != k (collision) j = id + 1 tant que table[j] != null j++ table[j] = chaîne sinon table[id] = chaîne
Alternative • Recherche du mot correspondant à l’identifiant id mot = table[id] • Recherche de l’identifiant pour la clé mot h = hashcode(k) id = couper h pour qu’il rentre dans log2 (n) bits chaîne = table[id] si chaîne == motrenvoyer id sinon j = id + 1 tant que table[j] != null j++ renvoyer j
Alternative : les overheads • Avec un tableau de String + un HashMap pour les collisions du hashcode • Pour un vocabulaire de 100 000 mots, avec 1 % de collisions • Nombre d’objets = au moins 1 + 100 000 + 1 + 1 000 + 1 000 + 1 000 = plus de 100 000 overheads Objet String HashMap pour les collisions
Alternative • Tableau de String beaucoup plus économe en mémoire que la double HashMap • Performances équivalentes en temps de calcul • La HashMap propose de nombreuses autres fonctionnalités : • Taille redimensionnable (au prix d’un temps de calcul élevé) • Itération • D’autres méthodes prédéfinies devront être recodées (size(), contains(), remove(), …) • Mais ces fonctionnalités sont superflues dans notre cas
Bilan • Quand la mémoire est importante : • Se méfier des objets Java « tout prêts » • Se méfier de l’encapsulation sauvage • Réfléchir aux fonctionnalités vraiment utiles • Préférer les types primitifs (int) aux objets(Integer) quand c’est possible • Accepter de recoder des choses si c’est nécessaire • MAIS • Ne pas en profiter pour bâcler son code Efficacité et structure correcte ne sont pas incompatibles ! • Ne pas réinventer la roue en moins bien Les algorithmes de base de l’API Java (tri, ajout, etc.) sont les meilleurs possibles