JavaRush /Java Blog /Random-IT /La storia di un'intervista: domande interessanti
GuitarFactor
Livello 30
Санкт-Петербург

La storia di un'intervista: domande interessanti

Pubblicato nel gruppo Random-IT
Recentemente ho avuto l'opportunità di partecipare ad un colloquio per una posizione di stagista in una delle grandi aziende IT. Il racconto di un'intervista: domande interessanti - 1Questo è stato il mio primo colloquio informatico e, secondo me, si è rivelato interessante. In totale, sono stato “interrogato” per più di 3 ore (questo è stato preceduto dai compiti e da un test in ufficio su un computer). Voglio rendere omaggio all'intervistatore che non si è arreso quando ho risposto in modo errato alla domanda, ma con l'aiuto delle sue domande guida mi ha costretto a riflettere e ad arrivare alla risposta corretta. Di seguito presenterò diversi "schizzi" - a mio avviso, domande piuttosto interessanti, alcune delle quali mi hanno permesso di comprendere più a fondo alcuni aspetti di Java. Forse ad alcuni queste cose sembreranno ovvie, ma penso che ci saranno persone per le quali ciò sarà utile. Di seguito le frasi sono evidenziate con i seguenti caratteri: Intervistatore - in grassetto Spiegazioni fuori campo e i miei pensieri - in corsivo Le mie risposte - in carattere normale Abbiamo finito con il background, passiamo al sodo)

Schizzo 1. “Un metodo apparentemente semplice”

Scrivi come implementeresti un metodo che restituisca il risultato della divisione del numero A per il numero B. L'intervistatore scrive su un pezzo di carta
int divide(int a, int b) {
}
*Ho guardato incredulo il pezzo di carta con la firma del metodo. Qual è il problema?* Scrivo:
int divide(int a, int b) {
    return a/b;
}
Ci sono problemi con questo metodo? *Mi sto prendendo per un idiota davvero stupido* Apparentemente no.. Poi arriva una domanda legittima: E se b=0? *Whoa, sto per essere cacciato da questo ufficio se continuo così!* Oh sì, certo. Qui abbiamo argomenti di tipo int, quindi verrà lanciata un'eccezione aritmetica. Se gli argomenti fossero di tipo float o double, il risultato sarebbe Infinity. Cosa faremo a riguardo? Sto iniziando a scrivere try/catch
int divide(int a, int b) {
    try {
        return a/b;
    } catch (Exception e) {
        e.printStackTrace();
        return ... // ??? what the hack?
    }
}
*Posso restituire e bloccare: qualcosa deve essere restituito in caso di errore. Ma come distinguere questo “qualcosa” dal risultato del calcolo?* Cosa restituiremo? Hm... cambierei il tipo della variabile di ritorno in Integer e in caso di eccezione restituirei null. Immaginiamo di non poter cambiare il tipo. Possiamo in qualche modo uscire? Forse possiamo fare qualcos'altro con l'eccezione? *Ecco che arriva* Possiamo anche inoltrarlo al metodo chiamante! Giusto. Come sembrerà?
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();
    }
}
È necessario gestire l'eccezione? Sì, perché lo inoltriamo esplicitamente dal metodo divide. (*Mi sbagliavo! Quelle che seguono guidano le domande dell'intervistatore per arrivare alla risposta corretta*) E l'eccezione aritmetica - che tipo di eccezione è - selezionata o deselezionata? Questa è un'eccezione di runtime, il che significa non selezionata. *Ecco la domanda killer* Quindi risulta, con le tue parole, se abbiamo specificato lancia un'eccezione aritmetica nella firma del metodo, allora diventa un'eccezione verificata? *Ugh!* Probabilmente... no. Sì, non c'è più. Se indichiamo lancia /eccezione non controllata/ nella firma, avvertiamo solo che il metodo può lanciare un'eccezione, ma non è necessario gestirla nel metodo chiamante. Questo è tutto risolto. C’è qualcos’altro che possiamo fare per evitare errori? *Dopo qualche riflessione* Sì, possiamo anche verificare se (b==0). Ed esegui un po' di logica. Giusto. Quindi possiamo procedere in 3 modi:
  • prova a prendere
  • lancia: inoltro al metodo chiamante
  • controllo degli argomenti
In questo caso dividequale metodo ritieni preferibile?
Sceglierei di inoltrare l'eccezione al metodo chiamante, perché... nel metodo divide non è chiaro come elaborare questa eccezione e che tipo di risultato intrestituire in caso di errore. E nel metodo chiamante utilizzerei l'argomento b per verificare se è uguale a zero. Sembra che questa risposta abbia soddisfatto l'intervistato, ma ad essere sincero, non sono sicuro che questa risposta sia inequivocabile))

Schizzo 2. “Chi è più veloce?”

Dopo la domanda standard, in cosa differisce un ArrayList da un LinkedList, è arrivata questa: cosa accadrà più velocemente: inserendo un elemento nel mezzo ArrayListo nel mezzo LinkedList? *Qui ho fatto un salto, mi sono ricordato che ovunque leggevo qualcosa del tipo “usa LinkedListper inserire o togliere elementi in mezzo alla lista”. A casa ho ricontrollato anche le lezioni di JavaRush, c'è una frase: “se hai intenzione di inserire (o eliminare) molti elementi nel mezzo di una raccolta, allora è meglio usare LinkedList. In tutti gli altri casi... ArrayList». Risposta automatica* Sarà più veloce con LinkedList. Chiarire per favore
  1. Per inserire un elemento al centro ArrayList, troviamo l'elemento nella lista in tempo costante, quindi ricalcoliamo gli indici degli elementi a destra di quello inserito, in tempo lineare.
  2. Per LinkedList.. Raggiungiamo prima il centro in tempo lineare e poi inseriamo un elemento in tempo costante, cambiando i collegamenti per gli elementi vicini.
Quindi risulta, quale è più veloce? Hm... Risulta lo stesso. Ma quando è LinkedListpiù veloce? Si scopre che quando lo inseriamo nella prima metà dell'elenco. Ad esempio, se lo inserisci all'inizio, ArrayListdovrai ricalcolare tutti gli indici fino alla coda, ma dovrai LinkedListsolo cambiare il riferimento del primo elemento. Morale: non credere letteralmente a tutto quello che c'è scritto, anche in JavaRush!)

Schizzo 3. “Dove saremmo senza uguali e hashcode!”

La conversazione su equals e hashcode è stata molto lunga: come sovrascriverlo, quale implementazione in Object, cosa succede dietro le quinte, quando un elemento viene inserito in HashMap, ecc. Citerò solo alcuni punti che secondo me sono interessanti* Immagina di aver creato una classe
public class A {
    int id;

    public A(int id) {
        this.id = id;
    }
}
E non hanno sovrascritto equalse hashcode. Descrivi cosa accadrà quando il codice verrà eseguito
A a1 = new A(1);
A a2 = new A(1);
Map<A, String> hash = new HashMap<>();
hash.put(a1, "1");
hash.get(a2);
*È positivo che prima del colloquio abbia dedicato un paio di giorni a comprendere gli algoritmi di base, la loro complessità e le strutture dei dati: mi ha aiutato molto, grazie CS50!*
  1. Creare due istanze della classe A

  2. Creiamo una mappa vuota, che per impostazione predefinita ha 16 cestini. La chiave è un oggetto di classe A, in cui i metodi equalse non vengono sovrascritti hashcode.

  3. Inseriscilo a1nella mappa. Per fare ciò, calcoliamo prima l'hash a1.

    A quanto sarà uguale l'hash?

    L'indirizzo di una cella in memoria è un'implementazione di un metodo di una classeObject

  4. Sulla base dell'hash, calcoliamo l'indice del paniere.

    Come possiamo calcolarlo?

    *Purtroppo non ho dato una risposta chiara qui. Hai un numero lungo - un hash e ci sono 16 bucket - come definire un indice in modo che gli oggetti con hash diversi siano distribuiti uniformemente tra i bucket? Potrei immaginare che l'indice sia calcolato in questo modo:

    int index = hash % buckets.length

    Già a casa ho visto che l'implementazione originale nel codice sorgente è leggermente diversa:

    static int indexFor(int h, int length)
    {
        return h & (length - 1);
    }
  5. Controlliamo che non ci siano collisioni e inseriamo a1.

  6. Passiamo al metodo get. È garantito che le istanze a1 e a2 abbiano un hashindirizzo diverso (diverso in memoria), quindi non troveremo nulla per questa chiave

    E se lo ridefinissimo solo hashcodein classe A e provassimo a inserire nell'hashmap prima una coppia con chiave a1, e poi con a2?

    Quindi per prima cosa troveremo il cestino desiderato hashcode: questa operazione verrà eseguita correttamente. Successivamente, iniziamo a scorrere gli oggetti Entrynella LinkedList allegata al carrello e confrontiamo le chiavi per equals. Perché equalsnon viene sovrascritto, l'implementazione di base viene presa dalla classe Object- confronto per riferimento. è garantito che a1 e a2 abbiano collegamenti diversi, quindi "mancheremo" l'elemento inserito a1 e a2 verrà inserito nella LinkedList come nuovo nodo.

    Qual è la conclusione? È possibile utilizzarlo come chiave in HashMapun oggetto con non sovrascritto equalshashcode?

    No, non puoi.

Schizzo 4. “Rompiamolo apposta!”

Dopo le domande su Errore ed Eccezione, è seguita la seguente domanda: Scrivi un semplice esempio in cui una funzione genererà StackOverflow. *Poi mi sono ricordato di come questo errore mi tormentava mentre cercavo di scrivere una funzione ricorsiva* Questo probabilmente accadrà nel caso di una chiamata ricorsiva, se la condizione per uscire dalla ricorsione è specificata in modo errato. *Poi ho iniziato a provare qualcosa di intelligente, alla fine l'intervistatore mi ha aiutato, tutto si è rivelato semplice*
void sof() {
    sof();
}
In cosa questo errore è diverso da OutOfMemory? *Non ho risposto qui, solo più tardi ho capito che si trattava di una domanda sulla conoscenza Stackdella Heapmemoria Java (le chiamate e i riferimenti agli oggetti sono archiviati nello Stack e gli oggetti stessi sono archiviati nella memoria Heap). Di conseguenza, StackOverflow viene eliminato quando non c'è più spazio in Stackmemoria per la successiva chiamata al metodo e OutOfMemorylo spazio per gli oggetti è esaurito in Heapmemoria*
Questi sono i momenti dell'intervista che ricordo. Alla fine sono stato accettato per uno stage, quindi ho davanti a me 2,5 mesi di formazione e, se tutto va bene, un lavoro in azienda) Se c'è interesse, posso scrivere un altro articolo, questa volta più piccolo, con un'analisi di un problema semplice ma illustrativo che mi è stato concesso durante un colloquio presso un'altra azienda. Questo è tutto per me, spero che questo articolo possa aiutare qualcuno ad approfondire o organizzare le proprie conoscenze. Buon apprendimento a tutti!
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION