本公众号(五分钟学大数据)将推出大数据面试系列文章—五分钟小面试,此系列文章将会深入研究各大厂笔面试真题,并根据笔面试题扩展相关的知识点,助力大家都能够成功入职大厂!
大数据笔面试系列文章分为两种类型:混合型(即一篇文章中会有多个框架的知识点—融会贯通);专项型(一篇文章针对某个框架进行深入解析—专项演练)。
此篇文章为系列文章的第一篇(混合型)
第一题:大数据笔试题-Java相关(美菜网)
写出下列程序的输出:
class Father{ static { System.out.println("Static Father"); } { System.out.println("Non-static Father"); } public Father(){ System.out.println("Constructor Father"); } } public class Son extends Father{ static { System.out.println("Static Son"); } { System.out.println("Non-static Son"); } public Son(){ System.out.println("Constructor Son"); } public static void main(String[] args) { System.out.println("First Son"); new Son(); System.out.println("Second Son"); new Son(); } }
运行结果:
Static Father Static Son First Son Non-static Father Constructor Father Non-static Son Constructor Son Second Son Non-static Father Constructor Father Non-static Son Constructor Son
分析:
这道程序题考察的是Java中的静态代码块、构造代码块、构造函数的概念。
- 静态代码块 static {}:
随着类的加载而执行,即JVM加载类后就执行,而且只执行一次,执行优先级高于非静态的初始化块,它会在类初始化的时候执行一次,执行完成便销毁,它仅能初始化类变量,即static修饰的数据成员。
- 非静态代码块,也称构造代码块 {}:
执行的时候如果有静态代码块,先执行静态代码块再执行非静态代码块,在每个对象生成时都会被执行一次,它可以初始化类的实例变量。非静态代码块会在构造函数执行时,在构造函数主体代码执行之前被运行。
- 构造函数:
对象一建立,就会调用与之相应的构造函数,也就是说,不建立对象,构造函数是不会运行的。
一个对象建立,构造函数只运行一次,而一般方法可以被该对象调用多次。
再来看本题,程序运行,执行main()方法会先加载main()方法所在的类,加载 Son 类,但是 Son 类继承自 Father 类,所以先加载父类,同时父类的静态代码块执行,然后加载 Son 类本身,Son 类自己的静态代码块开始执行,类加载完成之后执行main()方法内部的语句,打印 First Son,然后 new Son(),在创建对象时先构造父类的对象,因为静态代码块只在类加载时执行一次,所以不再执行,然后执行父类的构造代码块,构造函数,构造代码块的优先级大于构造函数。然后在执行 Son 对象本身。完成之后打印 Second Son,接着又 new Son(),重复以上步骤。构造代码块和构造函数在每次new的时候都会执行一次。
第二题:大数据面试题-JVM相关(丰巢科技)
问:解释内存中的栈(stack)、堆(heap)和静态存储区的用法?
答:通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;而通过new关键字和构造器创建的对象放在堆空间;程序中的字面量(literal)如直接书写的100、“hello”和常量都是放在静态存储区中。栈空间操作最快但是也很小,通常大量的对象都是放在堆空间,整个内存包括硬盘上的虚拟内存都可以被当成堆空间来使用。
String str = new String(“hello”);
上面的语句中str放在栈上,用new创建出来的字符串对象放在堆上,而“hello”这个字面量放在静态存储区。
补充:较新版本的Java中使用了一项叫“逃逸分析“的技术,可以将一些局部对象放在栈上以提升对象的操作性能。(在 Java SE 6u23+ 开始支持,并默认设置为启用状态,可以不用额外加这个参数。)
第三题:大数据面试题-海量数据相关(腾讯)
问:给 40 亿个不重复的 unsigned int 的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那 40 亿个数当中?
答:方案 1:申请 512M 的内存,512M是42亿多 bit,一个 bit 位代表一个 unsigned int 值。读入 40 亿个数,设置相应的 bit 位,读入要查询的数,查看相应 bit 位是否为 1,为 1 表示存在,为 0 表示不存在。
方案 2:这个问题在《编程珠玑》里有很好的描述,大家可以参考下面的思路,探讨一下: 因为 2^32 为 42 亿多,所以给定一个数可能在,也可能不在其中; 这里我们把 40 亿个数中的每一个用 32 位的二进制来表示 ,假设这 40 亿个数开始放在一个文件中。
然后将这 40 亿个数分成两类:
- 最高位为 0
- 最高位为 1
并将这两类分别写入到两个文件中,其中一个文件中数的个数<=20 亿,而另一个>=20 亿(相当于折半); 与要查找的数的最高位比较并接着进入相应的文件再查找
然后再把这个文件为又分成两类:
- 次最高位为 0
- 次最高位为 1
并将这两类分别写入到两个文件中,其中一个文件中数的个数<=10 亿,而另一个>=10 亿(相当于折半); 与要查找的数的次最高位比较并接着进入相应的文件再查找。
.....
以此类推,就可以找到了,而且时间复杂度为 O(logn)。
第四题:大数据面试题-Hadoop相关(阿里)
问:MapReduce 中排序发生在哪几个阶段?这些排序是否可以避免?为什么?
答:
- 一个 MapReduce 作业由 Map 阶段和 Reduce 阶段两部分组成,这两阶段会对数据排序,从这个意义上说,MapReduce 框架本质就是一个 Distributed Sort。
- 在 Map 阶段,Map Task 会在本地磁盘输出一个按照 key 排序(采用的是快速排序)的文件(中间可能产生多个文件,但最终会合并成一个),在 Reduce 阶段,每个 Reduce Task 会对收到的数据排序(采用的是归并排序),这样,数据便按照 Key 分成了若干组,之后以组为单位交给 reduce 处理。
- 很多人的误解在 Map 阶段,如果不使用 Combiner 便不会排序,这是错误的,不管你用不用 Combiner,Map Task 均会对产生的数据排序(如果没有 Reduce Task,则不会排序,实际上 Map 阶段的排序就是为了减轻 Reduce端排序负载)。
- 由于这些排序是 MapReduce 自动完成的,用户无法控制,因此,在hadoop 1.x 中无法避免,也不可以关闭,但 hadoop2.x 是可以关闭的。
第五题:大数据面试题-Kafka相关(商汤科技)
问:kafka数据分区和消费者的关系,kafka的数据offset读取流程,kafka内部如何保证顺序
答:
- kafka数据分区和消费者的关系:1个partition只能被同组的⼀一个consumer消费,同组的consumer则起到均衡效果
- kafka的数据offset读取流程:
- 连接ZK集群,从ZK中拿到对应topic的partition信息和partition的Leader的相关信息
- 连接到对应Leader对应的broker
- consumer将⾃自⼰己保存的offset发送给Leader
- Leader根据offset等信息定位到segment(索引⽂文件和⽇日志⽂文件)
- 根据索引⽂文件中的内容,定位到⽇日志⽂文件中该偏移量量对应的开始位置读取相应⻓长度的数据并返回给consumer
3.kafka内部如何保证顺序:kafka只能保证partition内是有序的,但是partition间的有 序是没办法的。爱奇艺的搜索架构,是从业务上把需要有序的打到同一个partition。