2021年 Java 面试题集锦 -- 持续更新

2024-08-24 17:48

本文主要是介绍2021年 Java 面试题集锦 -- 持续更新,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Java面试题

Java集合

  1. ArrayList、HashSet、HashMap是线程安全的吗?如果不是怎么获取线程安全的集合?

    ArrayList、HashSet、HashMap都不是线程安全的。在集合中只有Vector和HashTable是线程安全的

  2. 什么是JAVA集合

    java 集合既用来存放对象的容器

    • 集合主要分为Collection和Map两个接口
    • Collection又分别被List和Set继承
    • List被AbstractList实现,又分为ArrayList、LinkList和VectorList
    • Set被AbstractSet实现,又分为HashSet和TreeSet
    • Map被AbstractMap实现,又分为HashMap和TreeMap
    • Map被Hashtable实现
  3. 哈希碰撞/哈希冲突是什么,如何解决?

    • 在对字符串,文件,甚至目录进行hash取值时,有可能得到的多个一样的hash值,这就是hash碰撞/hash冲突。举个众所周知的Hash函数CRC32,如果你给这个Hash函数“plumless” 和“buckeroo”这2个字符串,它会生成相同的Hash值,这是已知的Hash冲突

      这里写图片描述

      解决方法有:

      1.开放地址法(再散列法)

      开放地执法有一个公式:Hi=(H(key)+di) MOD m i=1,2,…,k(k<=m-1)

      其中,m为哈希表的表长。di 是产生冲突的时候的增量序列。如果di值可能为1,2,3,…m-1,称线性探测再散列。如果di取1,则每次冲突之后,向后移动1个位置.如果di取值可能为1,-1,2,-2,4,-4,9,-9,16,-16,…kk,-kk(k<=m/2),称二次探测再散列。如果di取值可能为伪随机数列。称伪随机探测再散列。

      2.再哈希法Rehash

      当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突时。缺点:计算时间增加。比如上面第一次按照姓首字母进行哈希,如果产生冲突可以按照姓字母首字母第二位进行哈希,再冲突,第三位,直到不冲突为止.这种方法不易产生聚集,但增加了计算时间。

      3.链地址法(拉链法)

      将所有关键字为同义词的记录存储在同一线性链表中.基本思想:将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。对比JDK 1.7 hashMap的存储结构是不是很好理解。至于1.8之后链表长度大于6rehash 为树形结构不在此处讨论。

  4. ArrayList和LinkedList的不同?

    ArrayList类似于数组而LinkedList类似于链表

    在ArrayList的前面或中间插入数据时,必须将其后所有的数据相应的后移,这会浪费很多时间,当然如果是只在集合后面添加元素,并随机访问其中元素时,使用ArrayList性能还是很出色的。

    如果是对集合的前面或者红箭添加或删除数据时,并按照顺序访问其中的元素时,就应该使用LinkedList

    总的来说ArrayList的查询效率高,增删效率差,适用于频繁查询和增删少的场景,而LinkedList的查询效率低,但增删效率高,使用与增删动作频繁和查询次数少的场景

  5. ArrayList和Vector的区别?

    • 同步性

      Vector是线程安全的,而ArrayList不是线程安全的,如果是一个线程访问集合最好用ArrayList,因为它不考虑线程安全所以效率会高一些;如果有多个线程访问集合就用Vector,因为不需要再去考虑和编写线程安全的代码

    • 数据增长

      ArrayList与Vector都有一个初始的容量大小,当存储元素超过本身容量时,Vector默认增长原来的一倍,而ArrayList增加原来的0.5倍。Vector是可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法

  6. HashMap的数据存储和实现原理你了解多少

    • 存值

      hashmap在存数据的时候是基于hash的原理,当我们调用put(key,value)方法的时候,其实我们会先对键key调用key.hashcode()方法,根据方法返回的hashcode来找到bucket的位置来存Entry对象。

      如果出现hashcode相同的情况,这时候就会产生hash碰撞(hashmap的底层存储结构是 数组+链表),这时候根据hashcode找到对应的bucket,然后在链表逐一检查有没有存相同的key,用equals()进行比较,如果有则用新的value取代旧的value,如果没有就在链表的尾部加上这个新的Entry对象

    • 取值

      当使用get(key)时,会调用key的hashcode方法获得hashcode,然后根据hashcode找到bucket,优于bucket对应的链表中可能存有多个Entry,这个时候会调用key的equals()找到对应的Entry,然后把值返回

  7. HashMap的长度为什么是2的幂次方?

    • HashMap为了存取高效,要尽量较少碰撞,就是要尽量把数据分配均匀,每个链表长度大致相同,这个实现就在把数据存到哪个链表中的算法;
      这个算法实际就是取模,hash%length,计算机中直接求余效率不如位移运算,源码中做了优化hash&(length-1),
      hash%length==hash&(length-1)的前提是length是2的n次方;
      为什么这样能均匀分布减少碰撞呢?2的n次方实际就是1后面n个0,2的n次方-1 实际就是n个1;
  8. HashSet和HashMap的区别与联系

    HashSet实际上为(key,null)类型的HashMap,而我们知道,HashSet的key是不能重复的,所以HashSet的值自然也是没有重复的.因为HashMap的key可以为null,所以HashSet的值可以为null

    HashMapHashSet
    HashMap实现了Map接口HashSet实现了Set接口
    HashMap储存键值对HashSet仅仅存储对象
    使用put()方法将元素放入map中使用add()方法将元素放入set中
    HashMap中使用键对象来计算hashcode值HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false
    HashMap比较快,因为是使用唯一的键来获取对象HashSet较HashMap来说比较慢

Redis

1.Redis的特点

Redis全称Remote Dictionary Server(远程字典服务),用C编写,NoSQL数据库服务器。其本质是一个key-value的内存数据库,类似于memcached,其QPS能达到数十万次,单个value的最大限制1GB,而memcahed只有1MB,其支持的数据结构有: 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)

Redis的缺点就是其是内存数据库,其上限受限于运行的物理内存的大小

2.Redis的性能问题及解决放案

  • Redis做内存快照(内存中数据在某一个时刻的转态记录)是有两个命令:

​ save:在主线程中执行,会导致阻塞

​ bgsave:创建一个子进程,专门写RDB文件,避免主线程阻塞,这是Redis RDB文件生成的默认配置。但是fork 创建过程中本身会阻塞主线程,主线程内存越大阻塞时间越长

  • AOF持久化会导致重启恢复速度变慢
  • Redis主从复制的速度和连接的稳定性受网络的影响,Master和Slave最好在一个局域网中

3.Redis使用的场景

​ ● 会话缓存(Session Cache)

​ ● 全页缓存(FPC)

​ ● 队列

​ ● 排行榜/计数器

​ ● 发布/订阅

4.Redis的优缺点

​ 优点

​ ● QPS能达到数十万
​ ● 所有的操作都是原子性的,也就是说一个操作要么成功要么失败,就像关系型数据库中的事务
​ ● 支持丰富的数据类型

​ 缺点

​ ●因为是内存数据库所以受限于机器本身的内存大小,Redis本身可以有过期策略,但内存增长过快时需要定期 删除数据

​ ●在重启Redis时,持久化的数据越大加载越慢,在完成加载之前,Redis将不提供服务

5.Redis的持久化是什么

RDB持久化:Redis默认的持久化方案。在指定的时间间隔内将内存中的数据写入到磁盘中,这些数据写在指定目录下的dump.rdb文件,Redis重启会通过dump.rdp文件恢复数据,因为是定时备份所以会有数据丢失的情况

AOF持久化:Redis默认不开启。它是为了弥补RDB的不足(数据丢失或数据不一致),所以它采用写日志的方式记录所有的写操作,Redis重启会通过日志文件恢复数据,这种情况如果数据量比较大的话,启动就会慢

RDB和AOF同时应用时:当Redis重启的时候,它会优先使用AOF来还原数据,因为数据比较全

6.RDB和AOF优缺点

优点

RDB:RDB是定时备份的,可以随时将数据还原到不同版本,也比较适用于灾难性恢复

AOF:AOF对日志文件进行追加,在日志进行写入过程中关机了或磁盘满了,使用redis-check-aof工具也可以修复这些问题

缺点

RDB:如果对数据完整性有要求的话就不适用于RDB,因为会丢数据。

​ 每次保存RDB时,Redis都要fork()出一个子进程,由子进程进行数据持久化的工作。数据集比较大时,fork()会比较耗时,可能会在一定毫秒内停止处理客户端的请求

AOF:AOF数据量比较大,使用fsync策略,恢复数据速度慢于RDB

7.Redis的数据类型

  • 字符串(strings):String数据结构是最简单的key-value类型

    用法:

    192.168.1.167:6379> set name dd
    OK
    192.168.1.167:6379> get name
    "dd"
    
  • 散列(hashes):类似Java中的hash,可以存放对象值,如用户:用户名、年龄等,具体通过HMSET 指令设置 hash 中的多个域,而 HGET 取回单个域,hgetall取得所有域

    用法:

    192.168.1.167:6379> hset user:1000 username dd birthyear 1999 verified 11
    (integer) 3
    192.168.1.167:6379> hget user:1000 username
    "dd"
    192.168.1.167:6379> hgetall user:1000
    1) "username"
    2) "dd"
    3) "birthyear"
    4) "1999"
    5) "verified"
    6) "11"
    192.168.1.167:6379> 
    
  • 列表(lists):List其实就是双向链表,rpush 设置一个列表值,lrange 获取一个列表值

    用法:

    192.168.1.167:6379> rpush list a
    (integer) 1
    192.168.1.167:6379> rpush list b
    (integer) 2
    192.168.1.167:6379> rpush list c
    (integer) 3
    192.168.1.167:6379> lrange list 0 -1
    1) "a"
    2) "b"
    3) "c"
    192.168.1.167:6379> 
  • 集合(sets):Set 是 String 的无序排列,就是不根据插入顺序进行排序。集合中不能出现重复数据。set是通过hash实现的,所以增删查的时间复杂度都是o(1)。SADD 指令把新的元素添加到 set 中。对 set 也可做一些其他的操作,比如测试一个给定的元素是否存在,对不同 set 取交集,并集或差,等等

    用法:

    192.168.1.167:6379> sadd myset 2 3 1
    (integer) 3
    192.168.1.167:6379> smembers myset
    1) "1"
    2) "2"
    3) "3"
    192.168.1.167:6379> 
  • 有序集合(sorted sets) :有序集合和集合一样是身体溶类型的集合,但不允许有重复元素。每个元素都有一个double类型的分数,redis通过这个分数从小到大进行排序。因为有序集合也通过hash实现,所以增删查的时间复杂度o(1)

    用法:

    192.168.1.167:6379> zadd mysortedset 1 b
    (integer) 1
    192.168.1.167:6379> zadd mysortedset 2 a
    (integer) 1
    192.168.1.167:6379> zadd mysortedset 3 c
    (integer) 1
    192.168.1.167:6379> zadd mysortedset 2 d
    (integer) 1
    192.168.1.167:6379> zrange mysortedset 0 10
    1) "b"
    2) "a"
    3) "d"
    4) "c"
    192.168.1.167:6379> 
  • 范围查询bitmaps:通过一个bit来标识元素对应的值

    用法:

    192.168.1.167:6379> setbit mybit 1 1
    (integer) 0
    192.168.1.167:6379> setbit mybit 2 1
    (integer) 0
    192.168.1.167:6379> setbit mybit 3 1
    (integer) 0
    192.168.1.167:6379> bitcount mybit
    (integer) 3
    192.168.1.167:6379> 
  • hyperloglogs :HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。

    在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

    但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素

    基数既不重复元素的个数

    用法:

    192.168.1.167:6379> pfadd myhyperloglog a
    (integer) 1
    192.168.1.167:6379> pfadd myhyperloglog b
    (integer) 1
    192.168.1.167:6379> pfadd myhyperloglog c
    (integer) 1
    192.168.1.167:6379> pfadd myhyperloglog c
    (integer) 0
    192.168.1.167:6379> pfcount myhyperloglog
    (integer) 3
    192.168.1.167:6379> 
  • 地理空间(geospatial) :存储地理位置信息

    用法:

    192.168.1.167:6379> geoadd point1 13.123 38.133 "name1"  13.223 38.333 "name2"
    (integer) 2
    192.168.1.167:6379> geopos point1 name1 name2
    1) 1) "13.12299996614456177"2) "38.13300034651486925"
    2) 1) "13.22299808263778687"2) "38.33299998487134985"
    192.168.1.167:6379> 

这篇关于2021年 Java 面试题集锦 -- 持续更新的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1103178

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in