Tiếp theo phần 1, mình xin kể tiếp câu chuyện phỏng vấn ở Silicon Valley. Sau lần thất bại đó, đơn vị tiếp tục đưa ứng viên thứ 2 ra phỏng vấn. Thật bất ngờ, khi bác Director of Software engineer lại sử dụng y chang bộ câu hỏi lần trước. Đó là tính huống không được tính đến, vì ai cũng nghĩ rằng cụ sẽ hỏi câu hỏi khác. Kết quả thì ai cũng biết, lại tạch
Đừng bỏ qua Phần 1:
Tuy nhiên, với phương châm không bỏ cuộc, never give up, vì nếu fail phỏng vấn đồng nghĩa là lost dự án, ảnh hưởng đến business của công ty. Theo chỉ thị của lãnh đạo, các ban bệ vào cuộc, toàn bộ record của phỏng vấn được nghe lại, mổ xẻ như phân tích băng ghi hình trận đá bóng. Câu hỏi phỏng vấn được Google search, gửi tới các chuyên gia để được tư vấn. Thậm chí còn được post lên diễn đàn công nghệ để tham khảo (bà con cũng nhảy vô cãi nhau ỏm tỏi, mỗi người 1 đáp án, gạch đá ném đủ xây nhà). Bản thân mình cũng phải cố gắng tìm hiểu Google, tham khảo ý kiến của nhiều người để tìm câu trả lời đúng nhất
Sau hàng loạt các phân tích bình luận của các chuyên gia thì đã tìm ra câu trả lời có thể nói là chính xác nhất
Câu 1: Viết code ví dụ về Singleton class
Thật ra có đến mấy kiểu viết code về Singleton class trong Java trong đó có 2 kiểu thông dụng là
Early Instantiation:
Tạo instance tại thời điểm class loade time
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | it-tab-size:4; tab-size:4;"> public class Singleton { // singleton instance, this instance is created in JVM during start of the application // which is early loading private static final Singleton singletonInst = new Singleton(); // making constructor private so that no other class could use the default constructor private Singleton() { } // the method which gives access to the only instance of Singleton public static Singleton getInstance(){ return singletonInst; } } |
Lazy Loading
Instance được tạo khi Singleton class được dùng lần đầu tiên, như code đã trình bày ở phần 1
Cần phải trình bày cả trường hợp đa luồng như phần 1 nữa. Túm váy lại phải phân tích từng trường hợp, đánh giá ưu nhược điểm
Và 1 Singleton có thể tạm gọi là chuẩn man là như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class Singleton { private static volatile Singleton self; private Singleton() { if (self != null) { throw new UnsupportedOperationException("Use getInstance()"); } } public static synchronized Singleton getInstance() { if (self == null) { self = new Singleton(); } return self; } } |
Quan trọng nhất là câu hỏi này, quyết định việc fail hay pass
Có trường hợp nào Singleton có tạo nhiều instance không?
Câu trả lời đúng ở đây là YES
Java có một khái niệm là Reflection, API cho phép kiểm tra và modify behavior của methods, class at runtime
Reflection này có thể gây ra việc destroy singleton property của singleton class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | // Java code to explain effect of Reflection // on Singleton property import java.lang.reflect.Constructor; // Singleton class class Singleton { // public instance initialized when loading the class public static Singleton instance = new Singleton(); private Singleton() { // private constructor } } public class GFG { public static void main(String[] args) { Singleton instance1 = Singleton.instance; Singleton instance2 = null; try { Constructor[] constructors = Singleton.class.getDeclaredConstructors(); for (Constructor constructor : constructors) { // Below code will destroy the singleton pattern constructor.setAccessible(true); instance2 = (Singleton) constructor.newInstance(); break; } } catch (Exception e) { e.printStackTrace(); } System.out.println("instance1.hashCode():- " + instance1.hashCode()); System.out.println("instance2.hashCode():- " + instance2.hashCode()); } } |
Dẫn tới việc singleton bị destroy và tạo 2 object khác nhau thuộc sample class
1 2 3 4 5 | Output:- instance1.hashCode():- 366712642 instance2.hashCode():- 1829164700 |
Ngoài ra còn có trường hợp serialize/desterilize object cũng xảy ra
https://gurunh.com/2018/05/singleton-co-thuc-su-de/
Trích dẫn nguyên văn câu trong Head First Design Patterns
Be careful if you are using multiple class loaders; this could defeat Singleton implementation and result in multiple instances.
Câu 2: Coding
Viết chuỗi đảo ngược kí tự
Ngoài việc dùng vòng lặp đảo vị trí phần tử như phần 1, thì có thể sử dụng thuật toán đệ quy
1 2 3 4 5 6 7 8 9 10 11 12 13 | public static int[] reverseArray(int[] a,int left,int right){ //Tail Recursion. if(left<right){ //swap elements a[i],a[j] int temp=a[left]; a[left]=a[right]; a[right]=temp; reverseArray(a, left+1, right-1); } return a; } |
Code đơn giản hơn không phải lặp, nhưng mình không nghĩ đệ quy là tối ưu nhất vì trong đệ quy hàm sẽ được gọi nhiều lần và bộ nhớ stack sẽ bị chồng thêm dữ liệu dễ gây issue về bộ nhớ
Bài code số 2
Cho số dương integer n, tìm số cách để để chia n thành 5 số dương nhỏ hơn sao cho cộng lại thì vẫn là n
Ví dụ n = 5 thì output 1 vì chỉ có 1 case (1, 1, 1, 1, 1)
N = 12 thì có 7 cách
Nhờ sự trợ giúp của anh Guc Gồ thì đã phát hiện ra đằng sau bài toán này là cả 1 bầu trời kiến thức về toán học
https://www.mathpages.com/home/kmath556/kmath556.htm
Đọc xong cảm thấy ngu người luôn
Đại loại công thức để tìm số cách thế này. N là số dương cần chia, K là số lượng số dương < N
Tạm tóm lược thay kiểu nông dân như thế này.
Phát biểu tổng quát: cho n viên kẹo chia cho k em bé, hỏi có bao nhiêu cách? (n, k nguyên dương)
Tóm tắt cách giải:
- Trải đều n viên kẹo ra bàn, nhận thấy có n – 1 chỗ trống giữa các viên kẹo
- Ta có k em bé -> cần nhét k-1 vách ngăn để ra k khoảng trống -> đáp án là tổ hợp chập (k-1) của (n-1), hay (n-1)C(k-1)
N = 7, k = 5 theo cách tính tổ hợp chập thì có 15 cách cụ thể là:
{2, 2, 1, 1, 1}
{2, 1, 2, 1, 1}
{2, 1, 1, 2, 1}
{2, 1, 1, 1, 2}
{1, 2, 2, 1, 1}
{1, 2, 1, 2, 1}
{1, 2, 1, 1, 2}
{1, 1, 2, 2, 1}
{1, 1, 2, 1, 2}
{1, 1, 1, 2, 2}
(Thực ra chỉ có 1 cách là 2 số 2 và 3 số 1)
{3, 1, 1, 1, 1}
{1, 3, 1, 1, 1}
{1, 1, 3, 1, 1}
{1, 1, 1, 3, 1}
{1, 1, 1, 1, 3
(Thực ra chỉ có 1 cách là 1 số 3 và 4 số 1)
Tuy nhiên do bài toán ko care đến order nên cần thêm bước loại trừ những kết quả trùng nhau nữa
Câu 3: Câu hỏi về vấn đề hệ thống read data 99%, write 1% thì nên chọn loại dữ liệu gì
Câu trả lời là ArrayList như phần 1, nhưng trường hợp đa luồng thì vẫn có thể dùng ArrayList nhưng ko share list này giữa các thread. Tóm lại là handle with care, hàng dễ vỡ xài cẩn thận
Hoặc 1 câu trả lời khác như sau (Xin tư vấn của 1 cao thủ Java)
Nếu là môi trường single-thread thì dùng ArrayList tốt hơn LinkedList trong trường hợp ở đây (nhất là khi list có kích thước tương đối lớn) là vì ArrayList là random access trong khi LinkedList thì ko.
Đổi lại thì thêm, remove phần tử trên ArrayList sẽ kém hơn về performance so với LinkedList (do phải xử lý cái backed array). Tất nhiên là performance của hệ thống thì ko thể dựa hoàn toàn trên tính toán định tính, mà cần dựa trên các công cụ monitor với con số cụ thể.
Nếu là môi trường multi-thread thì nên dùng CopyOnWriteArrayList, lý do là nó đảm bảo thread-safe trong khi implement của hàm read() trong mô hình CopyOnWrite nó ko đòi hỏi phải sử dụng lock.
Tuy nhiên, câu hỏi thế này thì nên hỏi ngược lại vì nó thiếu thông tin cần thiết để có đáp án chính xác
Dẫn chứng nguyên văn của một tiền bối
Read là read kiểu gì, write là write ntn, vặn lại luôn là đã có điểm cộng rồi. Đề bài cho thế ko thể đủ dữ kiện phán đoán được. Ví dụ random read thì array hơn, nhưng sequential read như nhau. Write cũng vậy, nếu write vào cuối thì array là nhanh nhất. Chém gió nâng cao thì nếu lượng phần tử cực lớn và sequential read thì array sẽ có ưu thế rõ ràng hơn do memory được allocate contiguous chứ không bị fragment như linked list. Do đó nó fit vào cùng cache line, vì thời buổi này bottle neck chủ yếu ở memory, cứ fragment xong miss cache một phát là bay vài trăm cycle ngay.
Câu 4: Anh có service expose API cho client, nếu anh dùng GET api call service qua HTTPS chú thấy có secure ko?
Trong trường hợp sensitive data phải include trong GET request, nó sẽ vi phạm security best practices. Full URL query parameters (bao gồm query parameters) của GET requess sẽ bị logged trong server logs và có nguy cơ được truy cập trái phép. Trong trường hợp data on the query parameters không nên xuất hiện trong logs, bạn nên sử dụng POST thay cho GET
OK, vậy là sau collect đủ các thông tin cần thiết, team đã build thành 1 bộ đề phỏng vấn bao gồm câu hỏi và đáp án. Y chang kiểu bộ đề tuyển sinh đại học.
Công việc tiếp theo là trao bộ đề lại cho 1 thanh niên thứ 3 đi phỏng vấn, vào cầu cho ông cụ hỏi đúng câu cũ
Ứng viên thứ 3 có English khá tốt, bộ đề, đáp án đã được bàn giao và đọc trước. Khác với lần pv trước, lần này có đầy đủ ban bệ lãnh đạo có mặt. Vì xác định lần này tạch thì coi như mất dự án, anh em treo niêu.
Anh e đã làm hết sức mình, còn lại trông chờ vào kĩ năng chém gió của candidate. Nghe nói em manager còn lên chùa Ba Vàng cúng đường hơn hai chục củ để giải vong
Đồng hồ dịch chuyển dần đến giờ interview 9:30, mọi người hồi hộp chờ đợi….
Lại bác Director of Software engineering xuất hiện, nụ cười hiền từ.
Câu hỏi thứ nhất
Câu 1) Viết code ví dụ về Singleton class
Câu 2) Viết code đảo chiều chuỗi…
Câu 3) ….
Đúng là kiếp trước ăn ở tốt, trời phật phù hộ độ trì, các câu hỏi y chang. Kết quả thế nào đã rõ…
Cảm xúc vỡ òa, đúng là bạc già không bằng gà son. Lần này đã bắt bài được cách interview, cụ không lừa em được nữa nhá
Happy Ending
Nhưng giờ vẫn nghĩ ko thông được vì sao cụ lại dùng chung 1 bộ câu hỏi để interview?
Kết luận
Các cụ ngày xưa đã nói rằng “Người thất bại là người bỏ cuộc”, người chiến thắng là người không từ bỏ. Đôi khi thành công đến được còn cần 1 chút may mắn. Nhưng may mắn chỉ có được khi bạn đã cố gắng hết sức của mình. Thành công của người này có được có thể phải đánh đổi bằng sự đóng góp, hi sinh của tập thể, nhưng đều dành cho mục đích chung.
Techtalk via giaosucan