JavaRush /Blog Java /Random-FR /L'histoire d'une interview : des questions intéressantes
GuitarFactor
Niveau 30
Санкт-Петербург

L'histoire d'une interview : des questions intéressantes

Publié dans le groupe Random-FR
Récemment, j'ai eu l'occasion d'assister à un entretien pour un poste de stagiaire dans l'une des grandes entreprises informatiques. L'histoire d'une interview : questions intéressantes - 1C'était mon premier entretien informatique et, à mon avis, il s'est avéré intéressant. Au total, j'ai été « interrogé » pendant plus de 3 heures (cela a été précédé de devoirs et d'un test au bureau sur ordinateur). Je tiens à rendre hommage à l'intervieweur, qui n'a pas abandonné lorsque j'ai mal répondu à la question, mais avec l'aide de ses questions suggestives, il m'a forcé à réfléchir et à trouver la bonne réponse. Ci-dessous, je présenterai plusieurs « croquis » - à mon avis, des questions assez intéressantes, dont certaines m'ont permis de mieux comprendre certains aspects de Java. Peut-être que ces choses sembleront évidentes à certains, mais je pense qu'il y aura ceux pour qui cela sera utile. Ci-dessous, les phrases sont mises en évidence dans les polices suivantes : Intervieweur - en gras Explications en voix off et mes pensées - en italique Mes réponses - en police normale Nous en avons fini avec l'arrière-plan, passons aux choses sérieuses)

Croquis 1. « Une méthode en apparence simple »

Écrivez comment vous mettriez en œuvre une méthode qui renvoie le résultat de la division du nombre a par le nombre b. L'intervieweur écrit sur une feuille de papier.
int divide(int a, int b) {
}
*J'ai jeté un coup d'œil incrédule au morceau de papier avec la signature de la méthode. Quel est le problème ?* J'écris :
int divide(int a, int b) {
    return a/b;
}
Y a-t-il des problèmes avec cette méthode ? *J'attrape un idiot vraiment stupide* Apparemment non. Vient ensuite une question légitime : et si b=0 ? *Whoa, je suis sur le point d'être expulsé de ce bureau si je continue comme ça !* Oh oui, bien sûr. Ici, nous avons des arguments de type int, donc une exception arithmétique sera levée. Si les arguments étaient de type float ou double, le résultat serait Infinity. Qu'allons-nous faire à ce sujet ? Je commence à écrire try/catch
int divide(int a, int b) {
    try {
        return a/b;
    } catch (Exception e) {
        e.printStackTrace();
        return ... // ??? what the hack?
    }
}
*Je peux revenir et geler : quelque chose doit être retourné en cas d'erreur. Mais comment distinguer ce « quelque chose » du résultat du calcul* ? Que retournerons-nous ? Hm... Je changerais le type de la variable de retour en Integer et en cas d'exception je retournerais null. Imaginons que nous ne puissions pas changer le type. Pouvons-nous en sortir d'une manière ou d'une autre ? Peut-être qu'on peut faire autre chose à l'exception ? *Le voici* Nous pouvons également le transmettre à la méthode appelante ! Droite. A quoi cela ressemblera-t-il?
int divide(int a, int b) throws ArithmeticException{
    return a/b;
}

void callDivide(int a, int b) {
    try {
        divide(a, b);
    } catch (ArithmeticException e) {
        e.printStackTrace();
    }
}
Est-il nécessaire de gérer l'exception ? Oui, parce que nous le transmettons explicitement à partir de la méthode Divide. (*Je me suis trompé ici ! Ce qui suit sont des questions suggestives de l'intervieweur pour arriver à la bonne réponse*) Et l'exception arithmétique - de quel type d'exception s'agit-il - cochée ou décochée ? Il s’agit d’une exception d’exécution, ce qui signifie qu’elle n’est pas cochée. *Voici la question qui tue* Il s'avère donc, selon vos mots, que si nous spécifions l'exception arithmétique dans la signature de la méthode, alors elle est devenue une exception vérifiée ? *Ugh !* Probablement... non. Oui, c'est parti. Si nous indiquons throws /unchecked exception/ dans la signature, nous avertissons seulement que la méthode peut lever une exception, mais il n'est pas nécessaire de la gérer dans la méthode appelante. C'est réglé. Y a-t-il autre chose que nous puissions faire pour éviter les erreurs ? *Après réflexion* Oui, nous pouvons également vérifier si (b==0). Et effectuez un peu de logique. Droite. Nous pouvons donc procéder de 3 manières :
  • essayer/attraper
  • throws – transfert vers la méthode appelante
  • vérification des arguments
Dans ce cas, dividequelle méthode vous semble préférable ?
Je choisirais de transmettre l'exception à la méthode appelante, car... dans la méthode Divide, il n'est pas clair comment traiter cette exception et quel type de résultat intrenvoyer en cas d'erreur. Et dans la méthode appelante, j'utiliserais l'argument b pour vérifier s'il est égal à zéro. Il semble que cette réponse ait satisfait la personne interrogée, mais pour être honnête, je ne suis pas sûr que cette réponse soit sans ambiguïté))

Croquis 2. « Qui est le plus rapide ? »

Après la question standard, en quoi une ArrayList diffère d'une LinkedList, est venue ceci : Qu'est-ce qui se passera plus rapidement - insérer un élément au milieu ArrayListou au milieu LinkedList? *Ici, j'ai sauté, je me suis souvenu que partout je lisais quelque chose comme « utiliser LinkedListpour insérer ou supprimer des éléments au milieu de la liste ». À la maison, j'ai même revérifié les conférences JavaRush, il y a une phrase : « si vous comptez insérer (ou supprimer) de nombreux éléments au milieu d'une collection, alors vous feriez mieux d'utiliser LinkedList. Dans tous les autres cas - ArrayList. Réponse automatique* Ce sera plus rapide avec LinkedList. Clarifiez s'il vous plaît
  1. Pour insérer un élément au milieu ArrayList, on retrouve l'élément dans la liste en temps constant, puis on recalcule les indices des éléments à droite de celui inséré, en temps linéaire.
  2. Pour LinkedList.. Nous atteignons d'abord le milieu en temps linéaire, puis insérons un élément en temps constant, en changeant les liens pour les éléments voisins.
Il s’avère donc qu’est-ce qui est le plus rapide ? Hm... Il s'avère que c'est pareil. Mais quand est-ce LinkedListplus rapide ? Il s'avère que lorsque nous l'insérons dans la première moitié de la liste. Par exemple, si vous l'insérez au tout début, vous ArrayListdevrez recalculer tous les indices jusqu'à la fin, mais vous LinkedListn'aurez qu'à changer la référence du premier élément. Moralité : ne croyez pas littéralement tout ce qui est écrit, même dans JavaRush !)

Croquis 3. « Où serions-nous sans égaux et sans hashcode ! »

La conversation sur les égaux et le hashcode a été très longue - comment le remplacer, quelle implémentation dans Object, que se passe-t-il sous le capot, lorsqu'un élément est inséré dans HashMap, etc. Je vais juste citer un certain nombre de points qui sont intéressants à mon avis* Imaginez que nous ayons créé une classe
public class A {
    int id;

    public A(int id) {
        this.id = id;
    }
}
Et ils n’ont pas outrepassé equalset hashcode. Décrire ce qui se passera lorsque le code sera exécuté
A a1 = new A(1);
A a2 = new A(1);
Map<A, String> hash = new HashMap<>();
hash.put(a1, "1");
hash.get(a2);
*C'est bien qu'avant l'entretien, j'ai passé quelques jours à comprendre les algorithmes de base, leur complexité et leurs structures de données - cela m'a beaucoup aidé, merci CS50 !*
  1. Créer deux instances de classe A

  2. Nous créons une carte vide, qui comporte par défaut 16 paniers. La clé est un objet de classe A, dans lequel les méthodes equalset ne sont pas remplacées hashcode.

  3. Mettez-le a1sur la carte. Pour ce faire, nous calculons d'abord le hachage a1.

    À quoi sera égal le hachage ?

    L'adresse d'une cellule en mémoire est une implémentation d'une méthode d'une classeObject

  4. Sur la base du hachage, nous calculons l'indice du panier.

    Comment peut-on le calculer ?

    *Malheureusement, je n'ai pas donné de réponse claire ici. Vous avez un nombre long - un hachage, et il y a 16 compartiments - comment définir un index afin que les objets avec des hachages différents soient répartis uniformément dans les compartiments ? J'imagine que l'indice est calculé comme ceci :

    int index = hash % buckets.length

    Déjà à la maison, j'ai vu que l'implémentation originale dans le code source est légèrement différente :

    static int indexFor(int h, int length)
    {
        return h & (length - 1);
    }
  5. Nous vérifions qu’il n’y a pas de collisions et insérons a1.

  6. Passons à la méthode get. Les instances a1 et a2 sont garanties d'avoir une adresse différente hash(adresse en mémoire différente), nous ne trouverons donc rien pour cette clé

    Et si nous le redéfinissons uniquement hashcodedans la classe A et essayons d'insérer dans le hashmap d'abord une paire avec la clé a1, puis avec a2 ?

    Ensuite, nous trouverons d’abord le panier souhaité hashcode- cette opération sera effectuée correctement. Ensuite, commençons par parcourir les objets Entryde la LinkedList attachée au panier et comparons les clés par equals. Parce que equalsn'est pas remplacé, alors l'implémentation de base est extraite de la classe Object- comparaison par référence. a1 et a2 sont garantis d'avoir des liens différents, nous allons donc « manquer » l'élément inséré a1, et a2 sera placé dans la LinkedList en tant que nouveau nœud.

    Quelle est la conclusion ? Est-il possible de l'utiliser comme clé dans HashMapun objet non remplacé equalshashcode?

    Non tu ne peux pas.

Croquis 4. « Cassons-le exprès !

Après les questions sur les erreurs et les exceptions, la question suivante a suivi : Écrivez un exemple simple dans lequel une fonction lancera StackOverflow. *Ensuite, je me suis rappelé à quel point cette erreur m'avait tourmenté lorsque j'essayais d'écrire une fonction récursive* Cela se produira probablement dans le cas d'un appel récursif, si la condition de sortie de la récursion est incorrectement spécifiée. *Puis j'ai commencé à essayer quelque chose d'intelligent, à la fin l'intervieweur m'a aidé, tout s'est avéré simple*
void sof() {
    sof();
}
En quoi cette erreur est-elle différente de OutOfMemory? *Je n'ai pas répondu ici, ce n'est que plus tard que j'ai réalisé qu'il s'agissait d'une question sur la connaissance Stackde Heapla mémoire Java (les appels et les références aux objets sont stockés dans la pile, et les objets eux-mêmes sont stockés dans la mémoire Heap). En conséquence, StackOverflow est rejeté lorsqu'il n'y a plus d'espace en Stackmémoire pour le prochain appel de méthode et OutOfMemoryque l'espace pour les objets est épuisé en Heapmémoire*
Ce sont les moments de l’entretien dont je me souviens. Au final, j'ai été accepté pour un stage, j'ai donc 2,5 mois de formation devant moi et, si tout se passe bien, un emploi dans l'entreprise) S'il y a de l'intérêt, je peux écrire un autre article, cette fois plus petit, avec une analyse d'un problème simple mais illustratif qui m'a donné un entretien dans une autre entreprise. C'est tout pour moi, j'espère que cet article aidera quelqu'un à approfondir ou à organiser ses connaissances. Bon apprentissage à tous !
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION