Java 面经收集(一)

1.HashMap的数据结构,在get和put的流程是怎么样的,HashMap中桶的下标是如何确定的

HashMap内部结构是数组(Node[] table)和链表结合组成的复合结构,数组被分成一个个桶(bucket)或槽,通过哈希值决定键值对在这个数组的寻址;哈希值相同的键值对,则以链表形式存储。当链表大小超过阈值(TREEIFY_THRESHOLD = 8)时,链表就会被改造成树形结构(红黑树)。

put方法

调用put方法时 ,首先将k,v封装到Node对象当中(节点),然后它的底层会调用K的hashCode()方法得出hash值。每个数组的位置就是一个哈希值, key的hashcode值 可看做成数组的下标 如果两个值哈希值一样,就会占用一个位置 ,就通过链表的方式把 value连接起来,他们就成了一个链表。如果下标位置上如果没有任何元素,就把Node添加到这个位置上。如果说下标对应的位置上有链表。此时,就会拿着k和链表上每个节点的k进行equal。如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末尾。如果其中有一个equals返回了true,那么这个节点的value将会被覆盖。

get方法

首先,我们传入要查找的键key,然后通过hash(key)方法计算键的哈希码。哈希码被用来确定键在 HashMap 中的桶(bucket)位置。根据哈希码,我们可以确定键在数组中的索引位置。
接下来,我们在确定的索引位置找到对应的桶。如果桶为空,即没有键值对存在,那么返回 null,表示没有找到对应的值。
如果桶不为空,那么可能存在多个键值对,这时我们需要遍历链表或红黑树来找到具有相同键的节点。在遍历过程中,我们会使用键的 equals() 方法来比较传入的键和当前节点的键是否相等。如果找到了相等的键,那么返回对应节点的值。
如果遍历完整个链表或红黑树仍然没有找到相等的键,那么返回 null,表示没有找到对应的值。
整个get()方法的原理就是根据键的哈希码确定索引位置,然后在对应的桶中遍历链表或红黑树,通过 equals() 方法比较键的相等性来找到对应的值

2.HashMap的扩容机制

在put()方法中有调用addEntry()方法,这个方法里面是具体的存值,在存值之前还要判断是否需要扩容resize()

Java7扩容必须满足两个条件:

1、 存放新值的时候当前已有元素的个数必须大于等于阈值

2、 存放新值的时候当前存放数据发生hash碰撞(当前key计算的hash值换算出来的数组下标位置已经存在值)

Java8中扩容只需要满足一个条件:

1.当前存放新值(注意不是替换已有元素位置时)的时候已有元素的个数大于等于阈值(已有元素等于阈值,下一个存放后必然触发扩容机制)且扩容发生在存放后,即是数据存放后(先存放后扩容),判断当前存入对象的个数,如果大于阈值则进行扩容.
java7是在存入数据前进行判断是否扩容,而java8是在存入数据后再进行扩容的判断。

3.ArrayList和HashMap的区别,使用场景的区别
实现的接口数据存放形式/使用场景底层数据结构默认大小和扩容时间复杂度

ArrayList

List单个数据数组

默认大小:10

扩容:原容量的0.5倍+1

查找:O(1)
HashMapMap键值对数组+链表(红黑树)

默认大小16

扩容:原容量的 1 倍,加载因子为0.75

直接将元素插入/或者直接返回未查找:O(1)

如果里面有元素,那么就沿着链表进行遍历,时间复杂度就是O(n)

如果是红黑树:O(logn)

4.ArrayList和LinkedList的区别,如何扩容

两者都属于集合类的事先方式

ArrayList是基于动态数组实现的,扩容是通过开辟一个新的数组空间,将原数组整体复制过去后,删除原数组。

LinkedList是基于双向链表实现的,不需要扩容

5.为什么并发会带来线程的不安全,java并发编程如何解决这个问题

什么是线程的不安全:

  • 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步,或者在调用方代码不做其他的协调操作,这个对象的行为获取的结果仍然是正确的,那个称这个对象是线程安全的。
  • 多线程环境下对共享变量的读写操作导致操作结果与预期结果不一致的问题。线程安全问题都是由全局变量及静态变量引起的。像多个线程访问同一个对象的ticket 数据,就是不安全的。

为什么并发会带来线程不安全

多线程并发问题的起因是由于多个线程同时访问共享资源而产生的竞争和冲突

java并发编程如何解决这个问题

1. synchronized关键字

synchronized关键字可以用来实现线程的互斥访问,保证同一时间只有一个线程可以访问共享资源。在Java中,可以使用synchronized关键字修饰方法或代码块,以达到线程同步的目的。例如:

public synchronized void add() {
    count++;
}

2. Lock接口

Lock接口是Java提供的另一种线程同步机制,相比synchronized关键字,Lock接口提供了更加灵活的锁机制,可以实现更加细粒度的线程同步。例如:

Lock lock = new ReentrantLock();
public void add() {
    lock.lock();
    try {
        count++;
    } finally {
        lock.unlock();
    }
}

3. volatile关键字

volatile关键字可以保证共享变量的可见性,即当一个线程修改了共享变量的值后,其他线程可以立即看到最新的值。例如:

volatile int count = 0;
public void add() {
    count++;
}

4. AtomicInteger类

AtomicInteger类是Java提供的一个原子性的整型变量,可以保证对该变量的所有操作都是原子性的。例如:

AtomicInteger count = new AtomicInteger();
public void add() {
    count.incrementAndGet();
}

5. CountDownLatch类

CountDownLatch类可以用来协调多个线程的执行,实现线程之间的同步。例如:

CountDownLatch latch = new CountDownLatch(2);
new Thread(() -> {
    // do something
    latch.countDown();
}).start();
new Thread(() -> {
    // do something
    latch.countDown();
}).start();
latch.await();
public synchronized void add() {
    count++;
}
6.Synchronized VS CAS

CAS是一种性能优化手段

CAS(Compare and Swap)算法是一种基于硬件的原子操作,用于实现多线程之间的同步和互斥。CAS算法的基本思想是:通过比较内存中的值和期望值是否相等,如果相等,则将内存中的值修改为新值,否则不进行操作,继续比较。

在Java中,CAS算法是通过Atomic类和Unsafe类来实现的。Atomic类是一个原子类,提供了一些原子操作方法,可以保证操作的原子性和线程安全性。Unsafe类是一个不安全类,提供了一些底层操作方法,可以绕过Java的安全机制,直接对内存进行操作。

通过使用CAS算法,可以避免竞态条件和线程同步等问题,提高程序的性能和并发性能。但是,需要注意CAS算法的一些缺点,比如ABA问题、性能退化问题等,需要根据实际情况进行选择和优化。

CAS算法主要用于实现多线程之间的同步和互斥。在Java中,CAS算法适用于一些需要高并发、低延迟的场景,比如:

计数器:CAS算法可以实现原子递增和递减操作,适用于计数器等场景。

状态标志:CAS算法可以实现状态标志的原子操作,适用于状态标志等场景。

队列:CAS算法可以实现无锁队列的原子入队和出队操作,适用于高并发场景。

线程池:CAS算法可以实现线程池的线程数量的原子修改操作,适用于动态调整线程池大小的场景。

7.线程池的核心参数以及使用流程

// 核心线程数
int corePoolSize = 10;
// 最大线程数
int maxPoolSize = 15;
// 存活时间(值)
long keepAliveTime = 60;
// 存活时间(单位)
TimeUnit keepAliveUnit = TimeUnit.SECONDS;
// 工作队列(具体队列特性请见下文)
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
// 拒绝策略(具体策略特性请见下文)
RejectedExecutionHandler rejectHandler = new ThreadPoolExecutor.AbortPolicy();

// 实例化线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
        corePoolSize, maxPoolSize, 
        keepAliveTime, keepAliveUnit, 
        workQueue, rejectHandler
);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值