Feu d'artifice! Être programmeur n’est pas facile. Vous devez constamment apprendre, toujours apprendre quelque chose de nouveau. Mais, comme dans toute autre entreprise, le plus difficile est de se lancer, de faire le premier pas vers son objectif. Et puisque vous êtes assis sur ce site et que vous lisez cet article, vous avez franchi la première étape. Cela signifie que vous devez maintenant avancer délibérément vers votre objectif, sans ralentir ni vous arrêter en cours de route. Si je comprends bien, votre objectif est de devenir développeur Java ou d'approfondir vos connaissances si vous en êtes un. Si tel est le cas, alors vous êtes au bon endroit, car nous continuerons d'analyser une liste complète de plus de 250 questions d'entretien pour les développeurs Java. Nous allons continuer!
Collections
84. Parlez-nous des itérateurs et de leur utilisation
Les collections sont l'un des sujets favoris dans tout entretien avec un développeur Java, et lorsqu'ils parlent de la hiérarchie des collections, les candidats disent souvent que cela commence par l' interface Collection . Mais ce n'est pas vrai, car au-dessus de cette interface il y en a une autre - Iterable . Cette interface représente la méthode iterator() , qui vous permet d'appeler un objet Iterator pour la collection actuelle. Et quel est exactement cet objet Iterator ? Un itérateur est un objet qui offre la possibilité de se déplacer dans une collection et de parcourir des éléments sans que l'utilisateur ait besoin de connaître l'implémentation d'une collection particulière. C'est-à-dire qu'il s'agit d'une sorte de pointeur vers les éléments de la collection, qui, pour ainsi dire, regardent un certain endroit de celle-ci. L'itérateur a les méthodes suivantes :- hasNext() - renvoie true s'il y a un élément situé après le pointeur (cette méthode permet de savoir si la fin de la collection a été atteinte) ;
- next() - renvoie l'élément suivant après le pointeur. S'il n'y en a pas, NoSuchElementException est levée . Autrement dit, avant d'utiliser cette méthode, il est préférable de s'assurer que l'élément existe - en utilisant hasNext() ;
- Remove() - supprime le dernier élément reçu de la collection à l'aide de la méthode next() . Si next() n'a jamais été appelé avant l'appel de remove() , une exception sera levée - IllegalStateException ;
- forEachRemaining(<Consumer>) - effectue l'action passée avec chaque élément de la collection (la méthode est apparue dans Java 8).
List<String> list = new ArrayList<>();
list.add("Hello ");
list.add("World, ");
list.add("It's ");
list.add("Amigo!");
Iterator iterator = list.iterator();
while(iterator.hasNext()) {
iterator.next();
iterator.remove();
}
System.out.println(list.size());
La console affichera :
0
Cela signifie que la suppression des éléments a réussi. Une fois que nous avions un itérateur, nous pourrions utiliser une méthode pour imprimer tous les éléments à l’écran :
iterator.forEachRemaining(x -> System.out.print(x));
Mais après cela, l'itérateur deviendrait impropre à une utilisation ultérieure, car il parcourrait toute la liste et un itérateur normal ne dispose pas de méthodes de retour en arrière. Ici, nous abordons progressivement LinkedList , à savoir sa méthode listIterator() , qui renvoie un type d'itérateur modernisé - ListIterator . Outre les méthodes d'itération classiques (standard), celle-ci en comporte d'autres :
- add(<Element>) - insère un nouvel élément dans la liste ;
- hasPrevious() - renvoie true s'il y a un élément situé avant le pointeur (qu'il y ait un élément précédent) ;
- nextIndex() - renvoie l'index dans la liste de l'élément suivant après le pointeur ;
- previous() - renvoie l'élément précédent (jusqu'au pointeur) ;
- previousIndex() - renvoie l'index de l'élément précédent ;
- set(<Element>) - Remplace le dernier élément renvoyé par la méthode next() ou previous() .
85. Quelle est la hiérarchie de collection dans Java Collection Framework ?
Il existe deux hiérarchies de collections en Java. La première hiérarchie est la hiérarchie Collection elle-même avec la structure suivante : Elle est à son tour divisée en sous-collections suivantes :- Set est une interface qui décrit une telle structure de données comme un ensemble contenant des éléments uniques (non répétitifs) non ordonnés. L'interface a des implémentations standard - TreeSet , HashSet et LinkedHashSet .
- List est une interface qui décrit une structure de données qui stocke une séquence ordonnée d'objets. Les instances contenues dans une liste peuvent être insérées et supprimées par leur index dans cette collection (analogue à un tableau, mais avec redimensionnement dynamique). L'interface a des implémentations standards - ArrayList , Vector ( considérées comme obsolètes et non réellement utilisées ) et LinkedList .
- La file d'attente est une interface qui décrit une structure de données qui stocke les éléments sous la forme d'une file d'attente qui suit la règle FIFO - First In First Out . L'interface a les implémentations standards suivantes : LinkedList (oui, elle implémente également Queue ) et PriotityQueue .
86. Quelle est la structure interne d’une ArrayList ?
ArrayList est similaire à un tableau, mais avec la possibilité de se développer dynamiquement. Qu'est-ce que ça veut dire? Le fait est qu'ArrayList fonctionne sur la base d'un tableau régulier, à savoir qu'il stocke les éléments dans un tableau interne (sa taille par défaut est de 10 cellules). Lorsque le tableau interne est plein, un nouveau tableau est créé dont la taille est déterminée par la formule :<размерТекущегоМассива> * 3 / 2 + 1
Autrement dit, si la taille de notre tableau est de 10, la taille du nouveau sera : 10 * 3 / 2 + 1 = 16. Ensuite, toutes les valeurs du premier (ancien) tableau y sont copiées à l'aide du méthode native System.arraycopy () et le premier tableau est supprimé. En fait, c'est ainsi que l'extensibilité dynamique d'ArrayList est implémentée . Regardons les méthodes ArrayList les plus utilisées : 1. add(<Elelement>) - ajoute un élément à la fin du tableau (jusqu'à la dernière cellule vide), et vérifie d'abord s'il y a de l'espace dans ce tableau. S'il n'y est pas, un nouveau tableau est créé dans lequel les éléments sont copiés. La complexité logarithmique de cette opération est O(1). Il existe une méthode similaire - add(<Index>,<Elelement>) . Il ajoute un élément non pas à la fin de la liste (tableau), mais à une cellule spécifique avec l'index fourni en argument. Dans ce cas, la complexité logarithmique différera selon l'endroit où elle est ajoutée :
- si c'était approximativement le début de la liste, la complexité logarithmique sera proche de O(N), car tous les éléments situés à droite du nouveau devront être déplacés d'une cellule vers la droite ;
- si l'élément est inséré au milieu - O(N/2) car nous devons déplacer seulement la moitié des éléments de la liste d’une cellule vers la droite.
87. Quelle est la structure interne de LinkedList ?
Si ArrayList contient des éléments dans un tableau interne, alors LinkedList se présente sous la forme d'une liste doublement chaînée. Cela signifie que chaque élément contient un lien vers l'élément précédent ( previous ) et le suivant ( next ). Le premier élément n'a pas de lien vers le précédent (c'est le premier), mais il est considéré comme la tête de liste, et la LinkedList a un lien directement vers lui. Le dernier élément, en fait, n'a pas d'élément suivant, c'est la queue de la liste, et il existe donc un lien direct vers celui-ci dans la LinkedList elle-même . Par conséquent, la complexité logarithmique de l’accès à la tête ou à la queue d’une liste est O(1). Dans ArrayList, lorsque la liste s'agrandissait, le tableau interne augmentait, mais ici tout se passe plus simplement - lors de l'ajout d'un élément, quelques liens changent simplement. Examinons quelques-unes des méthodes LinkedlList les plus utilisées : 1. add(<Elelement>) - ajout à la fin de la liste, c'est-à-dire après le dernier élément (5), un lien vers le nouvel élément sera ajouté comme next . Le nouvel élément aura un lien vers le dernier (5) comme l'élément précédent . La complexité logarithmique d'une telle opération sera O(1), puisque seul un lien vers le dernier élément est nécessaire, et comme vous vous en souvenez, la queue a un lien direct depuis LinkedList et la complexité logarithmique pour y accéder est minime. 2. add(<Index>,<Elelement>) - ajout d'un élément par index. Lors de l'ajout d'un élément, par exemple, au milieu d'une liste, les éléments de la tête et de la queue (des deux côtés) sont d'abord itérés jusqu'à ce que l'emplacement souhaité soit trouvé. Si nous voulons insérer un élément entre le troisième et le quatrième (dans la figure ci-dessus), alors lors de la recherche du bon endroit, le lien suivant du troisième élément pointera déjà vers le nouveau. Pour le nouveau, le lien précédent pointera vers le troisième. Ainsi, le lien du quatrième élément - précédent - pointera déjà vers le nouvel élément, et le lien suivant du nouvel élément pointera vers le quatrième élément : La complexité logarithmique de cette méthode dépendra de l'index donné au nouvel élément :- s'il est proche de la tête ou de la queue, il s'approchera de O(1), puisqu'il ne sera pas réellement nécessaire de parcourir les éléments ;
- s'il est proche du milieu, alors O(N/2) - les éléments de la tête et de la queue seront triés simultanément jusqu'à ce que l'élément requis soit trouvé.
88. Quelle est la structure interne d’un HashMap ?
Peut-être l'une des questions les plus fréquemment posées lors d'un entretien avec un développeur Java. HashMap v fonctionne avec des paires clé-valeur . Comment sont-ils stockés dans le HashMapv lui-même ? À l’intérieur du HashMap se trouve un tableau de nœuds :Node<K,V>[] table
Par défaut, la taille du tableau est de 16, et elle double à chaque fois qu'il est rempli d'éléments (lorsque LOAD_FACTOR est atteint - un certain pourcentage de plénitude, par défaut il est de 0,75 ). Chaque nœud stocke un hachage de la clé, une clé, une valeur et un lien vers l'élément suivant : En fait, « lien vers l'élément suivant » signifie que nous avons affaire à une liste à chaînage unique, où chaque élément contient un lien vers le prochain. Autrement dit, HashMap stocke les données dans un tableau de listes à lien unique. Mais je noterai tout de suite : lorsqu'une cellule du tableau a un lien vers une liste similaire à lien unique composée de plus d'un élément, ce n'est pas bon. Ce phénomène est appelé collision . Mais tout d’abord. Voyons comment une nouvelle paire est enregistrée à l'aide de la méthode put . Tout d’abord, le hachCode() de la clé est récupéré. Par conséquent, pour que hashmap fonctionne correctement , vous devez prendre des classes dans lesquelles cette méthode est remplacée en tant que clés. Ce code de hachage est ensuite utilisé dans la méthode interne - hash() - pour déterminer le nombre dans la taille du tableau . Ensuite, en utilisant le numéro reçu, on accède à une cellule spécifique du tableau . Nous avons ici deux cas :
- La cellule est vide - la nouvelle valeur Node y est stockée .
- La cellule n'est pas vide - la valeur des clés est comparée. S'ils sont égaux, la nouvelle valeur du nœud écrase l'ancienne, s'ils ne sont pas égaux, l'élément suivant est accédé et comparé à sa clé... Et ainsi de suite jusqu'à ce que la nouvelle valeur écrase une ancienne ou atteigne la fin du liste chaînée unique et y sera stockée comme dernier élément.
GO TO FULL VERSION