20210802练习
编辑时间:2021/08/03
读完本节:大概花费15分钟,共1560词
文章目录
1.列举java的常用容器(集合)
- Java Collection包括List、Set和Map,其中常用的List包括ArrayList、LinkedList、Vector;常用的Set包括HashSet、TreeSet、LinkedHashSet;常用的Map包括HashMap、TreeMap、SortMap、LinkedHashMap、ConcurrentHashMap等。
2.List和Set的区别,不可重复是什么意思?
- List可以存放重复的数据,但是Set不能存放可重复的数据;
- List存放元素的顺序是有序的,Set存放的数据是无序的;
- List不能存放null值,Set可以存放null值;
- Set存放的数据不可重复指的是Set底层使用的是Map结构。只不过Set和Map的区别在于Set底层的Entry数组中的value值是null,通过将数据存入key保证不可重复。存入数据的过程中,两个对象通过hashCode()方法比较相等,且两个对象的equals()方法返回值也相等对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象的相等规则。即”相等的对象必须具有相等的散列码
3.HashMap是否线程安全,如果想用线程安全的HashMap怎么做?
- HashMap不是线程安全的集合;
- 如果像使用线程安全的HashMap可以使用Collections类对HashMap进行加锁,或者改用效率更加高的ConcurrentHashMap
4.编写一个单例模式,常见的单例有哪些,分别列举?
public class SingletonClass{
/** 枚举单例 */
public enum Singleton{
INSTANCE;
public Singleton getInstance(){
return INSTANCE;
}
}
}
public class SingletonClass{
/** 饿汉单例 */
public class Singleton{
private Singleton INSTANCE = new Singleton();
private Singleton(){}
public static getInstance(){
return INSTANCE;
}
}
}
public class Singleton {
/*
* 懒汉单例,双重锁
* 利用静态变量来记录Singleton的唯一实例
* volatile 关键字确保:当INSTANCE变量被初始化成Singleton实例时,
* 多个线程正确地处理INSTANCE变量
*/
private volatile static Singleton INSTANCE;
private Singleton(){}
public static Singleton getInstance(){
if(INSTANCE == null){
synchronized(Singleton.class){ //进入同步区域
if(INSTANCE == null){ //在检查一次,如果为null,则创建
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
public class Singleton{
private static volatile Singleton INSTANCE;
private statci Lock lock = new ReentrantLock();
private Singleton(){}
public static Singleton getInstance(){
try{
lock.lock();
if(INSTANCE == null)
INSTANCE = new Singleton();
}catch(){
e.printStaticTrace();
}finally{
lock.unlock();
}
}
}
5.有哪些排序算法,写出快速排序算法的实现
-
快速排序Java实现
public static void quickSort(int[] arr,final int left,final int right) { if (left > right) return; int base = arr[left]; int i = left; int j = right; // 不相遇 while (i != j) { // 从右边开始找比基准数小的就停下,反言之就是比基准数大或者相等就继续 while (arr[j] >= base && i < j) { j--; } while (arr[i] <= base && i < j) { i++; } //左边比base大,右边比base小,停下,直接交换左右指针指向的位置 int t = arr[i]; arr[i] = arr[j]; arr[j] = t; } // i=j 相遇位置的数赋值给基准数这个位置的元素 arr[left] = arr[i]; // 把基准数赋值给相遇位置的数 arr[i] = base; quickSort(arr, left, i - 1); quickSort(arr, i + 1, right); } }
6.给一个二叉树,使用递归和非递归完成先序,中序和后序的遍历
//先序遍历
public void preOrder(TreeNode root) {
if (root == null)
return;
System.out.print(root.val + " ");
preOrder(root.left);
preOrder(root.right);
}
public void preOrder1(TreeNode root) {
Stack<TreeNode> stack=new Stack<>();
TreeNode currNode=root;
if(currNode!=null) {
stack.push(currNode);//根节点入栈
while(!stack.isEmpty()) {
currNode=stack.pop();//弹出根节点
System.out.print(currNode.val+" ");
if(currNode.right!=null) {//右节点先压入栈
stack.push(currNode.right);
}
if(currNode.left!=null) {
stack.push(currNode.left);
}
}
}
}
//中序遍历
public void inOrder(TreeNode root) {
if (root == null)
return;
preOrder(root.left);
System.out.print(root.val + " ");
preOrder(root.right);
}
public void inOrder1(TreeNode root) {
Stack<TreeNode> stack=new Stack<>();
TreeNode currNode=root;
while(currNode!=null||!stack.isEmpty()) {
//先访问左节点,压入左节点
while(currNode!=null) {
stack.push(currNode);
currNode=currNode.left;
}
if(!stack.isEmpty()) {
currNode=stack.pop();//弹出左节点
System.out.print(currNode.val+" ");
currNode=currNode.right;//开始压入右节点
}
}
}
//后序遍历
public void afterOrder(TreeNode root) {
if (root == null)
return;
preOrder(root.left);
preOrder(root.right);
System.out.print(root.val + " ");
}
public void afterOrder1(TreeNode root) {
Stack<TreeNode> stack1=new Stack<>();
Stack<TreeNode> stack2=new Stack<>();
TreeNode currNode=root;
while(currNode!=null||!stack1.isEmpty()) {
while(currNode!=null) {
//使用两个栈在stack2中按照左右顺序入栈
stack1.push(currNode);
stack2.push(currNode);
currNode=currNode.right;
}
if(!stack1.isEmpty()) {
currNode=stack1.pop();//弹出左节点
currNode=currNode.left;
}
}
while(!stack2.isEmpty()) {
currNode=stack2.pop();
System.out.print(currNode.val+" ");
}
}
//层序遍历
public void levelOrder(TreeNode root) {
if (root == null)
return;
Queue<TreeNode> q = new LinkedList<>();// 定义一个队列
q.offer(root);// 存入根节点
while (!q.isEmpty()) {
TreeNode temp = q.poll();// 弹出根节点
System.out.print(temp.val + " ");// 输出根节点
if (temp.left != null) {// 找到根节点下的左右节点并存入队列
q.offer(temp.left);
}
if (temp.right != null) {
q.offer(temp.right);
}
}
}
7.数据库的事务的四大特性及数据库的隔离级别
四大特性:原子性、隔离性、一致性、持久性
隔离级别:
读未提交:在一个事务中,读到了其他事务没有提交的数据。会产生数据脏读现象,不能限制不可重复读和幻读。
读已提交:在一个事务中可以读取到其他事务已经提交的数据变化。可防止脏读现象,无法限制不可重复读和幻读。
可重复读:在一个事务中直到事务结束之前,都可以反复的读到事务刚开始看到的数据,并且一直不会发生变化,有效的防止了脏读和不可重复读。
可序列化:只有等一个事务结束,才能进行下一个事务。
8.TCP的三次握手和四次挥手
9.GET/POST的区别,除了GET/POST还有哪些?
GET和POST发送基于http协议,但是由于服务器的限制,导致两者发送的方式不同
且GET是幂等POST是非幂等可能会修改服务器中的资源请求导致两次结果不同
GET:参数通过URL传输,且会被浏览器主动缓存,传输的非ASCII需要转码
POST:有两个TCP数据包,将参数放置在request body中,缓存需要手动设置,且非ASCII数据通过MIME传输
还有DELETE:删除信息和PUT:添加信息