6月18日面试总结
2020-06-18 本文已影响0人
拼搏男孩
1、给一个由0和1组成的二维数组,1代表陆地,0代表海洋,统计出这个二维数组中有多少块陆地,上下左右可以连接的陆地算一块陆地。
比如下面的这个数组就有3块陆地
0 0 0 0 1 0
0 1 0 0 0 0
1 1 1 0 0 0
0 1 0 1 0 0
这道题是leetcode题库中的第200题,一看就知道要使用图的遍历算法,然而图已经忘得一干二净了,直接和面试官说不会。这道题的官方解答在这:https://leetcode-cn.com/problems/number-of-islands/solution/dao-yu-shu-liang-by-leetcode/
2、有N个人从1开始编号,从1开始报数,报数为M的那个人退出,然后从下个人开始继续从1开始报数,请问最后一个人的序号
这里提供一种解法:
public static int test1(){
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
list.add(i);
}
int m = 3;
int index = 0;
while (list.size()>1){
index += m-1;
index = index%list.size();
list.remove(index);
}
return list.get(0);
}
创建一个ArrayList存储元素,index指的是将要删除的元素,由于index可能超出list的长度,所以需要和list.size()进行模运算,让index的值始终小于list.size()/
3、大数据量分页查询的优化
SELECT * FROM student LIMIT 80000,10
单纯使用limit语句进行分页查询的话实际上把所有的数据都查出来了,然后把前面的数据丢弃,十分影响效率。如果主键id是自增的话可以考虑使用索引覆盖。
我们都知道,利用了索引查询的语句中如果只包含了那个索引列(覆盖索引),那么这种情况会查询很快。
因为利用索引查找有优化算法,且数据就在查询索引上面,不用再去找相关的数据地址了,这样节省了很多时间。另外Mysql中也有相关的索引缓存,在并发高的时候利用缓存就效果更好了。
SELECT * FROM student WHERE id >= (SELECT id FROM student LIMIT 80000,1) LIMIT 10
主键索引是聚簇索引,数据存放在节点上,所以可以通过id很快地查询到所有列。
4、线程的三种实现方式以及它们的区别
第一种方式:继承Thread类
package com.qianfeng.thread;
public class Test1 {
public static void main(String[] args) {
Thread t1 = new MyThread();
Thread t2 = new MyThread();
t1.start();
t2.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"启动了");
}
}
第二种方式:实现Runable接口
package com.qianfeng.thread;
public class Test2 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyThread2());
Thread t2 = new Thread(new MyThread2());
t1.start();
t2.start();
}
}
class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"启动了");
}
}
第三种方式:实现Callable接口
package com.qianfeng.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test3 {
public static void main(String[] args) {
MyThread3 thread = new MyThread3();
FutureTask<Integer> futureTask = new FutureTask<>(thread);
new Thread(futureTask).start();
try {
System.out.println("子线程的返回值"+futureTask.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
class MyThread3 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"启动了");
return 1;
}
}
对比:
- 继承Thread类的方式比较简单,直接创建一个子类对象然后start。实现Runable接口的方法需要创建一个实现类的对象然后放在Thread类的构造方法中。实现Callable接口的方法需要创建一个FutureTask类的对象包装Callable接口的实现类,然后传入Thread类的构造方法中。
- 由于Java的单继承机制,继承Thread类后就不能再继承其他类了,所以继承Thread类的方式不够灵活,相反,实现Runable接口与Callable接口的方式比较灵活。
- 继承Thread类多个线程不能共享资源,而实现Runable接口与Callable接口的方式可以在实现类中定义一个静态变量实现资源的共享。
- 继承Thread类与实现Runable接口的方式没有返回值,实现Callable接口的方式可以获得方法执行的返回值。