0%

网易传媒大数据开发实习生一面面经

1. 面试描述

这次是网易传媒大数据开发实习生的一面, 也是我人生中第一次面试, 很紧张, 自从接到面试通知之后就开始一直在准备.

但是还是紧张. 巨紧张.

  1. 首先还是自我介绍, 这里我就说了下我项目的一些实现,我简历上有两个项目, 第一个是Java + redis + mysql. 讲了下实现原理, 生产者消费者什么的, 然后第二个项目是关于Python数据分析的, 和这次面试关联不大, 所以我就一句话带过, 然后说了下项目里面的创新点, 就是对算法改进避免生成多个小文件, 而是生成大文件和索引文件.

  2. 自我介绍完了之后面试官顺便就问java多线程怎么实现的, 我回答的是线程池和阻塞队列, 然后强调了下是根据逻辑确定的线程池的那几个参数, 而不是用的默认的.

  3. 然后就开始问基础知识了, 先问的java里面的多态, 这个我就简单回答了一下. 父类引用指向子类类型, 执行过程中使用的子类的方法.因为实在不知道要怎么展开来说了

  4. 然后让讲一讲java里面常用的集合, 我是根据接口来讲的, 先讲到了分为2个接口Collection, Map, 然后再讲的Set, List, Queue这些.

  5. 下一个问题就是将Mapreduce流程. 这个我前几天才刚看过, 讲了很长时间. 从读取一个文件开始, 用InputFormat类分区分为InputSplit, 然后也讲到了RecordReader, 也讲了默认的是TextInputFormat. 下面就讲的是继承Mapper类重写map方法, 然后就讲shuffle, 这个shuffle向来是重点, 所以也没什么问题.分为map端的shuffle和reduce端的shuffle. 然后我记得我好像忘了将reduce端shuffle之后处理逻辑了, 就是再次归并, 执行重写的reduce方法了. 可能是因为紧张忘了. 然后最后说到保存到文件用的类OutputFormat, 默认是TextOutputFormat保存为文本文件. 总体上感觉这个题答得还行

  6. 讲一下常用的spark算子. 这个感觉也没什么问题, 我是将算子分为transformation和action算子, 然后transformation算子又分为宽依赖和窄依赖算子. 三部分分别讲了下.

  7. 讲一下数据倾斜的解决方法. (我一听到数据倾斜的时候, 我心里就在想可能要gg了, 因为我之前知道大数据面试有数据倾斜这么个问题, 但是一直没有系统的准备过这个题.) 所以这道题完全就是自我发挥了. 我给了两种办法, 一种是重新分区, 换hash函数. 另外一种是从业务逻辑的角度, 增加几个特征, 使得很多key都相同的几率降低.

  8. 又问到了常用的排序算法, 我分为了两类. 交换排序和非交换排序. 因为紧张我还给说错了(这里我听回放录音才听出来, 我把堆排序放到非交换排序里面了, 本来想说的是基数排序). 然后我又附加的说了外部排序. 当时的想法就是(前天刚复习的外部排序k路归并算法, 看胜者树败者树看了一晚上, 这里不说就亏了)

  9. 问了redis基本数据类型, 我说了8种, 5种基本的, 3种扩展的.

  10. 然后又问了熟不熟linux的操作, 问查看进程, 我回答的ps命令. 一般用的是ps -ef (其实ef具体代表什么我也不太清楚) 然后我讲了一下ps命令一般和grep命令通过管道连用, 查看指定的进程. 然后也回答了对于java程序, 还可以用jps.

  11. 算法题, 链表相加. 给出两个 非空 的链表用来表示两个非负的整数,并且它们的每个节点只能存储 一位 数字。输出这两个数相加的结果

    本来这题真的不难, 在leetcode上我觉得难度连medium都不会达到. 但是最要命的是, 面试官要让我用java自带的LinkedList<>来写这个函数. 这和之前在leetcode上的链表题完全不同!!! 只能用LinkedList就意味着没有next指针可以操作, 就只能用迭代器. 而我之前对迭代器并不熟悉.

    然后想了一会, 想出来了, 然后又碰到了一个问题, 没法用指针, 就没法链表翻转, 当时就在赌, Collections里面有reverse这个方法.

    其实是有的. 但我把函数用错了. Collections.reverse(a)是把a翻转返回void, 我当时以为返回一个翻转后的链表. 还好没运行.

    面试的时候还想到了不反转链表, 因为LinkedList是双向链表, 可以从后向前遍历. 但是我忘了怎么返回最后一个元素的方法了.

    等面试完, 才想到可以用listIterator来这样做. 但是已经没有办法了.

2. 总结

个人觉得前面知识部分答的还可以, 就是数据倾斜没有回答好.

需要改进的地方在算法题上, 因为之前都是在leetcode上刷题, 所以像LinkedList这种数据结构根本用不到, 这才导致了对没有指针的链表操作十分不熟悉, 导致了更加紧张, 写的时候丢三落四(例如这一次面试就忘了两个链表相加之后把最高位的0去掉, 也忘了用listIterator可以直接从后向前开始遍历, 但还好没忘首先判断链表是不是null, 要是这个忘了可能前面答的再好也没用了).

也提醒大家, 不要以为leetcode刷好就没事了. 常用的集合的API也一定要掌握起来.

贴上我漏洞百出的代码吧, 作为警醒希望下次不要犯这么低级的错误

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
LinkedList<Integer> add(LinkedList<Integer> l1, LinkedList<Integer> l2){
if(l1 == null || l2 == null){
throw new NullPointerException();
}
LinkedList<Integer> ans = new LinkedList<>();
LinkedList<Integer> l1Reverse = Collections.reverse(l1); //Collections.reverse(a)是把a翻转返回void
LinkedList<Integer> l2Reverse = Collections.reverse(l2); //不是返回一个翻转后的新的链表
Iterator it1 = l1Reverse.iterator(); // Iterator是泛型接口, 应该用Iterator<Integer>
Iterator it2 = l2Reverse.iterator();
int carry = 0;
int remainder = 0;
while(it1.hasNext() || it2.hasNext()){
int a1 = 0;
int a2 = 0;
if(!it1.hasNext()){
a1 = 0;
a2 = it2.next();
}
else if(!it2.hasNext()){
a1 = it1.next();
a2 = 0;
}
else{
a1 = it1.next();
a2 = it2.next();
}
int sum = a1 + a2 + carry;
remainder = sum % 10;
carry = sum / 10;
ans.add(remainder);
}
if(carry != 0){
ans.add(carry);
}
return Collections.reverse(ans); //这里也忘了去除加法后的前导0了. 太紧张什么都忘了
}