面试题记录

redis基本数据类型:

String(字符串)

Hash(哈希)

List(列表)

Set(集合)

zset(sorted set:有序集合)



springcloud核心组件:

服务发现——Netflix Eureka

客服端负载均衡——Netflix Ribbon

断路器——Netflix Hystrix

服务网关——Netflix Zuul

分布式配置——Spring Cloud Config



两阶段提交(2PC)
两阶段提交(Two-phase Commit,2PC),通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。

  1. 运行过程
    1.1 准备阶段
    协调者询问参与者事务是否执行成功,参与者发回事务执行结果。
    1.2 提交阶段
    如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。
    需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。



存在的问题
2.1 同步阻塞 所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
2.2 单点问题 协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。
2.3 数据不一致 在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
2.4 太过保守 任意一个节点失败就会导致整个事务失败,没有完善的容错机制。




springboot自动装配原理:

@SpringBootApplication是一个复合注解或派生注解,在@SpringBootApplication中有一个注解@EnableAutoConfiguration,翻译成人话就是开启自动配置,其定义如下:

这个注解也是一个派生注解,其中的关键功能由@Import提供,其导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()

扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。

这个spring.factories文件也是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,

这些类名以逗号分隔。

这个@EnableAutoConfiguration注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在SpringApplication.run(…)的内部就会执行selectImports()方法,

找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。




springboot常用注解

@SpringBootApplication:核心的注解

@EnableAutoConfiguration:自动配置注解

@Configuration:定义配置类,指出该类是 Bean 配置的信息源,相当于传统的xml配置文件

@ComponentScan:组件扫描。让spring Boot扫描到Configuration类并把它加入到程序上下文

@Repository:用于标注数据访问组件,即DAO组件。

@Service:一般用于修饰service层的组件

@RestController:用于标注控制层组件(如struts中的action),表示这是个控制器bean

@ResponseBody:表示该方法的返回结果直接写入HTTP response body中

@Bean:产生一个bean,并交给spring管理

@AutoWired:byType方式。把配置好的Bean拿来用,完成属性、方法的组装

@RequestMapping:处理请求地址映射的注解

@RequestParam:用在方法的参数前面




springboot注入的两种方式有什么区别

@Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上

@Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在

@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,

如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。




线程的创建方式:

继承Thread类创建线程,重写run方法

实现Runnable接口创建线程

使用Callable和Future创建线程

使用线程池例如用Executor框架




用Runnable还是Thread

如果你要继承其他类,调用Runnable接口好了




一、缓存击穿
缓存中没有数据,数据库有数据,高并发同时冲向数据库,DB压力增大
引发击穿的原因:
第一次访问
恶意访问不存在的key
Key过期
合理的规避方案:
服务器启动时, 提前写入
规范key的命名, 通过中间件拦截
加互斥锁
对某些高频访问的Key,设置合理的TTL或永不过期
二、缓存穿透
缓存中没有数据,数据库也没有数据,恶意攻击压垮数据库
合理的规避方案:
布隆过滤器:将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力
缓存空对象:当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源
三、缓存雪崩
缓存中大量数据集中过期,高并发请求冲向数据库,使数据库宕机
1.缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
2.如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
3.设置热点数据永远不过期。




hashMap的实现方式

HashMap 将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对。

因此,当需要存储一个 Entry 对象时,会根据 hash 算法来决定其在数组中的存储位置,再根据equals() 方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据hash算法找到其在数组中的存储位置,再根据 equals() 方法从该位置上的链表中取出该Entry。




HashMap和Hashtable的区别

HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。

HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。

HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。

由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。

HashMap不能保证随着时间的推移Map中的元素次序是不变的。




HashSet和HashMap的区别

HashMap HashSet

HashMap实现了Map接口 HashSet实现了Set接口

HashMap储存键值对 HashSet仅仅存储对象

使用put()方法将元素放入map中 使用add()方法将元素放入set中

HashMap中使用键对象来计算hashcode值 HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false

HashMap比较快,因为是使用唯一的键来获取对象 HashSet较HashMap来说比较慢

vv
基本数据类型

byte

short

int

long

float

double

boole

char




基本数据类型和封装类型的区别

byte	基本数据类型 对应 Byte		封装类  -128  127

short 	基本数据类型 对应 Short		封装类

int		基本数据类型 对应 Interger	封装类  需要new 默认值是null

long	基本数据类型 对应 Long		封装类

float	基本数据类型 对应 Float		封装类

double	基本数据类型 对应 Double		封装类

boolean	基本数据类型 对应 Boolean	封装类

char	基本数据类型 对应 Character	封装类

String是一个单独的封装类

封装类是一个类,可以调用类中的方法,比如tostring等,基本数据类型就不行




关系型数据库四大特性

原子性:全成功或者全失败

一致性:执行之前的数据和执行之后得数据必须是一致的

隔离性:多个事务不能被其他事务的操作所干扰,多个并发事务之间要相互隔离

持久性:每一次的事务提交后就会保证不会丢失




mysql调优

尽量全值匹配

不要在索引列上做任何操作

范围条件放在最后

尽量用覆盖索引

不等于(!= 与 <>) 要少用

Null与Not null 对性能有影响

注意like查询条件

字符类型一定要加引号




mysql连接方式

笛卡尔积:两表关联,把左表的列和右表的列通过笛卡尔积的形式表达出来。

左连接:两表关联,左表全部保留,右表关联不上用null表示。

右连接:右表全部保留,左表关联不上的用null表示。

内连接:两表关联,保留两表中交集的记录。

全连接:两表关联,查询它们的所有记录。union




锁的概念

出现争夺资源。

读锁. 共享锁. 一个进程在读取某表的时候,另一个进程也可以读取.但是当前进程仅仅能读取这个表,而不能操作其他表.

写锁. 独占锁. 一个进程在写表时,是不允许另一个进程写,当前进程仅仅能操作这一个表.




索引种类

Primary 主键索引 不重复,不为空,不能为0

Key 普通索引 没什么要求

Unique 唯一索引 不重复,可以为null,

Fulltext 全文索引 列是 varchar,char,text.没什么要求。 关键字来自列的部分




怎么判断对象是垃圾对象

  1. 什么样的对象是垃圾?一般来说,所有指向对象的引用都已失效,不可能再有程序能调用到这个对象,那么这个对象就成了垃圾,应该被回收。

1.1 根据这个思路,很容易就能想到用《引用计数》的办法来确定一个对象是否是垃圾。即每当多一个引用指向对象时,引用计数加一,每当少一个引用指向对象时,引用计数减一,

引用计数减到零,对象就可以被回收了。

1.2 然而引用计数有一个致命问题不好解决,就是循环引用的问题。比如说一个循环链表,他们循环引用者,引用计数永远不会为零,但是实际上程序已经不能访问他们了,他们应该被回收。

1.3 所以Java实际上是使用基于GC Roots的可达性分析,什么是GC Roots?所有类的静态变量,每个线程调用栈上的本地变量。

(实际上我们编程时也是要从这些地方开始访问数据),所有这些对象,以及被这些对象所指向的对象,都是活的对象。活的对象所指向的对象也是活的对象。

1.4 所以只要在GC的时刻,让程序暂停运行,然后从GC Roots开始分析,最后没有被标记为活对象的对象就是垃圾了。




创建对象的步骤

检测类是否被加载
为对象分配内存
为分配的内存空间初始化零值
对对象进行其他设置
执行init方法




创建对象的方式

①类名 对象名=new 类名();最常用方式,步骤

②运用反射手段,调用java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法步骤

③调用对象的clone()方法步骤

④运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法步骤




何时需要做jvm调优?

  1. heap 内存(老年代)持续上涨达到设置的最大内存值;
  2. Full GC 次数频繁;
  3. GC 停顿时间过长(超过1秒);
  4. 应用出现OutOfMemory 等内存异常;
  5. 应用中有使用本地缓存且占用大量内存空间;
  6. 系统吞吐量与响应性能不高或下降。




JVM调优原则:
1、多数的Java应用不需要在服务器上进行JVM优化;
2、多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题;
3、在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合);
4、减少创建对象的数量;
5、减少使用全局变量和大对象;
6、JVM优化是到最后不得已才采用的手段;
7、在实际使用中,分析GC情况优化代码比优化JVM参数更好;




JVM调优目标 :

  1. GC低停顿;
  2. GC低频率;
  3. 低内存占用;
  4. 高吞吐量;




dubbo
1.随机模式。按权重设置随机概率。在一个截面上碰撞的概率较高,但调用越大分布越均匀
2.轮询模式。按公约后的权重设置轮询比例。但存在响应慢的服务提供者会累积请求
3.最少活跃调用数。响应快的提供者接受越多请求,响应慢的接受越少请求
4.一致hash。根据服务提供者ip设置hash环,携带相同的参数总是发送的同一个服务提供者,若服务挂了,则会基于虚拟节点平摊到其他提供者上




常用线程池
newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
这种类型的线程池特点是:
工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。
在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
newFixedThreadPool
创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。
newSingleThreadExecutor
创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。
newScheduleThreadPool
创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。




新建状态
当用new操作符创建一个线程时, 例如new Thread®,线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码

就绪状态
一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。

 处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。

运行状态
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.

阻塞状态
线程运行过程中,可能由于各种原因进入阻塞状态:
1>线程通过调用sleep方法进入睡眠状态;
2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
3>线程试图得到一个锁,而该锁正被其他线程持有;
4>线程在等待某个触发条件;

死亡状态
有两个原因会导致线程死亡:

  1. run方法正常退出而自然死亡,
  2. 一个未捕获的异常终止了run方法而使线程猝死。
    为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.




缓存常见面试题
https://blog.csdn.net/qq_40241957/article/details/84205320



jvm常用优化参数
1、堆设置
-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n:设置持久代大小
2、收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器
3、垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
4、并行收集器设置
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
5、并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:设置并发收集器年轻代




ioc 控制反转
不通过 new 关键字来创建对象,而是通过 IoC 容器(Spring 框架) 来帮助我们实例化对象。我们需要哪个对象,直接从 IoC 容器里面过去即可
控制 :指的是对象创建(实例化、管理)的权力
反转 :控制权交给外部环境(Spring 框架、IoC 容器)
减少代码耦合




Aop面向切面编程
两个类公用一块代码,可以通过oop(面向对象编程)思想中的继承,将公用的类继承,减少重复代码
代码重复问题
解决横切逻辑代码和业务代码混在一起的情况,从根本上解耦合
切 :指的是横切逻辑,原有业务逻辑代码不动,只能操作横切逻辑代码,所以面向横切逻辑
面 :横切逻辑代码往往要影响的是很多个方法,每个方法如同一个点,多个点构成一个面。这里有一个面的概念




Springmvc的优点:
(1)可以支持各种视图技术,而不仅仅局限于JSP;
(2)与Spring框架集成(如IoC容器、AOP等);
(3)清晰的角色分配:前端控制器(dispatcherServlet) ,请求到处理器映射(handlerMapping),处理器适配器(HandlerAdapter),视图解析器(ViewResolver)。
(4) 支持各种请求资源的映射策略。




Mybatis的一级、二级缓存:
(1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。
(2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置 ;
(3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear 掉并重新更新,如果开启了二级缓存,则只根据配置判断是否刷新。




SpringMVC怎么样设定重定向和转发的?
(1)转发:在返回值前面加"forward:",譬如"forward:user.do?name=method4"
(2)重定向:在返回值前面加"redirect:",譬如"redirect:http://www.baidu.com"




SpringMVC常用的注解有哪些?
@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。
@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。
@ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。




arraylist和linkedlist区别
都是实现list接口的列表,arraylist是基于数组的数据结构,linkedlist是基于链表的数据结构,当获取特定元素时,ArrayList效率比较快,它通过数组下标即可获取,而linkedlist则需要移动指针。当存储元素与删除元素时linkedlist效率较快,只需要将指针移动指定位置增加或者删除即可,而arraylist需要移动数据




springMVC和struts2的区别有哪些?
(1)springmvc的入口是一个servlet即前端控制器(DispatchServlet),而struts2入口是一个filter过虑器(StrutsPrepareAndExecuteFilter)。
(2)springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。
(3)Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。




什么是三次握手?
TCP协议建立连接时,需要三次发送数据包:
第一次:客户机向服务器端请求建立连接
第二次:服务器收到客户机的请求,发出响应
第三次:客户机收到响应 认为连接建立成功




UNION DISTINCT和UNION ALL。 UNION DISTINCT组合两个输入,并应用DISTINCT过滤重复数据




数据库隔离级别

  1. ISOLATION_READ_UNCOMMITTED(读取未提交)
    读未提交数据,这是事务最低的隔离级别,在并发的事务中,它充许一个事务可以读到另一个事务未提交的更新数据。(会出现脏读,不可重复
    读和幻读)
  2. ISOLATION_READ_COMMITTED(读已提交)
    读已提交数据,保证在并发的事务中,一个事务修改的数据提交后才能被另外一个事务读取到。(会出现不可重复读和幻读)
  3. ISOLATION_REPEATABLE_READ(可重复读)
    可重复读,这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻读。一般是采用“快照”的方式来实现的。
  4. ISOLATION_SERIALIZABLE(可串行化)
    事务被处理为顺序执行。这是花费最高,但也是最可靠的事务隔离级别。能有效的避免脏读、不可重复读、幻读。




LinkedeList和ArrayList的区别
1、数据结构不同
ArrayList是Array(动态数组)的数据结构,LinkedList是Link(链表)的数据结构。
2、效率不同
当随机访问List(get和set操作)时,ArrayList比LinkedList的效率更高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找。
当对数据进行增加和删除的操作(add和remove操作)时,LinkedList比ArrayList的效率更高,因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动。【视频教程推荐:Java视频教程】
3、自由性不同
ArrayList自由性较低,因为它需要手动的设置固定大小的容量,但是它的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用;而LinkedList自由性较高,能够动态的随数据量的变化而变化,但是它不便于使用。
4、主要控件开销不同
ArrayList主要控件开销在于需要在lList列表预留一定空间;而LinkList主要控件开销在于需要存储结点信息以及结点指针信息。

未初始化时默认长度为10,扩容最大值interger. max val - 8



悲观锁
悲观锁,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态
乐观锁
乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以只会在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回用户错误的信息,让用户决定如何去做。




Java中HashMap是利用“拉链法”处理HashCode的碰撞问题。在调用HashMap的put方法或get方法时,都会首先调用hashcode方法,去查找相关的key,当有冲突时,再调用equals方法。hashMap基于hasing原理,我们通过put和get方法存取对象。当我们将键值对传递给put方法时,他调用键对象的hashCode()方法来计算hashCode,然后找到bucket(哈希桶)位置来存储对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当碰撞发生了,对象将会存储在链表的下一个节点中。hashMap在每个链表节点存储键值对对象。当两个不同的键却有相同的hashCode时,他们会存储在同一个bucket位置的链表中。键对象的equals()来找到键值对。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值