1. ArrayList和LinkedList区别,ArrayList是否会越界
ArrayList底层为动态数组,主要优势在于通过下标(随机查询)进行查询而LinkedList底层是双向链表结构的查询是需要便利链表的。但是如果是获取第一个或者最后一个元素这两个集合是没有区别的。因为LinkedList记录了第一个元素和最后一个元素并不需要去遍历链表。当然我们平时说的 ArrayList查询快 插入慢这里的插入指的是在中间插入而是需要维护下标索引(插入时进行扩容也会慢是因为要及进行数据 迁移)。而LinkedList中间插入的时候因为其是双向链表可以不连续只需要维护 befor 和after,不用维护索引所以查询没有ArrayList快。但是同样的 元素 linkedList比ArrayList占用的空间大。 另外lenkindlist还额外的实现了Deque(双端队列)接口,所以linkendList还可以当作队列来实现, ArrayList初始化大小为10 ,后续扩容为原始大小的1.5倍,linkendList 没有初始化大小它是一个链表直接存储。
2. ArrayList和HashSet 区别
- HashSet 是无序且不重复的是散列结构不是线性结构所以查询效率相对不错,并且线程不安全的。每次添加需要判断hashCode是否存在。
- ArrayList 可以存重复的值且是有序的,通过可变大小的数据来实现允许所有元素。
- 他们都不是线程安全的,但是HashSet没有ArrayList快因为它需要维护去重
3. 为什么redis 快
- 纯内存中的操作而且还是单线程(不用去来回切换上下文)
- IO多路复用
- 高效的数据结构(简单动态字符串SDS,双向链表,哈希表,跳跃表,整数数组,压缩列表)
4. bean 的生命周期(如何创建一个bean)
- 根据路径找到类创建bean定义
- 推断构造方法(默认都是使用无参构造,当然可以通过@Autowired指定具体的构造方法)
- 实例化类
- 依赖注入
- 回调Aware方法
- 初始化前(@PostConstruct一些业务场景比如查询数据库...)
- 初始化(调用InitializingBean他的afterPropertiesSet)
- 初始化后(这里如果有Aop的话会去走aop产生代理对象将代理对象放到单例池)
- 将创建出来的bean 放到IOC 容器中去(单例池也可以理解为一个map集合中去)
- 使用bean
- spring容器停止销毁bean
5.创建线程池7大参数
- corePoolSize核心线程数
- maxPoolSize 最大线程数
- keepAliveSeconds 线程空闲存活是时间
- queueCapacity队列长度
- 拒绝策略
- 线程工厂
- 时间单位(存活时间)
6. ArrayList 多属性去重
使用 stream 中的 filter+ 函数式方法进行去重
List<User> list=new ArrayList<>();
User user = new User();
user.setPassword("1242");
user.setNickName("xkk");
user.setType("sucess");
User user1 = new User();
user1.setPassword("1242");
user1.setNickName("xkk");
user1.setType("faile");
User user2 = new User();
user2.setPassword("666");
user2.setNickName("xqq");
user2.setType("dis");
list.add(user);
list.add(user1);
list.add(user2);
list.stream().filter(distinctByKey(new Function<User, Object>() {
//这里为了方便理解没有使用lambada表达式
@Override
public Object apply(User user) {
//这里使用密码和昵称进行去重
return user.getPassword()+":"+user.getNickName();
}
})).collect(Collectors.toList());
public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
return new Predicate<T>() {
@Override
public boolean test(T t) {
return seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) ==null;
}
};
}
重载和重写的区别
重载:发生在同一个类中,方法名相同,参数类型不同,个数不同,顺序不同。 方法返回值和修饰符不可以不同,发生在编译时(报错)。
重写:发生在父子类中,方法名参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类。如果父类的方法修饰符为 private 则子类就不能重写该方法。
ConcurrentHashMap的扩容机制
1.7 版本 :
- 是基于Segment分段实现的
- 每个Segment相对于一个小型的hashMap
- 每个Segment内部进行扩容,和hashMap的扩容逻辑类似
- 先生成新数组,然后将数据转移到新的数组
- 扩容的判断也是每个Segment内部但单独判断的,判断是否超过阈值
1.8 版本 :
- 不再基于Segment分段实现
- 当某个线程进行put时,如果发现正在扩容那么改下安城进行一起扩容
- 支持多个线程同时扩容
- 扩容之前也是先生成一个新的数组
- 在转移数组时,先将原数组进行分组,将每组分给不同的线程来讲进行元素的转移,每个线程负责一组或者多组的元素转移工作。
如何理解springboot中的starter
定义了一个starter的jar包,写一个配置类 @Configuration 将需要用到的bean 定义配置进来进来然后在starter的META-INF/spring.factories中写入该配置类,spring会按照约定来加载该配置类
redis 缓存淘汰策略
8种策略
- volatile-lru,针对设置了过期时间的key,使用lru算法进行淘汰。
- allkeys-lru,针对所有key使用lru算法进行淘汰。
- volatile-lfu,针对设置了过期时间的key,使用lfu算法进行淘汰。
- allkeys-lfu,针对所有key使用lfu算法进行淘汰。
- volatile-random,从所有设置了过期时间的key中使用随机淘汰的方式进行淘汰。
- allkeys-random,针对所有的key使用随机淘汰机制进行淘汰。
- volatile-ttl,删除生存时间最近的一个键。
- noeviction(默认),不删除键,值返回错误。
主要是4种算法,针对不同的key,形成的策略。 算法:
- lru 最近很少的使用的key(根据时间,最不常用的淘汰)
- lfu 最近很少的使用的key (根据计数器,用的次数最少的key淘汰)
- random 随机淘汰
- ttl 快要过期的先淘汰
java IO 流有哪些
- 字节流 :字节流按 8 位传输以字节为单位输入输出数据。
- 字符流 :字符流按 16 位传输以字符 为单位输入输出数据
IO 和 NIO
- IO: 同步阻塞式IO 特点是简单方便处理并发能力低。
- NIO:New IO 是同步非阻塞IO,是传统IO的升级客户端个服务端通过Channel(通道)通讯,实现多路复用。
HashMap的原理
HashMap是基于Hash算法实现的,我们通过put方法来进行存储,和get方法进行取值。当key 传入时 HashMap会根据key.hashCode计算出key的哈希code 根据哈希code将value保存到bucket 中,当计算 hash值相同时我们称之为hash 冲突,hash 的处理时是使用链表和 红黑树进行存储 value,冲突个数较少时使用链表,反之使用红黑树。
HashSet的原理
HashSet的底层使用的就是HashMap来保存元素,但是hashSet是不允许有重复的key。
float 和 double 的区别是什么?
float 最多可以存储 8 位的十进制数,并在内存中占 4 字节。 double 最可可以存储 16 位的十进制数,并在内存中占 8 字节。