安卓 每日一题 2020年7-8月问题及答案 转载
最新 文章连接,本文不再同步
Android7月2日题: RxJava中的自定义操作符实现思路?
参考答案: RxJava的整体结构使用观察者与装饰器模式的组合实现,如果我们需要设计一个自定操作符,主要有以下情况:
-
如果自定义的操作符是作为第一个事件源头,可以通过继承Observable类进行下游对接。
-
如果自定义的操作符是作为变换过程中间的部份,可以通过继承AbstractObservableWithUpstream来进行上游与下游的对接过程。
-
如果自定义的操作符是作为最终观查者收到的结果的二次处理,可以通过继承Observer来进行结果的处理
Android7月6日题: 双亲委托机制的优点有哪些?
参考答案: 1.避免重复加载,当父加载器已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
2.安全性考虑,防止核心API库被随意篡改。
Android7月7日题: SurfaceView和View的区别?
参考答案:
View需要在UI线程对画面进行刷新,而SurfaceView可在子线程进行页面的刷新
SurfaceView在底层已实现双缓冲机制,因此SurfaceView比View更适用于需要频繁刷新、刷新时数据处理量很大的页面(如视频播放界面)
Android7月8日题: JetPack未来的发展如何?
参考答案:
因为JetPack可以解决百分之五十以上的容错问题,很多的疑难杂症JetPack已经考虑到了,Google安卓开发全家桶组件库集,未来Google会重推JetPack
Android7月9日题: android 中的动画有哪几类,它们的特点和区别是什么?
参考答案: 两种,一种是 Tween 动画、还有一种是 Frame 动画。Tween 动画,这种实现方式可以使视图组件移动、放大、缩小以及产生透明度的变化;另一种 Frame 动画,传统的动画方法,通过顺序的播放排列好的图片来实现,类似电影。
Android7月10日题: 内存溢出和内存泄漏有什么区别?
参考答案: 程序运行时所需的内存大于程序允许的最高内存,这时会出现内存溢出;应该被回收的内存,由于各种原因回收不了,这就是内存泄漏。内存泄漏过多,就容易导致内存溢出
Android7月13日题: Handler中ThreadLocal有什么作用
参考答案: 主要用来存储Looper,保证Looper的唯一性。ThreadLocal确保了每一个线程都有自己独一无二的Looper,这样就让handler机制能够有鲜明的线程的相关性。
Android7月14日题: scrollTo()和scollBy()的区别?
参考答案: scollBy内部调用了scrollTo,它是基于当前位置的相对滑动;而scrollTo是绝对滑动,因此如果使用相同输入参数多次调用scrollTo方法,由于View的初始位置是不变的,所以只会出现一次View滚动的效果。
Android7月15日题: 日志埋点,网络监测,权限监测,等等,诸多功能,代码重复有繁琐,有更优雅的方式实现吗?
参考答案:
切面思想,多种切面思想的方式实现,例如:动态代理,OOP封装,AspectJ 等实现。
Android7月16日题: JetPack中是如何监听Activity生命周期?
参考答案:
可以使用Lifecycles组件去感知Activity的生命周期
Activity因为已经实现了LifecycleOwner接口,能直接作为被观察者,所以可以直接在上面添加观察者,观察者类实现LifecycleObserver接口,以实现对Activity生命周期的监听。
Android7月20日题: 你是如何理解Java中强引用、软引用、弱引用以及虚引用?
参考答案:
(1)强引用:一般的Object obj = new Object() ,就属于强引用。
(2)软引用:一些有用但是并非必需,用软引用关联的对象,系统将要发生OOM之前,这些对象就会被回收。
一个程序用来处理用户提供的图片。如果将所有图片读入内存,这样虽然可以很快的打开图片,但内存空间使用巨大,一些使用较少的图片浪费内存空间,需要手动从内存中移除。如果每次打开图片都从磁盘文件中读取到内存再显示出来,虽然内存占用较少,但一些经常使用的图片每次打开都要访问磁盘,代价巨大。这个时候就可以用软引用构建缓存。
(3)弱引用:一些有用(程度比软引用更低)但是并非必需,用弱引用关联的对象,只能生存到下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收。
注意:软引用 SoftReference和弱引用 WeakReference,可以用在内存资源紧张的情况下以及创建不是很重要的数据缓存。当系统内存不足的时候,缓存中的内容是可以被释放的。实际运用(WeakHashMap、ThreadLocal)
(4)虚引用(幽灵引用),作用只是GC回收时得到一个通知
Android7月22日题: JVM虚拟机栈帧主要由哪些部份组成,他们的作用分别是?
参考答案:
-
局部变量表
用于存放方法参数和方法内定义的局部变量 -
操作数栈
用于从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者。 -
动态链接
Java虚拟机栈中,每个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用,持有这个引用的目的是为了支持方法调用过程中的动态连接。 -
方法出口
当一个方法开始执行,有两种方式退出方法:- 正常完成出口:方法正常完成并退出,没有抛出任何异常
- 异常完成出口:方法执行过程中遇到异常,并且这个异常在方法体内部没有得到处理,导致方法退出。
Android7月23日题: java中自定义注解的元注解有哪些?
参考答案:
1.@Retention: 定义注解的保留策略
2.@Target:定义注解的作用目标
3.@Document:说明该注解将被包含在javadoc中
4.@Inherited:说明子类可以继承父类中的该注解
Android7月24日题: DNS劫持是什么,在你的项目开发中你如何避免DNS劫持导致的问题?
参考答案: DNS劫持一般指域名劫持,通过攻击域名解析服务器(DNS),或伪造域名解析服务器(DNS)的方法,把目标网站域名解析到错误的IP地址从而实现用户无法访问目标网站或者访问钓鱼网站的目的。
为了避免DNS劫持,我们可以采用直连IP的方式避免。也可以使用HTTP(HTTPS)协议进行域名解析,域名解析请求直接发送至HTTPDNS服务器,绕过运营商Local DNS,避免域名劫持问题。Okhttp提供了DNS接口,使用如阿里、腾讯提供的httpdns服务取代系统的DNS解析。
Android7月28日题: SharedPrefrences的apply和commit有什么区别?
参考答案: apply没有返回值而commit返回boolean表明修改是否提交成功。
apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而commit是同步的提交到硬件磁盘.
Android7月29日题: LayoutInflater 的 inflate 方法的几个参数分别代表什么?
参****考答案: 第一个参数为布局文件ID方法用三个参数时,root的参数是否为空,决定了我们要不要沿用root的布局属性,attachToRoot是否为true,决定了是否将我们的layout作为子View添加进去。
方法用两个参数时,root既决定了是否要沿用root的布局属性,也决定了是否要将我们的layout作为子View添加进去。
Android7月30日题: Activity 的启动模式
参考答案: standard 标准模式:每次启动一个 Activity 都会重新创建一个新的实例,不管这个实例是否已经存在,此模式的 Activity 默认会进入启动它的 Activity 所属的任务栈中;
singleTop 栈顶复用模式:如果新 Activity 已经位于任务栈的栈顶,那么此 Activity 不会被重新创建,同时会回调 onNewIntent方法,如果新 Activity 实例已经存在但不在栈顶,那么Activity 依然会被重新创建;
singleTask 栈内复用模式:只要 Activity 在一个任务栈中存在,那么多次启动此 Activity 都不会重新创·建实例,并回调onNewIntent 方法,此模式启动 Activity A,系统首先会寻找是否存在 A 想要的任务栈,如果不存在,就会重新创建一个任务栈,然后把创建好 A 的实例放到栈中;
singleInstance单实例模式:这是一种加强的 singleTask 模式,具有此种模式的 Activity 只能单独地位于一个任务栈中,且此任务栈中只有唯一一个实例;
Android7月31日题: 多进程操作同一个文件怎么办?
参考答案: 在多个进程同时操作同一份文件的过程中,很容易导致文件中的数据混乱,需要锁操作来保证数据的完整性。可以使用flock文件锁来完成多进程操作文件的同步。但是flock不支持递归。而且多个进程都持有了读锁,现在都想升级到写锁,就会发生死锁。因此还要进一步封装,可以加入计数器支持递归与降级。
Android8月1日题: 常 见 的 Activity 类 型 有 FragmentActivitiy ,ListActivity,TabAcitivty 等。请描述一下 Activity 生命周期 。
参考答案: Activity 从创建到销毁有多种状态,从一种状态到另一种状态时会激发相应的回调方法,这些回调方法包括:onCreate onStart onResume onPause onStop onDestroy
其实这些方法都是两两对应的,onCreate 创建与 onDestroy 销毁;
onStart 可见与 onStop 不可见;onResume 可编辑(即焦点)与 onPause。
Android8月4日题: Android 中的 Context, Activity,Appliction 有什么区别?
参考答案: 相同:Activity 和 Application 都是 Context 的子类。
Context 从字面上理解就是上下文的意思,在实际应用中它也确实是起到了管理
上下文环境中各个参数和变量的总用,方便我们可以简单的访问到各种资源。
不同:维护的生命周期不同。Context 维护的是当前的 Activity 的生命周期,
Application 维护的是整个项目的生命周期。
使用 context 的时候,小心内存泄露,防止内存泄露,注意一下几个方面:
-
不要让生命周期长的对象引用 activity context,即保证引用 activity 的对象要与 activity 本身生命周期是一样的。
-
对于生命周期长的对象,可以使用 application,context。
-
避免非静态的内部类,尽量使用静态类,避免生命周期问题,注意内部类对外部对象引用导致的生命周期变化。
Android8月6日题: 什么是 AIDL 以及如何使用?
参考答案: aidl 是 Android interface definition Language 的英文缩写, 意思Android 接口定义语言。
②使用 aidl 可以帮助我们发布以及调用远程服务,实现跨进程通信。
③将服务的 aidl 放到对应的 src 目录,工程的 gen 目录会生成相应的接口类
我们通过 bindService(Intent,ServiceConnect,int)方法绑定远程服务,在 bindService 中有一个 ServiceConnec 接口, 我们需要覆写该类的onServiceConnected(ComponentName,IBinder)方法,这个方法的第二个参数 IBinder 对象其实就是已经在 aidl 中定义的接口,因此我们可以将 IBinder 对象强制转换为 aidl 中的接口类。
我们通过 IBinder 获取到的对象(也就是 aidl 文件生成的接口)其实是系统产生的代理对象,该代理对象既可以跟我们的进程通信,又可以跟远程进程通信, 作为一个中间的角色实现了进程间通信。
Android8月7日题: 子线程中能不能new handler?为什么?
参考答案: 不能,如果在子线程中直接new Handler() 会抛出异常java.lang.RuntimeException: Can’t create handler inside thread that has not called
在没有调用 Looper.prepare() 的时候不能创建 Handler, 因为在创建Handler 的源码中做了如下操作:
Handler 的构造方法中
public static Looper myLooper() {
return sThreadLocal.get();
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
“Can’tcreate handler inside thread that has notcalled Looper.prepare()”);
}
Android8月10日题: JVM 老年代和新生代的比例
参考答案:
Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。
在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。
堆大小 = 新生代 + 老年代。其中,堆的大小可以通过参数 –Xms、-Xmx 来指定。
(本人使用的是 JDK1.6,以下涉及的 JVM 默认值均以该版本为准。)
默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小。其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ), 即:Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。
JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。
因此,新生代实际可用的内存空间为 9/10 ( 即 90% )的新生代空间
Android8月11日题: 说下java 中的线程创建方式,线程池的工作原理。
参考答案:
java 中有三种创建线程的方式,或者说四种
-
继承 Thread 类实现多线程
-
实现 Runnable 接口
-
实现 Callable 接口
-
通过线程池
线程池的工作原理:线程池可以减少创建和销毁线程的次数,从而减少系统资源的消耗,当一个任务提交到线程池时
a.首先判断核心线程池中的线程是否已经满了,如果没满,则创建一个核心线程执行任务,否则进入下一步
b.判断工作队列是否已满,没有满则加入工作队列,否则执行下一步
c.判断线程数是否达到了最大值,如果不是,则创建非核心线程执行任务, 否则执行饱和策略,默认抛出异常
Android8月12日题: 多线程间通信和多进程之间通信有什么不同,分别怎么实现?
参考答案:
一、进程间的通信方式
-
管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
-
有名管道 (namedpipe) :有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
-
信号量(semophore ) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
-
消息队列( messagequeue ) :消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
-
信号 (sinal ) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
-
共享内存(shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
-
套接字(socket ) :套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
二、线程间的通信方式
-
锁机制:包括互斥锁、条件变量、读写锁
-
互斥锁提供了以排他方式防止数据结构被并发修改的方法
-
读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
-
条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
-
-
信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
-
信号机制(Signal):类似进程间的信号处理线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。
Android8月13****日题: synchronized 和volatile 关键字的区别
参考答案:
- volatile 本质是在告诉 jvm 当前变量在寄存器(工作内存)中的值是不确定的, 需要从主存中读取;synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
- volatile 仅能使用在变量级别;synchronized 则可以使用在变量、方法、和类级别的
- volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性
- volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
- volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化
Android8月14日题: 事件分发中的onTouch 和onTouchEvent 有什么区别,又该如何使用?
参考答案:
这两个方法都是在 View 的 dispatchTouchEvent 中调用的,onTouch 优先于 onTouchEvent 执行。如果在 onTouch 方法中通过返回 true 将事件消费掉,onTouchEvent 将不会再执行。
另外需要注意的是, onTouch 能够得到执行需要两个前提条件, 第一mOnTouchListener 的值不能为空,第二当前点击的控件必须是 enable 的。因此如果你有一个控件是非 enable 的,那么给它注册 onTouch 事件将永远得不到执行。对于这一类控件,如果我们想要监听它的 touch 事件,就必须通过在该控件中重写 onTouchEvent 方法来实现
Android8月17日题: Fragment 跟 Activity 之间是如何传值的
参考答案:
当 Fragment 跟 Activity 绑定之后, 在 Fragment 中可以直接通过getActivity()方法获取到其绑定的 Activity 对象,这样就可以调用 Activity的方法了。在 Activity 中可以通过如下方法获取到 Fragment 实例
获取到 Fragment 之后就可以调用 Fragment 的方法。也就实现了通信功能。
Android8月18日题: 请解释下 Android 程序运行时权限与文件系统权限的区别?
参考答案:
apk 程序是运行在虚拟机上的,对应的是 Android 独特的权限机制,只有体现到文件系统上时才使用 linux 的权限设置。
(一)linux 文件系统上的权限
-rwxr-x–x system system 4156 2010-04-30 16:13 test.apk代表的是相应的用户/用户组及其他人对此文件的访问权限,与此文件运行起来具有的权限完全不相关。比如上面的例子只能说明 system 用户拥有对此文件的读写执行权限;system 组的用户对此文件拥有读、执行权限;其他人对此文件只具有执行权限。而 test.apk 运行起来后可以干哪些事情,跟这个就不相关了。千万不要看 apk 文件系统上属于 system/system 用户及用户组,或者root/root 用户及用户组,就认为 apk 具有 system 或 root 权限
(二)Android 的权限规则
(1)Android 中的 apk 必须签名
(2)基于 UserID 的进程级别的安全机制
(3)默认 apk 生成的数据对外是不可见的
(4)AndroidManifest.xml 中的显式权限声明
Android8月19日题: Devik 进程,linux 进程,线程的区别
参考答案:
Dalvik 虚拟机运行在 Linux 操作系统之上。Linux 操作系统并没有纯粹的线程概念,只要两个进程共享一个地址空间,那么就可以认为它们是同一个进程的两个线程。Linux 系统提供了两个 fork 和 clone 调用,其中,前者是用来创建进程的,而后者是用来创建线程的。
一般来说,虚拟机的进程和线程都是和目标机器本地操作系统的进程和线程一一对应的,这样的好处是可以使本地操作系统来调度进程和线程。
每个 Android 应用程序进程都有一个 Dalvik 虚拟机实例。这样做得好处是Android 应用程序进程之间不会互相影响,也就是说,一个 Android 应用程序进程的意外终止,不会影响到其他的应用程序进程的正常运行。
每个 Android 应用程序进程都是由一种称为 Zygote 的进程 fork 出来的。
Zygote 进程是由 init 进程启动起来的,也就是在系统启动的时候启动的。
Zygnote 进程在启动的时候,会创建一个虚拟机实例,并且在这个虚拟机实例将所有的 Java 核心库都加载起来。每当 Zygote 进程需要创建一个
Android 应用程序进程的时候,它就通过复制自身来实现,也就是通过 fork 系统调用来实现。这些被 fork 出来的 Android 应用程序进程,一方面是复制了 Zygote 进程中的虚拟机实例,另外一方面是与 Zygote 进程共享了同一套 Java 核心库。这样不仅 Android 程序进程的创建很快,而且所有的应用程序都共享同一套 Java 核心库而节省了内存空间。
Android8月20日题: ART是什么?
参考答案:
ART是在Android 4.0引入并在Android 5.0中设为默认解决方案的主要特性之一。ART取代了Dalvik,但是前者与后者仍然保持了字节码级的兼容。ART的主要特征之一就是安装时对应用的AOT编译。这种方式的主要优点就是优化 产生的本地代码性能更好,执行起来需要更少的电量。劣势在于安装文件所需的空间和时间。在Android 6.0中, 大的应用需要数分钟才能安装完。
但是在Android N开发者预览版包含了一个混合模式的运行时。应用在安装时不做编译,而是解释字节码,所以可以快速启动。ART中代码在执行期间被分析,分析结果保存起来。然后,当设备空转和充电的时候,ART会执行针 对“热代码”进行编译,生成app image文件,并且在启动时一次性把它们加载到缓存。
Android8月24日题:
运行结果
public static void main(String[] args){
int i = 0;
System.out.println((i++)+(++i)+(i++)+(++i));
System.out.println(i);
}
参考答案:
int a = i++; // a= 0, i = 1
int b = ++i; // b = 2, i = 2
int c = i++; // c = 2, i = 3
int d = ++i; // d= 4, i = 4
a + b + c + d; // 0 + 2 + 2 + 4 = 8
Android8月26日题: 线程和进程的区别?
参考答案:
线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。
Android8月27日题: 说下冷启动与热启动是什么,区别,如何优化,使用场景等。
参考答案:
app 冷启动:当应用启动时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用, 这个启动方式就叫做冷启动(后台不存在该应用进程)。冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始 化 Application 类,再创建和初始化 MainActivity 类(包括一系列的测量、布局、绘制),最后显示在界面上。
app 热启动:当应用已经被打开, 但是被按下返回键、Home 键等按键时回到桌面或者是其他程序的时候,再重新打开该 app 时, 这个方式叫做热启动(后台已经存在该应用进程)。热启动因为会从已有的进程中来启动,所以热启动就不会走Application 这步了,而是直接走 MainActivity(包括一系列的测量、布局、绘制),所以热启动的过程只需要创建和初始化一个 MainActivity 就行了,而不必创建和初始化 Application
冷启动的流程
当点击 app 的启动图标时,安卓系统会从 Zygote 进程中 fork 创建出一个新的进程分配给该应用,之后会依次创建和初始化 Application 类、创建 MainActivity 类、加载主题样式 Theme 中的 windowBackground 等属性设置给 MainActivity 以及配置 Activity 层级上的一些属性、再 inflate 布局、当onCreate/onStart/onResume 方法都走完了后最后才进行 contentView 的measure/layout/draw 显示在界面上
冷启动的生命周期简要流程:
Application 构造方法 –> attachBaseContext()–>onCreate –>Activity 构造方法 –> onCreate() –> 配置主体中的背景等操作 –>onStart() –> onResume() –> 测量、布局、绘制显示
冷启动的优化主要是视觉上的优化,解决白屏问题,提高用户体验,所以通过上面冷启动的过程。能做的优化如下:
1、减少 onCreate()方法的工作量
2、不要让 Application 参与业务的操作
3、不要在 Application 进行耗时操作
4、不要以静态变量的方式在 Application 保存数据
5、减少布局的复杂度和层级
6、减少主线程耗时
Android8月28日题: 横竖屏切换时Activity 的生命周期
参考答案:
此时的生命周期跟清单文件里的配置有关系。
-
不设置 Activity 的android:configChanges 时,切屏会重新调用各个生命周期默认首先销毁当前 activity,然后重新加载。
-
设置 Activity
android:configChanges="orientation|keyboardHidden|screenSize"时,切屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法。
通常在游戏开发, 屏幕的朝向都是写死的。
Android8月31日题: hashmap怎么处理hash碰撞?
参考答案:
hashmap利用“拉链法”处理HashCode的碰撞问题;将键值对传递给put方法时,他调用键对象的hashCode()方法来计算hashCode,然后找到bucket(哈希桶)位置来存储对象;当用get方法获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当碰撞发生了,对象将会存储在链表的下一个节点中。hashMap在每个链表节点存储键值对对象。当两个不同的键却有相同的hashCode时,他们会存储在同一个bucket位置的链表中。键对象的equals()来找到键值对。
本文来自博客园,作者:清霜辰,转载请注明原文链接:https://www.cnblogs.com/cnjim/p/18443479
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了