webpack4.0各個擊破(3)—— Assets篇
885
2022-05-30
前言
面試別人,對我來說是一件新奇事,以前都是別人面試我。我清楚地知道,我在的地域與公司,難以吸引到中國的一流軟件人才。所以,我特地調低了期望,很少問什么深入的技術問題,只問一些廣泛的、基礎的。我只要最終給Leader一句“這個人技術還行/很好/非常好”,就行了。至于其它能力、綜合水平,由別人把關。為此,在挑選唯一的一道筆試題時,我特別地上心。
首先,我不敢用網上那些廣為流傳的,比如Leetcode、《程序員面試寶典》里的題——這些都太難了!正兒八經做,其實很少有人能在1小時內完美做出來,除非之前遇到過。我本人也并非什么思維敏捷的牛人,不然也不會混得這么慘。正所謂己所不欲,勿施于人,我也不希望以后別人考我特別麻煩的算法題,所以自創了一道特別簡單的。
其次,對(Android平臺的)Java程序員來說,大多數情況下不需要寫什么復雜的算法。相反,Java層主要做的是界面控制、業務邏輯、數據流之類的,更提倡代碼的簡單和可讀,盡量用既有的公共類庫,不惜損失一些運行效率。拿一道復雜的算法題,考一個Java程序員,多少有點刁難人。
最后,還是那個薪資待遇和人才梯度問題。沒有Google的工資,就別考Google的題;沒有Google的向心力,就別期待有Google級別的人才來面試。
亮題
以下有一個 static method,類外會調用它,一個個地插入一些元素進入一個List。可以改變這個List內容的,只有這一個method,要求任何時候這個List都是有序的。比如,依次插入3、2、1、2,我希望List的順序是1、2、2、3。
class?Solution?{
private?static?List
public?static?void?addElement(int?e)?{
//?TODO:?Insert?e?to?sSorted?and?make?sure?sSorted?is?always?sorted.
}
}
我會給出15分鐘的時間,而其實往往會再多給10分鐘。(有興趣,你可以停在這試試。相信在看文章這種輕松的環境下,理清這道題的思路也就10~30秒。)
(為什么下限是10秒呢?唉……一不小心暴露了我智商的峰值。我實際問過一些同事,他們通常在理解的同時,就立刻給出了正確的思路,過程不足5秒,其中甚至包括一個硬件工程師,和一個只負責溝通和文檔的妹子。)
提示
在過程中,我會逐步給出一些提示,從接口到思路,都會主動提供,其它也基本有問必答。如果單純考算法,C語言才是最合適的,因為它沒有什么高級的工具類,什么復雜點的都得自己寫。而Java,則有一些“基礎”類庫是難以記憶的。比如前面出現的java.util.List,就沒有多少人能在紙上寫出它的常用接口。
我并不想考察什么死記硬背,在這個時代,斷網后本來就沒幾個程序員能正常編程。所以我會主動提供一份List的不完全接口列表。
public?interface?List
public?void?add(int?location,?E?object);
public?void?add(E?object);
public?void?clear();
public?boolean?contains(Object?object);
public?boolean?equals(Object?object);
public?E?get(int?location);
public?int?indexOf(Object?object);
public?boolean?isEmpty();
public?E?remove(int?location);
public?E?set(int?location,?E?object);
public?int?size();
}
我沒有給出完全的接口,因為給多了無疑是誤導人。真正能用上的接口其實也就3個,但我也總不能只給3個,提示得太明顯,也限制了對方的思路。所以,給出了可能用得上的這幾個。我也沒給出注釋,因為有聲明就已經夠了。而且如果對方問起,我也會給出解釋。
一開始我想,考一個排序算了。但是轉念一想,這也太不負責任了。對面要是背一道冒泡排序的解法上來,達不到考察技術水平的目的,Boss也不會認可。本著“放水不能太明顯”的原則,我想考插入排序,并且把題目弄得沒多少人見過。
排序是一類基本算法,合格的程序員至少會一種。大多數人都只會入門級的冒泡排序,而我更喜歡插入排序,原因……你會明白的。
插入排序,其實就是把數組或列表在邏輯上分成兩部分,一部分是待排序的,一部分是有序的。一開始,有序的部分只有一個元素(或者一個都沒有),然后從待排序的部分里一個個抽出來,插入到有序的部分。等元素都插入到了有序的部分,排序過程也就完成了。
你看,也就***N次的事。而我這道題,就是只考插入排序算法的一半,會插就行。
在面試過程中,我甚至常常親自解釋插入排序是怎么回事——放水到這個份上,我都不忍心再退步了。
真正的考察點
這是一份Android平臺的開發工作,Boss要求的是能干活、干好活。我給出的建議要求是:
熟悉Java。
有良好的溝通、表達能力。
學習能力強,喜歡不斷拓展計算機領域的知識。
有良好的編碼習慣,愿意為代碼的簡潔、優雅而反復修改。
我建議Boss放棄學歷和工作年限的要求,技術崗位就應該只考察技術(和其它基本能力),不應該考察技術的間接證明。
Java是Android的基本功(我們不玩Kotlin、Scala、React Native等新花樣),這門語言如果不扎實,那至少得帶半年。
我沒有在Android崗明確地要求考察Android,是因為Android的那些東西相對來說容易學習。即便是毫無經驗的新手,要搞清楚什么“四大組件”“五大布局”,也就一兩天的事。而如果Java不夠扎實,各種肉眼可見的大小bug就會層出不窮,知識盲點一兩年都補不完。
溝通是職場基本功。如果話都說不清,那么會顯著降低團隊的溝通效率。而且,我個人認為,話說不清的人,代碼一定寫不好。語言條理清晰,邏輯層次分明,體現到代碼上,就是簡潔、明朗。
學習能力、求知欲,是作為一個程序員的基本素養。因為,大部分人的工作,類似于在一堆按鈕中,找到合適的那個按下去;而程序員的工作,往往是閉著眼睛這么干。開發工程師通常是在一堆未知(沒讀過的代碼、不知道的接口)中,把一小部分變成已知(讀懂了的代碼或接口),進行一些增刪改,最后達成外界(產品經理、設計師、測試工程師)賦予的業務目標。
一些職業賣口水,一些職業賣口才。一些職業賣青春,一些職業賣肉體(咳咳,我說的是空姐和搬磚,想歪的去面壁)。一些職業賣知識,一些職業賣能力。
程序員,或者說軟件開發工程師,賣的是學習能力(其實也包括青春和肉體),快速學會各種知識,找到那些藏在屏幕外的按鈕,并且正確的按下去。比如,像Bash這類Command line工具,就是自己敲命令出來執行,而不是去界面上找功能對應的按鈕;而程序設計、實現,就是去發現、或者創造一種解決問題的辦法,然后用代碼表達出來——你看,都是在干一些反UI、UX設計的事。唯有不斷地學習,才能提高效率,把自己從加班中解脫出來,把項目從bug中拯救出來。所以,厭學的人當不了好程序員,也干不長。
編碼習慣,相對次之。部分觀點認為,這東西伴隨一生,如果一開始沒有好習慣,這輩子都沒辦法改了。Boss就是這么認為的,我倒是不這么認為。我相信編碼習慣的可塑性是很高的——你不按規范寫,我不給你merge,改不改?
但是,編碼習慣作為程序員的軟技能,還是可以一定程度上看出其技術素養、代碼質量的。至于優雅什么的,我其實沒有真的敢這么期待。
所以,我這道題其實是考察這四點。
能寫出來,并且無明顯問題,代表Java基本功扎實。
理解我對題目的描述,和我確認清楚題目的細節,這是看溝通能力。
List接口不知道,我給你啊;插入排序不會,我教你啊;其它還有什么不會,你問啊——這是在考察學習能力。
代碼的字里行間,可以明顯看出編碼習慣。
面試結果
總體來說,我很傷心。
第一位就讓我很傷心,當我看了他前兩行代碼,就不忍心接著往下看:
private?static?List
public?static?void?addElement(int?e)?{
if?(null?==?sorted)?{
sorted.add(e);
}
//?I?couldn't?read?more!
第一行就編譯不過。如果他對Java的一些命名規范有一定的了解,就絕不會把sSorted寫成sorted。(當然,sSorted也許并不是合適的命名方式,因為s和m這類前綴有些冗余。我通常遵守Android源碼的通用規范,它是有這類前綴的。)
第二行必然拋出NullPointerException,而不知道是該慶幸還是悲傷的是,它永遠執行不到。根據我已經給出的一個接口addElement,和可以猜到或者問出來的讀取接口,都是不會把sSorted變成null的。這體現了溝通、理解能力的一點問題。
此外,即使sSorted因為什么bug而變成null,這里也不應該做處理,而是任其拋出NullPointerException,或者轉義一下,主動拋出IllegalStateException。否則,此處將變成一個不會crash的隱藏bug。不能用正常處理,代替異常處理;當然,也不能用異常處理,代替流程控制。
另外,更令我失望的是,有一位是這么寫的:
for?(int?i?=?0;?i?
if?(e?==?sSorted.get(i))?{
sSorted.add(i,?e);
}
}
我問他,如果這個元素不在這個List里存在怎么辦?如果這個List是空的怎么辦?他頓時一囧,我也一起囧,心想自己是不是太壞了。
還有一位,仿佛聽見了我這幾個問題,他竟然一一作答:
if?(sSorted.size?==?0)?{
sSorted.add(e);
return;
}
if?(e?>=?sSorted.get(sSorted.size?-?1))?{
sSorted.add(e);
return;
}
if?(e?<=?sSorted.get(0))?{
sSorted.add(0,?e);
return;
}
if?(sSorted.contains(e))?{
sSorted.add(sSorted.indexOf(e),?e);
return;
}
//?more...
他想干什么呢?也許是優化性能吧,只能這么幫腔了。另外,他對size的理解,和數組的length相同。
這位算是經驗比較豐富(30歲),對Java的理解比較深入的了。他說排序不需要手寫,Java里有現成的接口。我說,是這樣沒錯,但接口我沒給出,如果你記得,那就寫出來吧。
于是他在剛才那一大段“優化”的后面,這么寫了:
sSorted.add(e);
sSorted.sort(new?Comp...able()?{
public?boolean??(left,?right)?{
return?right?>=?left;
}
});
思路上,插入后再排序,我先不吐槽。我明明說了“記得”再寫,這Comparable及其接口int compareTo(T another)如果記不清,我就當看lambda表達式了。可是,他這個?分明是Comparator的int compare(T lhs, T rhs)接口呀!
不過,其實這些我都可以捏著鼻子認了,因為我也手寫不出來。但List是沒有sort方法的呀!
Arrays和Collections才有各自的sort方法,它倆算是銀彈型工具類,而Array和Collection是沒有的。這個細節,誰用誰知道,知道了就絕不會記錯,盡管就差一個s。
還有一位,他先插入、再冒泡排序,是這么寫的:
sSorted.add(e);
for?(int?i?=?0,?sSorted.size(i)?>?sSorted.get(e),?i++)?{
temp?=?sSorted.get(e);
sSorted.get(e)?=?sSorted.size(i);
sSorted.size(i)?=?temp;
}
你沒看錯,for()里面是,分隔的。
你沒看錯,temp是從石頭縫里蹦出來的。
你沒看錯,List.get(e)是可以對其賦值的。
你沒看錯,List.size(i)是可以傳參數進去的。
還有兩位,直接交白卷放棄了。
其中一位還比較認真,思考了一會兒,說“我不想浪費時間”。
我沒亂用詞,他確實“比較認真”。另一位在我遞過去后,直接看兩眼就遞回來,“排序我不會”,然后看手機去了。
o(╯□╰)o
參-
我自己在紙上寫的時候,花了大概5分鐘去思考細節,再花5分鐘寫出來。(唉……一不小心,又暴露了自己奇慢無比的思維,以及奇慢無比的寫字速度。)這比我此前預計的時間多了好幾倍!
不過,以我給的15~25分鐘,應該不算太難為人……吧?
class?Solution?{
private?static?List
public?static?void?addElement(int?e)?{
int?i;
for?(i?=?0;?i?
if?(e?<=?sSorted.get(i))?{
break;
}
}
sSorted.add(i,?e);
}
}
這是我自己在紙上寫的答案。(如果有興趣,可以停在此處,考慮下這是否是最優算法。)
@Override
public?E?get(int?location)?{
if?(location?>=?0?&&?location?
Link
if?(location?(size?/?2))?{
for?(int?i?=?0;?i?<=?location;?i++)?{
link?=?link.next;
}
}?else?{
for?(int?i?=?size;?i?>?location;?i--)?{
link?=?link.previous;
}
}
return?link.data;
}
throw?new?IndexOutOfBoundsException();
}
這是 java.util.LinkedList在Android(API 23)上的實現,而反編譯Oracle JDK 1.8的實現也大同小異。也就是說,我寫的答案雖然看似簡潔,但其最壞時間復雜度與先插入再排序也沒太大區別,都是O(n2)。
終日打燕,反而被燕啄了眼!(暴露了真實水平。)
我后來又寫了一個參-,算是勉強在臉上摸了些防曬霜。(大家有興趣可以想想為什么這是一個改進。當然,一定還有更好的方案。)
class?Solution?{
private?static?List
public?static?void?addElement(int?e)?{
int?i?=?0;
for?(int?j?:?sSorted)?{
if?(e?<=?j)?{
break;
}
++i;
}
sSorted.add(i,?e);
}
}
(我沒有在提示列表中給出迭代器,結果自己也被晃過去了。)
隱藏的殺手锏
面試官在出題考察應聘者時,應聘者也在通過這道題考察這家公司。
為了避免讓人覺得這家公司考題太簡單、工作內容太無趣、里面的員工(我)水平太低,我還準備了一些后續問題,由淺入深,作為殺手锏。
為什么LinkedList可以賦值給List?
考察多態(polymorphism)。
為什么List
考察泛型(generic)。
為什么List里面是Integer,但放進去和拿出來的都是int?
(此處有坑,其實拿出來的還是Integer。)
考察基本數據類型的自動裝箱、拆箱(auto boxing/unboxing)。
如何在外面有多線程調用時,保證這個唯一的List的正確性?
考察synchronized和volatile。
如何在多線程狀態下的每一個線程,各保持一個獨立的List?
考察ThreadLocal。
(當然,還有一些和Android相關的問題。)
我真心是沒想考算法,所以連算法復雜度的評估都沒打算問。實際情況是,我往往沒有機會問這些問題,因為沒幾個人寫出來。
吐槽與建議
首先,噴一下大學擴招……算了,不扯這么遠了。那兩位放棄做題的,一個是計算機學院的,一個是軟件工程學院的。排序寫不出來,竟然也是能畢業的!
有兩位是某App的開發者。我把他們的App下載下來,發現了一堆bug后,本來想忍忍、就當沒看見、碼農何苦為難碼農,然后手機發熱、卡頓、滅屏后幾乎點亮不了(內存泄露吃光了RAM,導致系統進程沒有內存可用)。過了一陣最終好了,我查看耗電排行,運行10分鐘就高居榜首,耗了17%的電——我嚇得立刻卸載了。一個第三方App能把系統給卡成這樣,一般人還真做不到。
還有兩位是“相關專業”的,非計算機、軟件工程專業,反而表現最佳,雖然還是沒寫出來。
他們無一例外,都是在大學以外,又參加過某些Java、Android培訓的。這些培訓班的水平,可見一斑。問題倒不一定是培訓班的教學質量,而是這種大規模提供人才轉型服務的形式本身——這個世界上,本來就不是誰,都能當一個好碼農,哪怕工作要求只是復制粘貼。
現在,很多碼農都戲稱自己是在“搬磚”、復制粘貼,但實際上程序員的工作不可能僅止于此。使用別人寫好的基本算法,參考別人的實現代碼,只是為了集中精力去解決抽象層次更高的業務問題。
“我們不寫代碼,我們只做代碼的搬運工。”——萬萬不可把這句話當做信條。
還有很多人,在沒有Demo的情況下,無論給多么詳細的API或其它資料,仍然無法寫代碼。他們只能在既有的基礎上,修修補補,無法憑空創作。
我推薦三本Java的基礎書:
《Java編程思想》(Think in Java)
這本是最合適的Java語言入門書。其它很多語法書都是從C/C++的角度來講Java的變化,或者從C++的思路來討論Java怎么用,而這本書的英文名則直接告訴你,請用Java來思考、解決問題。
《Effective Java》
Java中有很多坑,Java中也有很多糖。如果沒有看過這本書,那么不知不覺就會犯很多大忌。
《代碼整潔之道》(Clean Code)
我是在獨立寫一個小項目的時候開始看的。看到一半時項目也寫到一半,頓時連代碼都不會寫了!每天都在寫自己看不下去的代碼,而不知道怎么寫能看得過眼的。加速看完后,重新開始會寫代碼。最終,項目后半部分的代碼,和前半部分完全不同,不像一個人寫的,我后來又重構了一遍。
(為什么我不多推薦點書呢?一來,是我本人也沒看過多少,囧;二來,三本是極限,根據我的經驗,推薦三本可以讓人看一本,推薦三本以上,受眾一本也不會看。)
我有一個朋友,也是一個前同事,好學如好色。他周末都在找一個大學教室看書,甚至有時請年假去教室看書。一本《Java編程思想》,逐行精讀三遍以上。工作經驗不足兩年,跳槽三次,現在在一家百億級上市公司,年薪三十萬,統率十人。
究其原因,無非基礎知識扎實,口水噴死面試官爾。
后記
我作為一個面試別人的初哥,一心只想著自己喜歡的***……呃不,插入排序算法,給別人造成了不必要的麻煩,只能說抱歉了。
和我一起面試的,還有一個負責文檔和規范的妹子(前面提到過的那個)。她也是有一票否決權的,而且用得比我更頻繁!
我最多只給交白卷的,和那個耗電超恐怖的App,這幾個人直接否決。而這個妹子,考察對方的其它綜合能力。
在實際工作中,她是需求的接口人,我們需要和她溝通,實現各方對我們團隊的需求。所以,只要她說一句“我和這個人難以溝通”,那么Boss基本上就直接拒絕了。
下次我還是換一道更簡單的吧,碼農何苦為難碼農。
本文轉載自微信公眾號【java學習之道】。
面試題
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。