Szkic 1. „Pozornie prosta metoda”
Napisz, jak zaimplementowałbyś metodę zwracającą wynik dzielenia liczby a przez liczbę b. Osoba przeprowadzająca wywiad pisze na kartce papieruint divide(int a, int b) {
}
*Z niedowierzaniem spojrzałem na kartkę papieru z podpisem metody. Gdzie jest haczyk?* Piszę:
int divide(int a, int b) {
return a/b;
}
Czy są jakieś problemy związane z tą metodą? *Łapię naprawdę głupiego głupka* Najwyraźniej nie.. Następnie pojawia się uzasadnione pytanie: A co jeśli b=0? *Wow, wyrzucą mnie z tego biura, jeśli będę tak dalej postępował!* O tak, oczywiście. Tutaj mamy argumenty typu int, więc zostanie zgłoszony wyjątek arytmetyczny. Jeśli argumenty były typu float lub double, wynikiem byłaby Infinity. Co zamierzamy z tym zrobić? Zaczynam pisać try/catch
int divide(int a, int b) {
try {
return a/b;
} catch (Exception e) {
e.printStackTrace();
return ... // ??? what the hack?
}
}
*Mogę wrócić i zamrozić: coś musi zostać zwrócone w przypadku błędu. Ale jak odróżnić to „coś” od wyniku obliczeń?* Co zwrócimy? Hm... Zmieniłbym typ zwracanej zmiennej na Integer iw przypadku wyjątku zwróciłbym wartość null. Wyobraźmy sobie, że nie możemy zmienić typu. Czy możemy się jakoś wydostać? Może poza tym zrobimy coś innego? *Oto nadchodzi* Możemy również przekazać to do metody wywołującej! Prawidłowy. Jak to będzie wyglądać?
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();
}
}
Czy konieczna jest obsługa wyjątku? Tak, ponieważ jawnie przekazujemy to z metody dzielenia. (*Tutaj się myliłem! Poniżej znajdują się pytania prowadzące osobę przeprowadzającą rozmowę kwalifikacyjną, które pozwalają na uzyskanie prawidłowej odpowiedzi*) A wyjątek arytmetyczny — jakiego rodzaju jest to wyjątek — jest zaznaczony czy nie? Jest to wyjątek środowiska wykonawczego, co oznacza, że jest niezaznaczony. *Nadchodzi zabójcze pytanie* Okazuje się, twoimi słowami, że jeśli w sygnaturze metody określiliśmy wyjątek arytmetyczny, to stał się on sprawdzonym wyjątkiem? *Ugh!* Prawdopodobnie... nie. Tak, nie. Jeśli w sygnaturze zaznaczymy rzuty /niezaznaczony wyjątek/, ostrzegamy jedynie, że metoda może zgłosić wyjątek, ale nie jest konieczna obsługa go w metodzie wywołującej. To załatwione. Czy jest coś jeszcze, co możemy zrobić, aby uniknąć błędów? *Po chwili namysłu* Tak, możemy również sprawdzić, czy (b==0). I zastosuj trochę logiki. Prawidłowy. Możemy więc pójść na 3 sposoby:
- próbuj złapać
- rzuty – przekazanie do metody wywołującej
- sprawdzanie argumentów
divide
która metoda jest Twoim zdaniem lepsza? Zdecydowałbym się przekazać wyjątek do metody wywołującej, ponieważ ... w metodzie dzielenia nie jest jasne, jak przetworzyć ten wyjątek i jaki typ wyniku int
ma zostać zwrócony w przypadku błędu. W metodzie wywołującej użyłbym argumentu b, aby sprawdzić, czy jest on równy zero. Wydaje się, że ta odpowiedź zadowoliła rozmówcę, ale szczerze mówiąc, nie jestem pewien, czy ta odpowiedź jest jednoznaczna))
Szkic 2. „Kto jest szybszy?”
Po standardowym pytaniu, czym różni się ArrayList od LinkedList, pojawiło się następujące pytanie: Co stanie się szybciej - wstawienie elementu na środekArrayList
czy na środek LinkedList
? *Tu podskoczyłem, przypomniało mi się, że wszędzie czytam coś w stylu „użyj LinkedList
, aby wstawić lub usunąć elementy na środku listy”. W domu nawet dwukrotnie sprawdziłem wykłady JavaRush, jest tam takie zdanie: „jeśli zamierzasz wstawić (lub usunąć) wiele elementów w środku kolekcji, lepiej użyj LinkedList
. We wszystkich innych przypadkach - ArrayList
”. Odpowiedź automatyczna* Dzięki . będzie szybciej LinkedList
. Wyjaśnij proszę
- Aby wstawić element w środku
ArrayList
, odnajdujemy go na liście w czasie stałym, a następnie przeliczamy indeksy elementów znajdujących się na prawo od wstawianego, w czasie liniowym. - Dla
LinkedList
.. Najpierw dochodzimy do środka w czasie liniowym, a następnie wstawiamy element w czasie stałym, zmieniając połączenia dla elementów sąsiednich.
LinkedList
szybciej? Okazuje się, że gdy wstawimy go do pierwszej połowy listy. Przykładowo, jeśli wstawisz go na samym początku, ArrayList
będziesz musiał przeliczyć wszystkie indeksy aż do samego końca, ale LinkedList
konieczna będzie jedynie zmiana odniesienia do pierwszego elementu. Morał: nie wierz dosłownie wszystkiemu, co jest napisane, nawet w JavaRush!)
Szkic 3. „Gdzie bylibyśmy bez równych sobie i hashcode!”
Rozmowa na temat równości i hashcode była bardzo długa - jak to zastąpić, jaka jest implementacjaObject
, co dzieje się pod maską, kiedy wstawia się element HashMap
itp. Podam tylko kilka punktów, które moim zdaniem są interesujące* Wyobraź sobie, że stworzyliśmy klasę
public class A {
int id;
public A(int id) {
this.id = id;
}
}
I nie zastąpili equals
i hashcode
. Opisz, co się stanie, gdy kod zostanie wykonany
A a1 = new A(1);
A a2 = new A(1);
Map<A, String> hash = new HashMap<>();
hash.put(a1, "1");
hash.get(a2);
*Dobrze, że przed rozmową poświęciłem kilka dni na zrozumienie podstawowych algorytmów, ich złożoności i struktur danych - bardzo mi to pomogło, dzięki CS50!*
-
Utwórz dwie instancje klasy A
-
Tworzymy pustą mapę, która domyślnie posiada 16 koszy. Klucz jest obiektem klasy A, w którym metody
equals
i nie są nadpisywanehashcode
. -
Umieść to
a1
na mapie. Aby to zrobić, najpierw obliczamy skróta1
.Ile będzie równy skrót?
Adres komórki w pamięci jest implementacją metody z klasy
Object
-
Na podstawie hasha obliczamy indeks koszyka.
Jak możemy to obliczyć?
*Niestety, nie udzieliłem tutaj jasnej odpowiedzi. Masz długą liczbę - hash, a jest 16 segmentów - jak zdefiniować indeks, aby obiekty o różnych skrótach były równomiernie rozmieszczone w segmentach? Mogę sobie wyobrazić, że indeks jest obliczany w ten sposób:
int index = hash % buckets.length
Już w domu widziałem, że oryginalna implementacja w kodzie źródłowym jest nieco inna:
static int indexFor(int h, int length) { return h & (length - 1); }
-
Sprawdzamy, czy nie ma kolizji i wstawiamy a1.
-
Przejdźmy do metody
get
. Instancje a1 i a2 na pewno mają innyhash
(inny adres w pamięci), więc dla tego klucza nic nie znajdziemyA co jeśli zdefiniujemy to na nowo tylko
hashcode
w klasie A i spróbujemy wstawić do hashmapy najpierw parę z kluczem a1, a potem z a2?Następnie najpierw znajdziemy żądany koszyk
hashcode
- ta operacja zostanie wykonana poprawnie. Następnie zacznijmy przeglądać obiektyEntry
na liście LinkedList dołączonej do koszyka i porównajmy klucze wedługequals
. Ponieważequals
nie zostanie przesłonięty, wówczas z klasy zostanie pobrana podstawowa implementacjaObject
- porównanie przez odniesienie. a1 i a2 mają gwarancję różnych łączy, więc „pominiemy” wstawiony element a1, a a2 zostanie umieszczone na liście LinkedList jako nowy węzeł.Jaki jest wniosek? Czy można go użyć jako klucza w
HashMap
obiekcie bez nadpisywaniaequalshashcode
?Nie, nie możesz.
Szkic 4. „Złammy to celowo!”
Po pytaniach dotyczących błędu i wyjątku pojawiło się następujące pytanie: Napisz prosty przykład, w którym funkcja wyrzuci StackOverflow. *Wtedy przypomniałem sobie, jak nękał mnie ten błąd, gdy próbowałem napisać jakąś funkcję rekurencyjną.* Prawdopodobnie stanie się to w przypadku wywołania rekurencyjnego, jeśli warunek wyjścia z rekurencji zostanie podany niepoprawnie. *Potem zacząłem próbować czegoś sprytnego, w końcu ankieter pomógł, wszystko okazało się proste*void sof() {
sof();
}
Czym ten błąd różni się od OutOfMemory
? *Nie odpowiedziałem tutaj, dopiero później zrozumiałem, że było to pytanie o znajomość Stack
pamięci Heap
Java (wywołania i referencje do obiektów przechowywane są w stosie, a same obiekty w pamięci sterty). W związku z tym StackOverflow jest wyrzucany, gdy w pamięci nie ma już miejsca Stack
na następne wywołanie metody, a OutOfMemory
w pamięci skończyło się miejsce na obiekty Heap
*
To są momenty z rozmowy, które pamiętam. Ostatecznie zostałem przyjęty na staż, więc przede mną 2,5 miesiąca szkolenia i jak wszystko pójdzie dobrze praca w firmie) W razie zainteresowania mogę napisać kolejny artykuł, tym razem mniejszy, z analiza prostego, ale ilustrującego problemu, że zostałem zaproszony na rozmowę kwalifikacyjną w innej firmie. To tyle, mam nadzieję, że ten artykuł pomoże komuś pogłębić lub uporządkować swoją wiedzę. Życzę wszystkim miłej nauki!
GO TO FULL VERSION