6. Parlez-nous de la liste
List est une interface qui représente une structure ordonnée d’objets, appelée liste. Le « truc » de cette structure est que les éléments contenus dans la List peuvent être insérés, modifiés ou supprimés par index, c'est-à-dire l'identifiant interne de la List . En d’autres termes, index signifie : « combien d’éléments se trouvent au début de la liste ». Le premier élément List a l’index 0, le second a l’index 1, et ainsi de suite. Le cinquième élément est donc à quatre éléments du début de la liste. Comme mentionné ci-dessus, l'ordre dans lequel les éléments sont ajoutés à une liste est important. C'est pourquoi la structure des données est appelée liste . Nous listons les méthodes propres à cette structure qui visent à travailler avec des éléments par index :- get - renvoie l'élément à la position spécifiée (par valeur d'index),
- supprimer - supprime l'élément à la position spécifiée,
- set - remplace l'élément à la position spécifiée par l'élément spécifié dans la méthode.
- push - ajoute l'élément transmis en haut de la pile ;
- peek - renvoie l'élément qui se trouve en haut de la pile ;
- pop - renvoie également l'élément qui se trouve en haut de la pile, mais le supprime ;
- vide - vérifie si la pile est vide - vrai ou non - faux ;
- search - recherche dans la pile un élément donné. Si un élément est trouvé, son numéro de séquence par rapport au haut de la pile est renvoyé. Si l'élément n'est pas trouvé, la valeur -1 est renvoyée.
7. Parlez-nous de la carte
Comme indiqué ci-dessus, une Map est une collection qui possède une structure distincte d'interfaces et de leurs implémentations. C'est distinct car ici les valeurs ne sont pas stockées une à la fois, mais dans une paire « clé-valeur ». Méthodes de base de la carte :- put(K key, V value) - ajout d'un élément à la Map ;
- get(Object key) - recherche une valeur par clé ;
- containKey(Object key) — vérifie la carte pour la présence de cette clé ;
- containValue(Object value) - vérifie la carte pour la présence de cette valeur ;
- remove(Object key) - suppression d'une valeur par sa clé.
- HashMap - conçu pour stocker des valeurs dans un ordre aléatoire, mais vous permet de rechercher rapidement des éléments cartographiques. Vous permet de spécifier une clé à l'aide du mot-clé null , mais pas plus d'une fois, car les paires avec les mêmes clés sont écrites les unes sur les autres. La condition principale est l'unicité des clés : les valeurs peuvent être répétées (il peut y avoir plusieurs valeurs nulles).
- LinkedHashMap est un analogue de HashMap qui stocke les valeurs dans l'ordre dans lequel elles ont été ajoutées. En conséquence, comme LinkedList , il a un en-tête - la tête d'une liste doublement chaînée. Une fois initialisé, il pointe vers lui-même.
LinkedHashMap a également un accessOrder qui spécifie comment les éléments seront accessibles lorsque l'itérateur est utilisé. Si accessOrder est faux, l'accès sera effectué dans l'ordre dans lequel les éléments ont été insérés. Si c'est vrai, les éléments seront dans l'ordre du dernier accès (le dernier élément accédé sera placé à la fin).
- TreeMap est une carte qui trie les éléments par valeurs clés. Similaire à TreeSet , mais pour les paires basées sur des valeurs clés. Pour définir les règles de tri TreeMap , les clés doivent implémenter l'interface Comparable . Sinon, il devrait y avoir un comparateur orienté clé (celui qui est spécifié dans le constructeur TreeMap ), un TreeSet - implémenté avec un objet TreeMap à l'intérieur, dans lequel, en fait, toute la magie se produit.
Vous pouvez en savoir plus sur le tri dans TreeMap à l'aide d'arbres rouge-noir dans l'article sur les fonctionnalités de TreeMap .
- Hashtable est similaire à HashMap , mais ne permet pas de stocker des valeurs nulles sous forme de clés ou de valeurs. Il est soigneusement synchronisé d’un point de vue multi-thread, ce qui signifie qu’il est sécurisé d’un point de vue multi-thread. Mais cette implémentation est obsolète et lente, vous ne verrez donc plus Hashtable dans des projets plus ou moins nouveaux.
8. ArrayList contre LinkedList. Lequel est-il préférable d’utiliser ?
Cette question est peut-être la plus populaire sur les structures de données et comporte certains pièges. Avant d'y répondre, apprenons-en davantage sur ces structures de données. ArrayList implémente l' interface List et fonctionne sur un tableau interne étendu selon les besoins. Lorsque le tableau interne est complètement rempli et qu'un nouvel élément doit être inséré, un nouveau tableau est créé avec une taille de (oldSize * 1.5) +1. Après cela, toutes les données de l'ancien tableau sont copiées dans le nouvel élément + nouvel élément et l'ancien sera supprimé par le ramasse-miettes . La méthode add ajoute un élément à la dernière cellule vide du tableau. Autrement dit, si nous y avons déjà 3 éléments, le suivant sera ajouté à la 4ème cellule. Passons en revue les performances des méthodes de base :- get(int index) - prendre un élément dans un tableau par index est le plus rapide en O(1) ;
- add(Object obj) - s'il y a suffisamment d'espace dans le tableau interne pour un nouvel élément, alors avec une insertion normale, du temps O(1) sera dépensé , puisque l'ajout est ciblé sur la dernière cellule.
Si nous devons créer un nouveau tableau et y copier le contenu, alors notre temps sera directement proportionnel au nombre d'éléments dans le tableau O(n) ;
- remove(int index) - lors de la suppression d'un élément, par exemple, du milieu, nous obtiendrons un temps O(n/2), car nous devrons déplacer les éléments à droite de celui-ci d'une cellule en arrière. En conséquence, si vous supprimez depuis le début de la liste, alors O(n), depuis la fin - O(1);
- add(int index, Object obj) - une situation similaire à la suppression : lors de l'ajout au milieu, nous devrons avancer les éléments de droite d'une cellule, donc le temps est O(n/2). Bien sûr, depuis le début - O(n), depuis la fin - O(1) ;
- set(int index, Object obj) - ici la situation est différente, puisqu'il vous suffit de trouver l'élément souhaité et de l'écrire sans déplacer le reste, donc O(1).
- get(int index) - lors de la recherche d'un élément qui se trouve au milieu de la liste, il commence à rechercher tous les éléments dans l'ordre jusqu'à ce que celui souhaité soit trouvé. Logiquement, la recherche devrait prendre O(n/2) , mais LinkedList a également une queue, donc la recherche est effectuée simultanément des deux côtés. En conséquence, le temps est réduit à O(n/4) .
Si l'élément est proche du début de la liste ou de la fin, alors l'heure sera O(1) ;
- add(Object obj) - lors de l'ajout d'un nouvel élément, l'élément « tail » aura un lien vers l'élément suivant ajouté, et le nouveau recevra un lien vers cet élément précédent et deviendra la nouvelle « tail ». En conséquence, le temps sera O(1) ;
- remove(int index) - logique similaire à la méthode get(int index) . Pour supprimer un élément du milieu de la liste, vous devez d’abord le retrouver. C'est encore O(n/4) , alors que la suppression elle-même ne prend rien, puisqu'elle change seulement le pointeur des objets voisins (ils commencent à se référer les uns aux autres). Si l'élément est au début ou à la fin, alors encore - O(1) ;
- add(int index, Object obj) et set(int index, Object obj) - les méthodes auront une complexité temporelle identique à get(int index) , puisque la plupart du temps est passé à rechercher l'élément. Par conséquent, pour le milieu de la liste - O(n/4) , pour le début - O(1).
Opération | Liste des tableaux | Liste liée |
---|---|---|
Obtenir par index obtenir(index) | O(1) | Au milieu O(n/4) |
Ajouter un nouvel élément add(obj) |
O(1) Si vous devez copier un tableau, alors - O(n) |
O(1) |
Supprimer l'élément supprimer (index int) |
Depuis le début - O(n) Du milieu - O(n/2) De la fin - O(1) |
Au milieu - O(n/4) A la fin ou au début - O(n) |
Ajouter un élément add (int index, Object obj) |
Retour en haut - O(n) Au milieu - O(n/2) Jusqu'à la fin - O(1) |
Au milieu - O(n/4) A la fin ou au début - O(n) |
Remplacer l'ensemble d'éléments (index, obj) | O(1) |
Au milieu - O(n/4) A la fin ou au début - O(n) |
- le meilleur choix si l'opération la plus fréquente consiste à rechercher un élément, à écraser un élément ;
- le pire choix si l'opération est une insertion et une suppression au début-milieu, car les opérations de décalage des éléments à droite auront lieu.
- le meilleur choix si notre opération fréquente est l'insertion et la suppression au début-milieu ;
- pire choix si l’opération la plus courante est la recherche.
9. Comment les éléments sont-ils stockés dans un HashMap ?
La collection HashMap contient un tableau interne Node[] table , dont les cellules sont également appelées buckets . Le nœud contient :- clé - lien vers la clé,
- valeur - référence à la valeur,
- hachage - valeur de hachage,
- next - lien vers le prochain nœud .
- La clé est vérifiée pour null . Si c'est null , alors la clé sera stockée dans la cellule table[0] car le code de hachage pour null est toujours 0.
- Si la clé n'est pas null , alors la méthode hashcode() de l'objet clé est appelée , qui renverra son code de hachage. Ce code de hachage est utilisé pour déterminer la cellule du tableau où l'objet Node sera stocké.
- Ensuite, ce hashcode est placé dans la méthode interne hash() , qui calcule le hashcode, mais dans la taille du tableau table[] .
- Ensuite, en fonction de la valeur de hachage, Node est placé dans une cellule spécifique du tableau table[] .
- Si la cellule table[] utilisée pour enregistrer l' élément Node actuel n'est pas vide, mais contient déjà un élément, alors les éléments Node sont itérés sur la valeur suivante jusqu'à ce que le dernier élément soit atteint. C'est-à-dire celui dont le champ suivant est null .
Lors de cette recherche, la clé de l' objet Node protégé est comparée aux clés de ceux recherchés :
- si une correspondance est trouvée, la recherche se terminera et le nouveau nœud écrasera le nœud dans lequel la correspondance a été trouvée (seul son champ de valeur sera écrasé ) ;
- si aucune correspondance de clé n'est trouvée, alors le nouveau nœud deviendra le dernier de cette liste et le précédent aura un lien suivant vers lui.
10. Expliquez l'itérateur
Dans le diagramme de mappage de la hiérarchie de collection ci-dessus, l'interface de collection était le point de départ de toute la hiérarchie, mais en pratique, ce n'est pas tout à fait comme ça. La collection hérite d'une interface avec une méthode iterator() , qui renvoie un objet qui implémente l' interface Iterator<E> . L'interface de l'Itérateur ressemble à ceci :public interface Iterator <E>{
E next();
boolean hasNext();
void remove();
}
next() - en appelant cette méthode, vous pouvez obtenir l'élément suivant. hasNext() - vous permet de savoir s'il existe un élément suivant et si la fin de la collection a été atteinte. Et quand il y a encore des éléments, hasNext() retournera true . En règle générale, hasNext() est appelée avant la méthode next() , car next() lancera une NoSuchElementException lorsqu'elle atteindra la fin de la collection . Remove() - Supprime l'élément récupéré lors du dernier appel à next() . Le but de Iterator est de parcourir les éléments. Par exemple:
Set<Integer> values = new TreeSet<>();
values.add(5);
values.add(3);
values.add(6);
values.add(8);
values.add(2);
values.add(4);
values.add(1);
values.add(7);
Iterator<Integer> iter = values.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
En fait, la boucle for-each est implémentée sous le capot à l'aide d'un itérateur. Vous pouvez en savoir plus à ce sujet ici . List fournit sa propre version d'un itérateur, mais une version plus cool et plus sophistiquée - ListIterator . Cette interface étend Iterator et dispose de méthodes supplémentaires :
- hasPrevious renverra true s'il y a un élément précédent dans la collection, false sinon ;
- previous renvoie l'élément actuel et passe au précédent ; s'il n'y en a pas, une NoSuchElementException est levée ;
- add insérera l'objet passé avant l'élément qui sera renvoyé par le prochain appel à next() ;
- set attribue à l'élément actuel une référence à l'objet passé ;
- nextIndex renvoie l'index de l'élément suivant. Si rien de tel n’existe, alors la taille de la liste est renvoyée ;
- previousIndex renvoie l'index de l'élément précédent. S'il n'y en a pas, alors le nombre -1 est renvoyé.
GO TO FULL VERSION