JavaRush /Java-Blog /Random-DE /Die Geschichte eines Interviews: interessante Fragen
GuitarFactor
Level 30
Санкт-Петербург

Die Geschichte eines Interviews: interessante Fragen

Veröffentlicht in der Gruppe Random-DE
Kürzlich hatte ich die Gelegenheit, an einem Vorstellungsgespräch für eine Praktikumsstelle in einem der großen IT-Unternehmen teilzunehmen. Die Geschichte eines Interviews: interessante Fragen - 1Das war mein erstes IT-Interview und meiner Meinung nach war es interessant. Insgesamt wurde ich mehr als 3 Stunden lang „verhört“ (davor gab es Hausaufgaben und einen Test im Büro am Computer). Ich möchte dem Interviewer meinen Dank aussprechen, der nicht aufgab, als ich die Frage falsch beantwortete, sondern mich mit Hilfe seiner Leitfragen zum Nachdenken zwang und zur richtigen Antwort kam. Im Folgenden werde ich einige „Skizzen“ vorstellen – meiner Meinung nach recht interessante Fragen, die mir zum Teil ein tieferes Verständnis bestimmter Aspekte in Java vermittelt haben. Vielleicht werden diese Dinge für einige offensichtlich erscheinen, aber ich denke, es wird diejenigen geben, für die dies nützlich sein wird. Unten sind die Sätze in den folgenden Schriftarten hervorgehoben: Interviewer – in Fettdruck Erklärungen aus dem Off und meine Gedanken – in Kursivschrift Meine Antworten – in normaler Schriftart Wir sind mit dem Hintergrund fertig, kommen wir zur Sache)

Skizze 1. „Eine scheinbar einfache Methode“

Schreiben Sie, wie Sie eine Methode implementieren würden, die das Ergebnis der Division von Zahl a durch Zahl b zurückgibt. Der Interviewer schreibt auf ein Blatt Papier
int divide(int a, int b) {
}
*Ich warf einen ungläubigen Blick auf den Zettel mit der Methodensignatur. Was ist der Haken?* Ich schreibe:
int divide(int a, int b) {
    return a/b;
}
Gibt es Probleme mit dieser Methode? *Ich fange einen wirklich dummen Idioten* Anscheinend nicht. Als nächstes kommt eine berechtigte Frage: Was wäre, wenn b=0? *Whoa, ich werde gleich aus diesem Büro geworfen, wenn ich so weitermache!* Oh ja, natürlich. Hier haben wir Argumente vom Typ int, sodass eine arithmetische Ausnahme ausgelöst wird. Wenn die Argumente vom Typ float oder double wären, wäre das Ergebnis Infinity. Was werden wir dagegen tun? Ich fange an, Try/Catch zu schreiben
int divide(int a, int b) {
    try {
        return a/b;
    } catch (Exception e) {
        e.printStackTrace();
        return ... // ??? what the hack?
    }
}
*Ich komme zurück und friere ein: Im Fehlerfall muss etwas zurückgegeben werden. Doch wie lässt sich dieses „Etwas“ vom Ergebnis der Berechnung unterscheiden?* Was werden wir zurückgeben? Hm... Ich würde den Typ der Rückgabevariablen in Integer ändern und im Ausnahmefall Null zurückgeben. Stellen wir uns vor, wir können den Typ nicht ändern. Können wir irgendwie rauskommen? Vielleicht können wir mit der Ausnahme etwas anderes machen? *Hier kommt es* Wir können es auch an die aufrufende Methode weiterleiten! Rechts. Wie wird es aussehen?
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();
    }
}
Ist es notwendig, die Ausnahme zu behandeln? Ja, weil wir es explizit von der Divide-Methode weiterleiten. (*Hier habe ich mich geirrt! Was folgt, sind die Leitfragen des Interviewers, die zur richtigen Antwort führen*) Und eine arithmetische Ausnahme – was für eine Ausnahme ist das – aktiviert oder deaktiviert? Dies ist eine Laufzeitausnahme, das heißt, sie ist nicht aktiviert. *Hier kommt die Killerfrage* Es stellt sich also heraus, dass, wenn wir in der Methodensignatur die Ausnahme „Throws Arithmetic Exception“ angegeben haben, es sich in Ihren Worten um eine geprüfte Ausnahme handelt? *Ugh!* Wahrscheinlich... nein. Ja, es ist weg. Wenn wir in der Signatur „wirft /ungeprüfte Ausnahme/“ angeben, warnen wir nur, dass die Methode eine Ausnahme auslösen kann, es ist jedoch nicht erforderlich, diese in der aufrufenden Methode zu behandeln. Das ist geklärt. Können wir sonst noch etwas tun, um Fehler zu vermeiden? *Nach einigem Nachdenken* Ja, wir können auch prüfen, ob (b==0). Und führen Sie etwas Logik aus. Rechts. Wir können also drei Wege gehen:
  • versuchen/fangen
  • throws – Weiterleitung an die aufrufende Methode
  • Argumentüberprüfung
Welche Methode ist in diesem Fall divideIhrer Meinung nach vorzuziehen?
Ich würde mich dafür entscheiden, die Ausnahme an die aufrufende Methode weiterzuleiten, weil ... In der Divide-Methode ist nicht klar, wie diese Ausnahme verarbeitet werden soll und welche Art von Ergebnis intim Fehlerfall zurückgegeben werden soll. Und in der aufrufenden Methode würde ich das Argument b verwenden, um zu prüfen, ob es gleich Null ist. Es scheint, dass diese Antwort den Befragten zufrieden gestellt hat, aber ehrlich gesagt bin ich mir nicht sicher, ob diese Antwort eindeutig ist))

Skizze 2. „Wer ist schneller?“

Nach der Standardfrage, wie unterscheidet sich eine ArrayList von einer LinkedList, kam diese: Was geht schneller – das Einfügen eines Elements in die Mitte ArrayListoder in die Mitte LinkedList? *Als ich sprang, fiel mir ein, dass ich überall etwas gelesen habe wie „zum LinkedListEinfügen oder Entfernen von Elementen in der Mitte der Liste“. Zu Hause habe ich sogar die JavaRush-Vorlesungen noch einmal überprüft. Dort gibt es einen Satz: „Wenn Sie viele Elemente in die Mitte einer Sammlung einfügen (oder löschen) möchten, verwenden Sie besser LinkedList. In allen anderen Fällen - ArrayList.“ Automatisch beantwortet* Mit geht es schneller LinkedList. Bitte klären Sie es
  1. Um ein Element in der Mitte einzufügen ArrayList, suchen wir das Element in der Liste in konstanter Zeit und berechnen dann die Indizes der Elemente rechts vom eingefügten Element in linearer Zeit neu.
  2. Für LinkedList.. Wir erreichen zunächst die Mitte in linearer Zeit und fügen dann in konstanter Zeit ein Element ein, wobei wir die Verknüpfungen für benachbarte Elemente ändern.
Es stellt sich also heraus, was schneller ist? Hm... Es stellt sich heraus, dass es dasselbe ist. Aber wann ist es LinkedListschneller? Das stellt sich heraus, wenn wir es in die erste Hälfte der Liste einfügen. Wenn Sie es beispielsweise ganz am Anfang einfügen, ArrayListmüssen Sie alle Indizes bis zum Ende neu berechnen, müssen aber LinkedListnur die Referenz des ersten Elements ändern. Moral: Glauben Sie nicht buchstäblich alles, was geschrieben steht, auch nicht in JavaRush!)

Skizze 3. „Wo wären wir ohne Equals und Hashcode!“

Das Gespräch über Equals und Hashcode war sehr lang – wie man es überschreibt, welche Implementierung in Object, was unter der Haube passiert, wenn ein Element in eingefügt wird HashMapusw. Ich möchte nur einige Punkte nennen, die meiner Meinung nach interessant sind* Stellen Sie sich vor, wir haben eine Klasse erstellt
public class A {
    int id;

    public A(int id) {
        this.id = id;
    }
}
Und sie haben nicht überschrieben equalsund hashcode. Beschreiben Sie, was passiert, wenn der Code ausgeführt wird
A a1 = new A(1);
A a2 = new A(1);
Map<A, String> hash = new HashMap<>();
hash.put(a1, "1");
hash.get(a2);
*Es ist gut, dass ich vor dem Interview ein paar Tage gezielt damit verbracht habe, die grundlegenden Algorithmen, ihre Komplexität und Datenstrukturen zu verstehen – es hat sehr geholfen, danke CS50!*
  1. Erstellen Sie zwei Instanzen der Klasse A

  2. Wir erstellen eine leere Karte, die standardmäßig 16 Körbe enthält. Der Schlüssel ist ein Objekt der Klasse A, in dem die Methoden equalsund nicht überschrieben werden hashcode.

  3. Tragen Sie es a1in die Karte ein. Dazu berechnen wir zunächst den Hash a1.

    Was wird der Hash sein?

    Die Adresse einer Zelle im Speicher ist eine Implementierung einer Methode einer KlasseObject

  4. Basierend auf dem Hash berechnen wir den Korbindex.

    Wie können wir es berechnen?

    *Leider habe ich hier keine klare Antwort gegeben. Sie haben eine lange Zahl – einen Hash, und es gibt 16 Buckets – wie definiert man einen Index, damit Objekte mit unterschiedlichen Hashes gleichmäßig auf die Buckets verteilt werden? Ich könnte mir vorstellen, dass der Index so berechnet wird:

    int index = hash % buckets.length

    Schon zu Hause habe ich gesehen, dass die ursprüngliche Implementierung im Quellcode etwas anders ist:

    static int indexFor(int h, int length)
    {
        return h & (length - 1);
    }
  5. Wir prüfen, dass keine Kollisionen vorliegen und fügen a1 ein.

  6. Kommen wir zur Methode get. Die Instanzen a1 und a2 haben garantiert eine unterschiedliche hash(unterschiedliche Adresse im Speicher), daher werden wir für diesen Schlüssel nichts finden

    Was wäre, wenn wir es nur hashcodein Klasse A neu definieren und versuchen, zuerst ein Paar mit dem Schlüssel a1 und dann mit a2 in die Hashmap einzufügen?

    Dann finden wir zunächst den gewünschten Korb nach hashcode- dieser Vorgang wird korrekt ausgeführt. Beginnen wir als Nächstes damit, die Objekte in der an den Warenkorb angehängten LinkedList durchzugehen Entryund die Schlüssel zu vergleichen equals. Weil equalsnicht überschrieben wird, wird die Basisimplementierung aus der Klasse übernommen Object– Vergleich per Referenz. a1 und a2 haben garantiert unterschiedliche Links, sodass wir das eingefügte Element a1 „verpassen“ und a2 als neuer Knoten in die LinkedList eingefügt wird.

    Was ist die Schlussfolgerung? Ist die Verwendung als Schlüssel in HashMapeinem Objekt ohne Überschreibung möglich equalshashcode?

    Nein, geht nicht.

Skizze 4. „Lass es uns absichtlich kaputt machen!“

Nach den Fragen zu Fehler und Ausnahme folgte die folgende Frage: Schreiben Sie ein einfaches Beispiel, in dem eine Funktion StackOverflow auslöst. *Dann fiel mir ein, wie mich dieser Fehler plagte, als ich versuchte, eine rekursive Funktion zu schreiben* Dies wird wahrscheinlich bei einem rekursiven Aufruf passieren, wenn die Bedingung zum Beenden der Rekursion falsch angegeben ist. *Dann habe ich angefangen, etwas Cleveres auszuprobieren, am Ende hat der Interviewer geholfen, alles hat sich als einfach herausgestellt*
void sof() {
    sof();
}
Wie unterscheidet sich dieser Fehler von OutOfMemory? *Ich habe hier nicht geantwortet, erst später wurde mir klar, dass es sich hierbei um Kenntnisse über den StackJava Heap-Speicher handelte (Aufrufe und Verweise auf Objekte werden im Stack gespeichert, und die Objekte selbst werden im Heap-Speicher gespeichert). Dementsprechend wird StackOverflow verworfen, wenn im StackSpeicher kein Platz mehr für den nächsten Methodenaufruf vorhanden ist und OutOfMemoryder Platz für Objekte im Speicher aufgebraucht ist Heap*
Das sind die Momente aus dem Interview, die mir in Erinnerung bleiben. Am Ende wurde ich für ein Praktikum angenommen, ich habe also 2,5 Monate Ausbildung vor mir und, wenn alles gut läuft, einen Job im Unternehmen.) Bei Interesse kann ich einen weiteren Artikel, diesmal kleiner, mit schreiben eine Analyse eines einfachen, aber anschaulichen Problems, dass ich bei einem anderen Unternehmen zu einem Vorstellungsgespräch eingeladen wurde. Das ist alles für mich. Ich hoffe, dieser Artikel hilft jemandem, sein Wissen zu vertiefen oder zu organisieren. Viel Spaß beim Lernen!
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION