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 cartaint 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
divide
quale 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 int
restituire 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 mezzoArrayList
o nel mezzo LinkedList
? *Qui ho fatto un salto, mi sono ricordato che ovunque leggevo qualcosa del tipo “usa LinkedList
per 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
- 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. - Per
LinkedList
.. Raggiungiamo prima il centro in tempo lineare e poi inseriamo un elemento in tempo costante, cambiando i collegamenti per gli elementi vicini.
LinkedList
più veloce? Si scopre che quando lo inseriamo nella prima metà dell'elenco. Ad esempio, se lo inserisci all'inizio, ArrayList
dovrai ricalcolare tutti gli indici fino alla coda, ma dovrai LinkedList
solo 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 inObject
, 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 equals
e 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!*
-
Creare due istanze della classe A
-
Creiamo una mappa vuota, che per impostazione predefinita ha 16 cestini. La chiave è un oggetto di classe A, in cui i metodi
equals
e non vengono sovrascrittihashcode
. -
Inseriscilo
a1
nella mappa. Per fare ciò, calcoliamo prima l'hasha1
.A quanto sarà uguale l'hash?
L'indirizzo di una cella in memoria è un'implementazione di un metodo di una classe
Object
-
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); }
-
Controlliamo che non ci siano collisioni e inseriamo a1.
-
Passiamo al metodo
get
. È garantito che le istanze a1 e a2 abbiano unhash
indirizzo diverso (diverso in memoria), quindi non troveremo nulla per questa chiaveE se lo ridefinissimo solo
hashcode
in 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 oggettiEntry
nella LinkedList allegata al carrello e confrontiamo le chiavi perequals
. Perchéequals
non viene sovrascritto, l'implementazione di base viene presa dalla classeObject
- 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
HashMap
un oggetto con non sovrascrittoequalshashcode
?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 Stack
della Heap
memoria 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 Stack
memoria per la successiva chiamata al metodo e OutOfMemory
lo spazio per gli oggetti è esaurito in Heap
memoria*
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!
GO TO FULL VERSION