面试提问的面经

见:https://zhuanlan.zhihu.com/p/68255395
#转发和重定向

转发过程:客户浏览器发送http请求----》web服务器接受此请求–》调用内部的一个方法在容器内部完成请求处理和转发动作----》将目标资源发送给客户;在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。在客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。

重定向过程:客户浏览器发送http请求----》web服务器接受后发送302状态码响应及对应新的location给客户浏览器–》客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址----》服务器根据此请求寻找资源并发送给客户。在这里location可以重定向到任意URL,既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向的路径,客户可以观察到地址的变化的。重定向行为是浏览器做了至少两次的访问请求的。

在这里插入图片描述
重定向,其实是两次request,
第一次,客户端request A,服务器响应,并response回来,告诉浏览器,你应该去B。这个时候IE可以看到地址变了,而且历史的回退按钮也亮了。重定向可以访问自己web应用以外的资源。在重定向的过程中,传输的信息会被丢失。

#有哪些线程池
详细见:https://blog.csdn.net/jiangpingjiangping/article/details/77860236
线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。
java中的有哪些线程池?

1.newCachedThreadPool创建一个可缓存线程池程

2.newFixedThreadPool 创建一个定长线程池

3.newScheduledThreadPool 创建一个周期性执行任务的线程池

4.newSingleThreadExecutor 创建一个单线程化的线程池

newFixedThreadPool
创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

newSingleThreadExecutor
创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

newScheduleThreadPool
创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。

newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

这种类型的线程池特点是:

工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。
在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。

where a= ? group by b= ?

走了几个索引?
如果经常需要同时对两个字段进行AND查询,那么使用两个单独索引不如建立一个复合索引,因为两个单独索引通常数据库只能使用其中一个,而使用复合索引因为索引本身就对应到两个字段上的,效率会有很大提高。

但是,往往都没有说为什么?想知道以下问题:
1、是不是在任何情况下数据库查询一次只会使用到一个索引?
2、如果不是,那么什么情况下只会使用一个索引?
3、那分别是什么造成上面的查询索引使用问题呢?

与其说是“数据库查询只能用到一个索引”,倒不是说是 和全表扫描/只使用一个索引的速度比起来,去分析两个索引二叉树更加耗费时间,所以绝大多数情况下数据库都是是用一个索引。

我们来想象一下当数据库有N个索引并且查询中分别都要用上他们的情况:

查询优化器(用大白话说就是生成执行计划的那个东西)需要进行N次主二叉树查找[这里主二叉树的意思是最外层的索引节点],此处的查找流程大概如下:
查出第一条column1主二叉树等于1的值,然后去第二条column2主二叉树查出foo的值并且当前行的coumn1必须等于1,最后去column主二叉树查找bar的值并且column1必须等于1和column2必须等于foo。
如果这样的流程被查询优化器执行一遍,就算不死也半条命了,查询优化器可等不及把以上计划都执行一遍,贪婪算法(最近邻居算法)可不允许这种情况的发生,所以当遇到以下语句的时候,数据库只要用到第一个筛选列的索引(column1),就会直接去进行表扫描了。

select count(1) from table1 where column1 = 1 and column2 = ‘foo’ and
column3 = ‘bar’

所以与其说是数据库只支持一条查询语句只使用一个索引,倒不如说N条独立索引同时在一条语句使用的消耗比只使用一个索引还要慢。
所以如上条的情况,最佳推荐是使用index(column1,column2,column3)

这种联合索引,此联合索引可以把b+tree结构的优势发挥得淋漓尽致:
一条主二叉树(column=1),查询到column=1节点后基于当前节点进行二级二叉树column2=foo的查询,在二级二叉树查询到column2=foo后,去三级二叉树column3=bar查找。

mysql的执行顺序
见:https://blog.csdn.net/u014044812/article/details/51004754

1、SELECT语句定义
一个完成的SELECT语句包含可选的几个子句。SELECT语句的定义如下:
SQL代码

<SELECT clause> [<FROM clause>] [<WHERE clause>] [<GROUP BY clause>] [<HAVING clause>] [<ORDER BY clause>] [<LIMIT clause>] 

SELECT子句是必选的,其它子句如WHERE子句、GROUP BY子句等是可选的。
一个SELECT语句中,子句的顺序是固定的。例如GROUP BY子句不会位于WHERE子句的前面。

2、SELECT语句执行顺序
SELECT语句中子句的执行顺序与SELECT语句中子句的输入顺序是不一样的,所以并不是从SELECT子句开始执行的,而是按照下面的顺序执行:

开始->FROM子句->WHERE子句->GROUP BY子句->HAVING子句->ORDER BY子句->SELECT子句->LIMIT子句->最终结果 

每个子句执行后都会产生一个中间结果,供接下来的子句使用,如果不存在某个子句,就跳过
对比了一下,mysql和sql执行顺序基本是一样的, 标准顺序的 SQL 语句为:

select 考生姓名, max(总成绩) as max总成绩 
 
from tb_Grade 
 
where 考生姓名 is not null 
 
group by 考生姓名 
 
having max(总成绩) > 600 
 
order by max总成绩 

笛卡尔积:
在这里插入图片描述

内连接 外连接

见:https://blog.csdn.net/plg17/article/details/78758593
在这里插入图片描述
一、内连接
关键字:inner join on
语句:select * from a_table a inner join b_table b on a.a_id = b.b_id;
执行结果:
在这里插入图片描述
说明:组合两个表中的记录,返回关联字段相符的记录,也就是返回两个表的交集(阴影)部分。

在这里插入图片描述
二、左连接(左外连接)
关键字:left join on / left outer join on
语句:select * from a_table a left join b_table b on a.a_id = b.b_id;
执行结果:
在这里插入图片描述
说明:
left join 是left outer join的简写,它的全称是左外连接,是外连接中的一种。
左(外)连接,左表(a_table)的记录将会全部表示出来,而右表(b_table)只会显示符合搜索条件的记录。右表记录不足的地方均为NULL。
在这里插入图片描述
三、右连接(右外连接)
关键字:right join on / right outer join on
语句:select * from a_table a right outer join b_table b on a.a_id = b.b_id;
执行结果:

在这里插入图片描述

说明:
right join是right outer join的简写,它的全称是右外连接,是外连接中的一种。
与左(外)连接相反,右(外)连接,左表(a_table)只会显示符合搜索条件的记录,而右表(b_table)的记录将会全部表示出来。左表记录不足的地方均为NULL。
在这里插入图片描述

父线程怎么获得子线程的返回值

获取Java线程返回值的几种方式
在实际开发过程中,我们有时候会遇到主线程调用子线程,要等待子线程返回的结果来进行下一步动作的业务。

那么怎么获取子线程返回的值呢,我这里总结了三种方式:

主线程等待。
== Join方法等待。==
==实现Callable接口。 ==

Entity类

package com.basic.thread;
public class Entity {
   	  private String name;
      public String getName() {
          return name;
      }
      public void setName(String name) {
          this.name = name;
      }
  }

主线程等待

 1 public static void main(String[] args) throws InterruptedException {
 2         Entity entity = new Entity();
 3         Thread thread = new Thread(new MyRunnable(entity));
 4         thread.start();
 5         // 获取子线程的返回值:主线程等待法
 6         while (entity.getName() == null){
 7             Thread.sleep(1000);
 8         }
 9         System.out.println(entity.getName());
10     }

Join方法阻塞当前线程以等待子线程执行完毕

1 public static void main(String[] args) throws InterruptedException {
2         Entity entity = new Entity();
3         Thread thread = new Thread(new MyRunnable(entity));
4         thread.start();
5         // 获取子线程的返回值:Thread的join方法来阻塞主线程,直到子线程返回
6         thread.join();
7         System.out.println(entity.getName());
8     }

通过实现Callable接口
这里又分为两种情况,通过FutureTask或线程池。

  1. FutureTask
1 @SuppressWarnings("all")
2     public static void main(String[] args) throws ExecutionException, InterruptedException {
3         FutureTask futureTask = new FutureTask(new MyCallable());
4         Thread thread = new Thread(futureTask);
5         thread.start();
6         if(!futureTask.isDone())
7             System.out.println("task has not finished!");
8         System.out.println(futureTask.get());
9     }

线程池

1 @SuppressWarnings("all")
2     public static void main(String[] args) throws ExecutionException, InterruptedException {
3         ExecutorService executorService = Executors.newCachedThreadPool();
4         Future future = executorService.submit(new MyCallable());
5         if(!future.isDone())
6             System.out.println("task has not finished!");
7         System.out.println(future.get());
8     }

Java开发的框架和技术Spring

详见:https://www.cnblogs.com/kaleidoscope/p/9630131.html
Spring MVC
Spring Boot
Spring Cloud
Hibernate

Spring主要是基于IOC反转Beans管理Bean类,主要依存于SSH框架(Struts+Spring+Hibernate)这个MVC框架,所以定位很明确,

Struts主要负责表示层的显示,
Spring利用它的IOC和AOP来处理控制业务(负责对数据库的操作),
Hibernate主要作用是数据的持久化到数据库。
SpringMVC是基于Spring的一个MVC框架,用以替代初期的SSH框架;(Spring Framework本身没有Web功能,Spring MVC使用WebApplicationContext类扩展ApplicationContext,使得拥有web功能)。
Spring Boot是基于Spring4的条件注册的一套快速开发整合包。

三者的发展与联系:

Spring 最初利用“工厂模式”( DI )和“代理模式”( AOP )解耦应用组件。大家觉得挺好用,于是按照这种模式搞了一个 MVC 框架(一些用 Spring 解耦的组件),用开发 web 应用( SpringMVC )。然后有发现每次开发都要搞很多依赖,写很多样板代码很麻烦,于是搞了一些懒人整合包( starter ),这套就是 Spring Boot 。

概念解读

  • 什么是Spring
    一个轻量级的控制反转(IoC)和面向切面(AOP)的容器
  • 什么是SpringMVC
    spring与mvc可以更好地解释什么是springMvc,MVC为现代web项目开发的一种很常见的模式,简言之C(控制器)将V(视图、用户客户端)与M(模块,业务)分开构成了MVC ,业内常见的mvc模式的开发框架有Struts1,Struts2等。spring作为专业的开发web项目的开源框架,springMvc为内部的一个模块环节,同样采取mvc设计模式。
  • 什么是SpringBoot
    Spring boot 是 Spring 的一套快速配置脚手架,可以基于spring boot 快速开发单个微服务,特点:简单易用,初学者和大牛都可以轻松上手,其中的注解会给使用者提供方便;
    Spring boot对第三方技术进行了很好的封装和整合,提供了大量第三方接口;
    可以通过依赖自动配置,不需要XML等配置文件

深入解读Spring Boot与Spring Boot的概念与关系

Spring Boot简化了基于 Spring的应用开发,通过少量的代码就能创建一个独立的、产品级別的 Spring应用。 Spring Boot为 Spring平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。多数 SpringBoot应用只需要很少的 Spring配置。

Spring Boot是由 Pivotal团队提供的全新框架,其设计目的是用来简化新 Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。用我的话来理解,就是Spring Boot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像 maven整合了所有的jar包,Spring Boot整合了所有的框架。

Spring Boot的核心思想就是约定大于配置,一切自动完成。采用 Spring Boot可以大大的简化你的开发模式,所有你想集成的常用框架,它都有对应的组件支持。

什么是Spring Cloud

  • Spring Cloud是一系列框架的有序集合。它利用 Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衠、断路器、数据监控等,都可以用 Spring Boot的开发风格做到一键启动和部署。 Spring并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组台起来,通过 Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系統开发工具包。
  • 微服务是可以独立部署、水平扩展、独立访问(或者有独立的数据库)的服务单元, Spring Cloud就是这些微服务的大管家,采用了微服务这种架构之后,项目的数量会非常多, Spring Cloud做为大管家就需要提供各种方案来维护整个生态。
  • Spring Cloud就是一套分布式服务治理的框架,既然它是一套服务治理的框架,那么它本身不会提供具体功能性的操作,更专注于服务之间的通讯、熔断、监控等。因此就需要很多的组件来支持一套功能。

Spring Boot和Spring Cloud的关系

Spring Boot和Spring Cloud的区别
Spring boot是Spring的一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务;Spring Cloud是一个基于Spring Boot实现的云应用开发工具
Spring Boot专注于快速、方便集成的单个个体,Spring Cloud是关注全局的服务治理框架;
Spring boot使用了默认约定大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现。
Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。

java中的自动装箱和自动拆箱

自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱
因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱原始类型

byte,short,char,int,long,float,double和boolean
对应的封装类为Byte,Short,Character,Integer,Long,Float,Double,Boolean。

1 //自动装箱
2 Integer total = 99;
4 //自动拆箱
5 int totalprim = total;

简单一点说,装箱就是自动将基本数据类型转换为包装器类型拆箱就是自动将包装器类型转换为基本数据类型

java的不可变类型

可变类和不可变类(Mutable and Immutable Objects)的初步定义:
可变类:当你获得这个类的一个实例引用时,你可以改变这个实例的内容。
不可变类:当你获得这个类的一个实例引用时,你不可以改变这个实例的内容。不可变类的实例一但创建,其内在成员变量的值就不能被修改。

如何创建一个自己的不可变类:

  • 所有成员都是private
  • 不提供对成员的改变方法,例如:setXXXX
  • 确保所有的方法不会被重载。手段有两种:使用final Class(强不可变类),或者将所有类方法加上final(弱不可变类)。
  • 如果某一个类成员不是原始变量(primitive)或者不可变类,必须通过在成员初始化(in)或者get方法(out)时通过深度clone方法,来确保类的不可变。

在 Java 中 String, BigInteger, BigDecimal 就是这样的类。

有一些很好的理由来说明为什么需要创建不可变类:

线程安全,不需要考虑同步问题
更加安全,能够被自由共享
更不容易出错
设计简单,使用方便

java的运行期异常和非运行期异常

异常的概念
Throwable: 有两个重要的子类:Exception(异常)和 Error(错误),二者都是 Java 异常处理的重要子类,各自都包含大量子类。

Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。

Exception(异常):是程序本身可以处理的异常。

Exception可以分为checked exceptions(非运行时异常)和unchecked exceptions(运行时异常):

  1. Unchecked Exception
    unchecked exceptions在编译时不进行检查。继承自RuntimeException的类,便是unchecked exceptions;

例如:

NullPointerException(空指针异常)IndexOutOfBoundsException(下标越界异常)
ClassCastException(类转换异常)
ArrayStoreException(数据存储异常,操作数组时类型不一致)
IO操作的BufferOverflowException异常
  1. CheckedExceptions
    在编译时进行检查的异常称为checked exceptions,也称编译异常。继承Exception的类,除了UncheckedException以外都是checked exceptions;从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。

常见异常:

FileNotFoundException
ParseException
ClassNotFoundException
CloneNotSupportedException
InstantiationException
InterruptedException
NoSuchMethodException
NoSuchFieldException

见:https://blog.csdn.net/qq_25223941/article/details/109648479

对集合对象进行排序是如何实现的

java集合的工具类Collections中提供了两种排序的方法,分别是:

Collections.sort(List list)
Collections.sort(List list,Comparator c)

第一种称为自然排序,参与排序的对象需实现comparable接口,重写其compareTo()方法,方法体中实现对象的比较大小规则,示例如下:
实体类:(基本属性,getter/setter方法,有参无参构造方法,toString方法)
在这里插入图片描述
在这里插入图片描述
第二种叫定制排序,或自定义排序,需编写匿名内部类,先new一个Comparator接口的比较器对象c,同时实现compare()其方法;
然后将比较器对象c传给Collections.sort()方法的参数列表中,实现排序功能;
在这里插入图片描述

java本身排序的方法和工具类

目前java工具类排序分为两种:

数组排序
集合工具类排序
首先要知道两个类:java.util.Arrays和java.util.Collections。我们使用Arrays对数组进行排序,使用 Collections对结合框架容器进行排序(集合(list / set)),如ArraysList,LinkedList等
1.java.util.Arrays类

提供了各种对象的排序:char[],byte[],long[],int[]和Object[],注Arrays.sort方法排序返回的结果是升序Ascending的排列顺序。你可以定制排序顺序。这个排序和TreeSet的红黑树排序方式不同,Set不允许有重复数据存在,因此,当有重复数据时,可以使用这个工具类进行排序。Arrays提供的排序算法是归并排序算法(当元素数量小于=7时采用的是插入排序),空间复杂度O(n)。

2.java.util.Collections类

该工具类提供了大量方法对集合元素进行排序、查询和修改等操作,还提供了将集合对象设置为不可变、对集合对象实现同步控制等方法。
见:https://blog.csdn.net/qiyeliuli/article/details/51446566

   Collections.sort(Arrays.asList(strs1),Collections.reverseOrder());

java覆写,隐藏,重载

覆盖/重写/覆写(一个意思!)(Overriding/Overwrite)
重写 重载前面强调过很多次。

这里主要是隐藏 主要是在重写的基础上但作用的是静态方法,

隐藏:父类和子类拥有相同名字的属性或者方法时,父类的同名的属性或者方法形式上不见了,实际是还是存在的
注意:当发生隐藏的时候,声明类型是什么类,就调用对应类的属性或者方法,而不会发生动态绑定
方法隐藏只有一种形式,就是父类和子类存在相同的静态方法 属性只能被隐藏,不能被覆盖
子类实例变量/静态变量可以隐藏父类的实例/静态变量,总结为变量可以交叉隐藏

在这里插入图片描述
对于重写,属性静态绑定,方法动态绑定。

使用递归注意哪些东西?使用场景,什么时候可以用,什么时候不可以用,递归主要消耗什么资源

什么情况下用递归?
递归的特点,可以看出递归可以大大缩短程序的代码,有意识的使用递归,可以用较短的代码解决一些复杂的问题。甚至有些问题非得使用递归解决不可。那么什么时候我们该使用递归呢?
递归算法的基本思想是:把规模大的、较难解决的问题变成规模较小的、易解决的同一问题。规模较小的问题又变成规模更小的问题,并且小到一定程度可以直接得出它的解,从而得到原来问题的解。
比如阶乘,也就是说求n可以先求n-1,以此类推,到1,这类问题都可以用递归解决,菲波拉锲数也可以递归。因为递归是总是调用自身解决问题,所以,必须有结束条件,否则会出问题,导致内存卡爆

递归算法:

优点:代码简洁、清晰,并且容易验证正确性。

缺点:它的运行需要较多次数的函数调用,如果调用层数比较深,每次都要创建新的变量,需要增加额外的堆栈处理,会对执行效率有一定影响,占用过多的内存资源。

函数调用 就是 压栈 退栈操作
先将调用函数的地址压栈
分配被调用函数 局部变量分配 形参 返回值 都要分配 调用后一层一层返回
递归自己调用自己多少次 就要分配多少次 局部变量分配 形参 返回值

多核CPU

转:https://zhidao.baidu.com/question/1823797955212689748.html
多核CPU是指一个CPU内有多个运算核心,比如四核心CPU,可以并行运算四个线程的任务。而这些核心都集成在一个CPU中,因此彼此之间的通信很快。
多个CPU是指许多个CPU个体,一般市面上只有最多同时安装两颗CPU的主板,由于两颗CPU需要主板的线路进行交互,所以1+1的性能肯定是小于2的。
比如一颗四核CPU和两颗双核CPU,假定他们除了核心数量不同外其余参数相同,那么一颗四核CPU的性能是好于两颗双核CPU并行的。

哈希碰撞

转:https://blog.csdn.net/kjfcpua/article/details/44238757

如果两个输入串的hash函数的值一样,则称这两个串是一个碰撞(Collision)。既然是把任意长度的字符串变成固定长度的字符串,所以必有一个输出串对应无穷多个输入串,碰撞是必然存在的。

一个优良的hash函数 f 应当满足以下三个条件:

(1)对于任意y,寻找x,使得f(x)=y,在计算上是不可行的。

(2)给定x1∈A,找x2∈B,,使得f(x1)=f(x2),在计算上是不可能的,这也就是弱无碰撞性。

(3)寻找x1,x2,使得f(x1)=f(x2),在计算上也是不可行的,这也就是强无碰撞性。

这样就称为安全保密的Hash函数,除了枚举外不可能有别的更快的方法。如第3条,根据生日定理,要想找到这样的x1,x2,理论上需要大约2^(n/2)的枚举次数。

因为前两条都能被破坏的hash函数太弱而被抛弃,几乎所有的hash函数的破解,都是指的破坏上面的第3条性质,即找到一个碰撞。在密码学上还有一个概念是理论破解,指的是提出一个算法,使得可以用低于理论值得枚举次数找到碰撞。

4、碰撞处理

通常有两类方法处理碰撞:开放寻址(Open Addressing)法和链接(Chaining)法。前者是将所有结点均存放在散列表T[0…m-1]中;后者通常是把散列到同一槽中的所有元素放在一个链表中,而将此链表的头指针放在散列表T[0…m-1]中。

(1)开放寻址法

所有的元素都在散列表中,每一个表项或包含动态集合的一个元素,或包含NIL。这种方法中散列表可能被填满,以致于不能插入任何新的元素。在开放寻址法中,当要插入一个元素时,可以连续地检查或探测散列表的各项,直到有一个空槽来放置待插入的关键字为止。有三种技术用于开放寻址法:线性探测、二次探测以及双重探测。

<1>线性探测

给定一个普通的散列函数h’:U —>{0,1,…,m-1},线性探测方法采用的散列函数为:h(k,i) = (h’(k)+i)mod m,i=0,1,…,m-1

 探测时从i=0开始,首先探查T[h'(k)],然后依次探测T[h'(k)+1],…,直到T[h'(k)+m-1],此后又循环到T[0],T[1],…,直到探测到T[h'(k)-1]为止。探测过程终止于三种情况: 

(1)若当前探测的单元为空,则表示查找失败(若是插入则将key写入其中);
  (2)若当前探测的单元中含有key,则查找成功,但对于插入意味着失败;
  (3)若探测到T[h’(k)-1]时仍未发现空单元也未找到key,则无论是查找还是插入均意味着失败(此时表满)。

线性探测方法较容易实现,但是存在一次群集问题,即连续被占用的槽的序列变的越来越长。采用例子进行说明线性探测过程,已知一组关键字为(26,36,41,38,44,15,68,12,6,51),用除余法构造散列函数,初始情况如下图所示:
在这里插入图片描述
散列过程如下图所示:
在这里插入图片描述

<2>二次探测

二次探测法的探查序列是:h(k,i) =(h’(k)+i*i)%m ,0≤i≤m-1 。初次的探测位置为T[h’(k)],后序的探测位置在次基础上加一个偏移量,该偏移量以二次的方式依赖于i。该方法的缺陷是不易探查到整个散列空间。

<3>双重散列

该方法是开放寻址的最好方法之一,因为其产生的排列具有随机选择的排列的许多特性。采用的散列函数为:h(k,i)=(h1(k)+ih2(k)) mod m。其中h1和h2为辅助散列函数。初始探测位置为T[h1(k)],后续的探测位置在此基础上加上偏移量h2(k)模m。

(2)链接法

将所有关键字为同义词的结点链接在同一个链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组T[0…m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于1,但一般均取α≤1。

举例说明链接法的执行过程,设有一组关键字为(26,36,41,38,44,15,68,12,6,51),用除余法构造散列函数,初始情况如下图所示:
在这里插入图片描述

最终结果如下图所示:
在这里插入图片描述

b树和b+树的区别

转:https://blog.csdn.net/login_sonata/article/details/75268075

b树(balance tree)和b+树应用在数据库索引,可以认为是m叉的多路平衡查找树,但是从理论上讲,二叉树查找速度和比较次数都是最小的,为什么不用二叉树呢?
因为我们要考虑磁盘IO的影响,它相对于内存来说是很慢的。数据库索引是存储在磁盘上的,当数据量大时,就不能把整个索引全部加载到内存了,只能逐一加载每一个磁盘页(对应索引树的节点)。所以我们要减少IO次数,对于树来说,IO次数就是树的高度,而“矮胖”就是b树的特征之一,它的每个节点最多包含m个孩子,m称为b树的阶,m的大小取决于磁盘页的大小。

实用索引不出现Using filesort

转:https://blog.csdn.net/nrsc272420199/article/details/104698404
不会出现 Using filesort的情况 — 符合最佳左前缀法则
order by也满足索引最佳左前缀法则的时候

order by a 
— order by a, b
— order by a,b,c 
— order by a desc,b desc ,c desc
explain select age, birth,name from tbl where age >10 order by age;
explain select age, birth,name from tbl where age >10 order by age,birth;
explain select age, birth,name from tbl where age >10 order by age,birth,name;
explain select age, birth,name from tbl where age >10 order by age desc,birth desc,name desc;

在这里插入图片描述
假如我将查询的内容改为 * ,我们再来看一下其执行计划:
在这里插入图片描述
在进行 大于 查询时,mysql会找到你的目标条件在索引树上的最小值,假如你指定的值,比索引树上的最小值还小,那肯定就是要查询所有的数据了,那mysql就懒得再去遍历你目标条件所在的索引树了,而是直接通过聚簇索引搜索出所有数据。
在这里插入图片描述

sql执行顺序

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值