tplink ⼆⾯
TP-Link Java ⾯试
TP-Link 是⼀家专注⽹络通信设备的公司,⽐如市⾯上很多路由器都是 TP-Link 做的,⼀开始我以
为这家公司是外企,后⾯了解到是国内的企业。
TP-Link 有两个 公司,TP-Link 普联负责 国内业务,TP-Link 联洲负责 国外业务,⼯资待遇啥的两边
差不多,都有独⽴的招聘官⽹。
我看了TP-Link 校招的岗位,虽然⼤部分都是嵌⼊式,但是其实也有后端开发的岗位,主要是做管
理设备类的后端系统,⽐如物联⽹后台 系统等等 。
不少同学跟我反馈,他们在上个 ⽉都已经 oc 了 TP-Link ,最近在陆续开奖。我发现能进 TP-Link 的
同学学 历都⽐较优秀的,不少都是 985 级别的同学,要想在校招市场上争 抢这部分优秀的学⽣,
那⾃然待遇肯定不能低。
我根据他同学的反馈,整理了 TP-Link 2025 届校招后端开发岗位的薪资情:
普通档 offer :19k 〜21k x 16 ,也就是年薪30w 〜33w
sp offer :23k 〜25 x 16 ,也就是年薪 36w 〜40w
ssp offer :26 〜28k x 16 ,也就是年薪 41w 〜44w
可以看到,基本是 30w 年薪以上,跟互联⽹⼤⼚薪资差距不⼤,⼯作 base 主要是深圳、成都、
上海。成都毕竟是⼆线城市,薪资相⽐⼀线城市会少 2k , ⽐如深圳/上海⽩菜价: 21k ,成都⽩菜
价: 19k
那话说回来,TP-Link ⾯试难度如何?我看了⼀些同学的TP-Link ⾯经,感觉相⽐互联⽹⼤⼚难度是
低⼀些,⼤部分都是问 10-20 个问题左右,不像互联⽹⼤⼚⾯试动不动就 30 个+问题,当然还是
会要求算法⼿撕的。
这次来分享⼀位同学 TP-Link Java 后端开发的⼆⾯⾯经,主要考察了 Java 基础、并发编程、JVM
这些知识点。

tplink ⼆⾯
说说 对于Java 反射的理解
Java 反射机制是在运⾏状态中,对于任意⼀个类,都能够知道这个类中的所有属性和⽅法,对于
任意⼀个对象,都能够调⽤它的任意⼀个⽅法和属性;这种动态获取的信息以及动态调⽤对象的
⽅法的功能称为 Java 语⾔的反射机制。
⽐如,获取⼀个 Class 对象。 Class.forName( 完整类名 ) 。通过 Class 对象获取类的构造⽅法,
class.getConstructor 。根据 Class 对象获取类的⽅法, getMethod 和 getMethods 。使⽤
Class 对象创建⼀个对象, class.newInstance 等。反射具有以下特性:
- 运⾏时类信息访问:反射机制允许程序在运⾏时获取类的完整结构信息,包括类名、包名、⽗
类、实现的接⼝、构造函数、⽅法和字段等。
- 动态对象创建:可以使 ⽤反射API 动态地创建对象实例,即使在编译时不知道具体的类名。这是
通过Class 类的newInstance() ⽅法或Constructor 对象的newInstance() ⽅法实现的。- 动态⽅法调⽤:可以在运⾏时动态地调⽤对象的⽅法,包括私有⽅法。这通过Method 类的
invoke() ⽅法实现,允许你传 ⼊对象实例和参数值来执⾏⽅法。- 访问和修改字段值:反射还允许程序在运⾏时访问和修改对象的字段值,即使是私有的。这是

通过Field 类的get() 和set() ⽅法完成的。反射的优点就是增加灵活性,可以在运⾏时动态获取对象实例。缺点是反射的效率很低,⽽且会
破坏封装,通过反射可以访问类的私有⽅法,不安全。
那我们为什么 会⽤反射?什么 时候会⽤到反射
在某种业务场景下,⽆法在编写源代码时就确定要⽤哪个类的对象,需要根据⽤⼾的⾏为做出动
态地响应。这个时候就可以考虑⽤反射机制在运⾏阶段根据⽤⼾的输⼊来判断到底实列化哪个类
的对象,并调⽤该对象的⽅法等操作。
例如:在美团点外卖后付款的界⾯,⽤⼾可以选择多种付款⽅式(微信、⽀付宝、银⾏卡等等 )。
假如每种⽀付⽅式都对应⼀个类,⽽在编写源代码的时候我们不能确定使⽤那种付款⽅式,为了
代码的可扩展性,也不 想⽤分⽀结构并为每个⽀付⽅式的类创建对象。那么,这种情况下就可以
考虑⽤反射机制,⽤⼾点击哪个⽀付⽅式,程序就在运⾏阶段创建哪个⽀付⽅式类的对象完成⽀
付。
Java 中线程安全的⽅式有哪些?
Java 中的锁是⽤于管理多线程并发访问共享资源的关键机制。锁可以确保在任意给定时间内只有⼀
个线程可以访问特定的资源,从⽽避免数据竞争和不⼀致性。Java 提供了多种锁机制,可以分为以
下⼏类:
内置锁(synchronized ):Java 中的 synchronized 关键字是内置锁机制的基础,可以⽤于⽅法
或代码块。当⼀个线程进⼊ synchronized 代码块或⽅法时,它会获取关联对象的锁;当线程离
开该代码块或⽅法时,锁会被释放。如果其他线程尝试获取同⼀个对象的锁,它们将被阻塞,
直到锁被释放。其中,syncronized 加锁时有⽆锁、偏向锁、轻量级锁和重量 级锁⼏个级别。偏
向锁⽤于当⼀个线程进⼊同步块时,如果没有任何 其他线程竞 争,就会使 ⽤偏向锁,以减少锁
的开销。轻量级锁使⽤线程栈上的数据结构,避免了操作系统级 别的锁。重量 级锁则涉及操作
系统级 的互斥锁。
ReentrantLock: java.util.concurrent.locks.ReentrantLock 是⼀个显式的锁类,提供了⽐
synchronized 更⾼级的功能,如可中断的锁等待、定时锁等待、公平锁选项等。
ReentrantLock 使⽤ lock() 和 unlock() ⽅法来获取和释放锁。其中,公平锁按照线程请求锁的顺序来分配锁,保证了锁分配的公平性,但可能增加锁的等待时间。⾮公平锁不保证锁分配
的顺序,可以减少锁的竞争,提⾼性能,但可能造成某些线程的饥饿 。
读写锁(ReadWriteLock ): java.util.concurrent.locks.ReadWriteLock 接⼝定义了 ⼀种锁,
允许多个读取者同时访问共享资源,但只允许⼀个写⼊者。读写锁通常⽤于读取远多于写⼊的
情况,以提⾼并发性。
乐观锁和悲观锁:悲观锁(Pessimistic Locking )通常指在访问数据前就锁定资源,假设最坏的
情况,即数据很可能被其他线程修改。 synchronized 和 ReentrantLock 都是悲观锁的例⼦。乐
观锁(Optimistic Locking )通常不锁定资源,⽽是在更新数 据时检查数据是否已被其他线程修
改。乐观锁常使⽤版本号或时间戳来实现。
⾃旋锁:⾃旋锁是⼀种锁机制,线程在等待锁时会持续循环检查锁是否可 ⽤,⽽不是放弃CPU
并阻塞。通常可以使 ⽤CAS 来实现。这在锁等待时间很短的情况下可以提⾼性能,但过度⾃旋
会浪费CPU 资源。
Synchornized 清楚吗?
synchronized 是Java 提供的原⼦性内置锁,这种内置的并且使⽤者看不到的锁也被称为监视器锁,
使⽤synchronized 之后,会在编译之后在同步的代码块前后加上monitorenter 和monitorexit 字节码
指令,他依赖操作系统底层互斥锁实现。他的作⽤主要就是实现原⼦性操作和解决共 享变量的内
存可⻅性问题。
执⾏monitorenter 指令时会尝试获取对象锁,如果对象没有被锁定或者已经获得了锁,锁的计数器
+1 。此时其他竞争锁的线程则会进⼊等待队列中。执⾏monitorexit 指令时则会把计数器-1 ,当计
数器值为0时,则锁释放,处于等待队列中的线程再继续竞争锁。
synchronized 是排它锁,当⼀个线程获得锁之后,其他线程必须等待该线程释放锁后才能获得锁,
⽽且由于Java 中的线程和操作系统原⽣线程是⼀⼀对应的,线程被阻塞或者唤醒时时 会从⽤⼾态切
换到内核态,这种转换⾮常消耗性能。
从内存语义来说,加锁的过程会清除⼯作内存中的共享变量,再从主 内存读取,⽽释放锁的过程
则是将⼯作内存中的共享变量写回主内存。
如果再深⼊到源码来说,synchronized 实际上有两个 队列waitSet 和entryList 。
当多个线程进⼊同步代码块时,⾸先进⼊entryList
有⼀个线程获取到monitor 锁后,就赋值给当前线程,并且计数器+1
如果线程调⽤wait ⽅法,将释放锁,当前线程置为null ,计数器-1 ,同时进⼊waitSet 等待被唤
醒,调⽤notify 或者notifyAll 之后⼜会进⼊entryList 竞争锁
如果线程执⾏完毕,同样释放锁,计数器-1 ,当前线程置为null

image-20250903165803622
讲⼀讲垃圾 回收机制?
JVM 有垃圾 回收机制的原因是为了 解决内 存管理的问题。在传统的编程语⾔中,开发⼈员需要⼿动分配和释放内存,这可能导致内存泄漏、内存溢出等问题。⽽Java 作为⼀种⾼级语⾔,旨在提供更
简单、更安全的编程环境,因此引⼊了垃圾 回收机制来⾃动管理内存。
垃圾 回收机制的主要⽬标是⾃动检测和回收不再使⽤的对象,从⽽释放它们所占⽤的内存空间。
这样可以避免内 存泄漏(⼀些对象被分配了内存却⽆法被释放,导致内存资源的浪费)。同时,垃圾回收机制还可以防⽌内存溢出(即程序需要的内存超过了可⽤内存的情况)。
在Java 中,判断对象是否为垃圾 (即不再被使⽤,可以被垃圾 回收器回收)主要依据两种主流的垃圾回收算法来实现:引⽤计数法和可 达性分析算法。
引⽤计数法:给对象添加⼀个引⽤计数器, 每当有⼀个地⽅引⽤它,计数器就加 1;当引 ⽤失
效,计数器就减 1;计数器为 0 的对象就是不可能再被使⽤的对象。这个⽅法实现简单,效率
⾼,但是⽬前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决
对象之间相互循环引⽤的问题。
可达性分析算法:这个算法的基本思想就是通过⼀系列的称为 “GC Roots” 的对象作为起点,从
这些节点开始向下搜索,节点所⾛过的路径称为引⽤链,当⼀个对象到 GC Roots 没有任何 引⽤
链相连的话,则证明此对象是不可⽤的
垃圾 回收算法有哪些?
标记-清除算法:标记-清除算法分为“标记”和“清除”两个 阶段,⾸先通过可达性分析,标记出所
有需要回收的对象,然后统⼀回收所有被标记的对象。标记-清除算法有两个 缺陷,⼀个是效率
问题,标记和清除的过程效率都不⾼,另外⼀个就是,清除结束后会造成⼤量的碎⽚空间。有
可能会造成在申请⼤块内存的时候因为没有⾜够的连续空间导致再次 GC 。
复制算法:为了 解决碎⽚空间的问题,出现了“复制算法”。复制算法的原理是,将内存分成两
块,每次 申请内存时都使⽤其中的⼀块,当内存不够时,将这⼀块内存中所有存活的复制到 另
⼀块上。然后将然后再把已使⽤的内存整个清理掉。复制算法解决了空间碎⽚的问题。但是也
带来了新的问题。因为每次 在申请内存时,都只能使⽤⼀半的内存空间。内存利⽤率严重不
⾜。
标记-整理算法:复制算法在 GC 之后存活对象较少的情况下效率⽐较⾼,但如果存活对象⽐较
多时,会执⾏较多的复制操作,效率就会下降。⽽⽼年代的对象在 GC 之后的存活率就⽐较
⾼,所以就有⼈提出了“标记-整理算法”。标记-整理算法的“标记”过程与“标记-清除算法”的标记
过程⼀致,但标记之后不会直接清理。⽽是将所有存活对象都移动到 内存的⼀端。移动结束后
直接清理掉剩余部分。
分代回收算法:分代收集是将内存划分 成了新⽣代和⽼年代。分配的依据是对象的⽣存周期,
或者说经历过的 GC 次数。对象创建时,⼀般在新⽣代申请内存,当经历⼀次 GC 之后如果对还
存活,那么对象的年龄 +1 。当年龄超过⼀定值(默认是 15 ,可以通过参数 -XX:MaxTenuringThreshold 来设定)后,如果对象还存活,那么该对象会进⼊⽼年代。Java 中有没有⽤到引⽤技术去做垃圾 回收呢?
没有,当前主流的 JVM 实现中,已经摒弃了引⽤计数法,主要以可达性分析算法的实现为主 ,但
是基本的可达性分析算法也同样存在问题,主要是 STW 时间⻓。
可达性分析的整个过程都需要 STW ,以避免对象的状态发⽣改变,这就导 致GC 停顿时⻓很⻓,⼤
⼤影响应⽤的整体性能。为了 解决上⾯这些问题,就引⼊了三 ⾊标记算法。
三⾊标记算法(Tricolor Marking Algorithm )是⼀种基于可达性分析的垃圾 回收算法。它将对 象状态分为三 种:⽩⾊、灰⾊、⿊⾊,其中:
⽩⾊(White ):表⽰对象未被访问过,即还未进⾏可达性分析。
灰⾊(Gray ):表⽰对象已被访问过,但其引⽤的对象尚未进⾏可达性分析。
⿊⾊(Black ):表⽰对象已被访问过,并且其引⽤的对象也已进⾏了可达性分析。
同时将标记过程可以分为三个 阶段:初始标记(Initial Marking )、并发标记(Concurrent Marking )和重新标记(Remark )。三个 标记阶段中,初始标记和重新标记是需要 STW ,⽽并发
标记是不需要 STW 。
其中最耗时的其实就是并发标记的这个阶段,因为这个阶段需要遍历整个对象树,⽽三⾊标记把
这个阶段做到了和应⽤线程并发执⾏,也就⼤⼤降低了 GC 的停顿时⻓。
怎么避免内 存泄漏呢?
** 内存泄露:** 内存泄漏是指程序在运⾏过程中不 再使⽤的对象仍然被引⽤,⽽⽆法被垃圾 收集器回收,从⽽导致可⽤内存逐渐减少。虽然在Java 中,垃圾 回收机制会⾃动回收不再使⽤的对象,但如果有 对象仍被不再使⽤的引⽤持有,垃圾 收集器⽆法回收这些内存,最终可能导致程序的内存使⽤不断增加。
内存泄露常⻅原因:
静态集合:使⽤静态数据结构(如 HashMap 或 ArrayList )存储对象,且未清理。
事件监听:未取消对事件源的监 听,导致对象持续被引⽤。
线程:未停⽌的线程可能持有对象引⽤,⽆法被回收。
常⻅的内存泄露的例⼦和解决⽅式如下。
1、静态属性导致内存泄露
会导致内存泄露的⼀种情况就是⼤量使⽤static 静态变量。在Java 中,静态属性的⽣命周 期通常伴
随着应⽤整个⽣命周 期(除⾮ClassLoader 符合垃圾 回收的条件)。下⾯来看⼀个具体的会导致内存
泄露的实例:
public class StaticTest {
public static List<Double> list = new ArrayList<>();
public void populateList() {
for (int i = 0; i < 10000000; i++) {
list.add(Math.random());
}
Log.info("Debug Point 2");
}
public static void main(String[] args) {
Log.info("Debug Point 1");
new StaticTest().populateList();如果监控内存堆内存的变化 ,会发现在打印Point1 和Point2 之间,堆内存会有⼀个明显 的增⻓趋势图。但当执⾏完populateList ⽅法之后,对堆内存并没有被垃圾 回收器进⾏回收。

但针对上述程序,如果将定义list 的变量前的static 关键字去掉,再次执⾏程序,会发现内存发⽣了
具体的变化 。VisualVM 监控信息如下图:

对⽐两个 图可以看出,程序执⾏的前半部分内存使⽤情况都⼀样,但当执⾏完populateList ⽅法之
后,后者不再有引⽤指向对应的数据,垃圾 回收器便进⾏了回收操作。因此,我们要⼗分留意
static 的变量,如果集合或⼤量的对象定义为 static 的,它们会 停留在整个应⽤程序的⽣命周 期当
中。⽽它们所占⽤的内存空间,本可以⽤于其他地⽅。
那么如何优 化呢?第⼀,进来减少静态变量;第⼆,如果使⽤单例,尽量采 ⽤懒加载。
2、 未关闭的资源
⽆论什么 时候当我们创建⼀个连接或打 开⼀个流,JVM 都会分配内存给这些资源。⽐如,数据库链
接、输⼊流和session 对象。
忘记关闭这些资源,会阻塞内存,从⽽导致GC ⽆法进⾏清理。特别是当程序发⽣异常时,没有在
finally 中进⾏资源关闭的情况。这些未正常关闭的连接,如果不进⾏处理,轻则影响程序性能,重
则导致OutOfMemoryError 异常发⽣。
如果进⾏处理呢?第⼀,始终记得在finally 中进⾏资源的关闭;第⼆,关闭连接的⾃⾝代码不能发
⽣异常;第三,Java7 以上版本可使⽤try-with-resources 代码⽅式进⾏资源关闭。
3、 使⽤ThreadLocal
ThreadLocal 提供了线程本地变量,它可以保 证访 问到的变量属于当前线程,每个线程都保存有⼀
个变量副本,每个线程的变量都 不同。ThreadLocal 相当于提供了⼀种线程隔离,将变量与线程相
绑定,从⽽实现线程安全的特性。

ThreadLocal 的实现中,每个Thread 维护⼀个ThreadLocalMap 映射表,key 是ThreadLocal 实例本
⾝,value 是真正需要存储的Object 。
ThreadLocalMap 使⽤ThreadLocal 的弱引 ⽤作为key ,如果⼀个ThreadLocal 没有外部强引 ⽤来引⽤
它,那么系统GC 时,这个ThreadLocal 势必会被回收,这样⼀来,ThreadLocalMap 中就会出现key
为null 的Entry ,就没有办法访问这些key 为null 的Entry 的value 。
如果当前线程迟迟 不结束的话,这些key 为null 的Entry 的value 就会⼀直存在⼀条强引 ⽤链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value 永远⽆法回收,造成内存泄漏。如何解决此问题?
第⼀,使⽤ThreadLocal 提供的remove ⽅法,可对当前线程中的value 值进⾏移除;
第⼆,不要使⽤ThreadLocal.set(null) 的⽅式清除value ,它实 际上并没有清除值,⽽是查找与当前线程关联的Map 并将键值对分别 设置为当前线程和null 。
第三,最好将ThreadLocal 视为需要在finally 块中关闭的资源,以确保即使在发⽣异常的情况下也始终关闭该资源。
try {
threadLocal.set(System.nanoTime());
//... further processing
} finally {
threadLocal.remove();
}说⼀下泛型,有什么 好处 ?
泛型是 Java 编程语⾔中的⼀个重要特性,它允许类、接⼝和⽅法在定义时使⽤⼀个或多个类型参
数,这些类型参数在使⽤时可以被指定为具体的类型。
泛型的主要⽬的是在编译时提供更强的类型检查,并且在编译后能够保留类型信息,避免了在运
⾏时出现类型转换异常。
为什么 需要泛型?
适⽤于多种数据类型执⾏相同的代码
private static int add(int a, int b) {
System.out.println(a + "+" + b + "=" + (a + b));
return a + b;
}
private static float add(float a, float b) {
System.out.println(a + "+" + b + "=" + (a + b));
return a + b;
}
private static double add(double a, double b) {
System.out.println(a + "+" + b + "=" + (a + b));
return a + b;
}如果没有泛型,要实现不同类型的加法,每种类型都需要重载⼀个add ⽅法;通过泛型,我们可以
复⽤为⼀个⽅法:
System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
return a.doubleValue() + b.doubleValue();
}泛型中的类型在 使⽤时指定,不需要强制类型转换(类型安全,编译器会检查类型)
看下这个例⼦:
List list = new ArrayList();
list.add("xxString");
list.add(100d);
list.add(new Person());我们在使⽤上述list 中,list 中的元素都是Object 类型(⽆法约束其中的类型),所以在取出集合元素时需要⼈为的强制类型转化到具体的⽬标类型,且很容易出现java.lang.ClassCastException 异常。
引⼊泛型,它将提供类型的约束,提供编译前的检查:
List<String> list = new ArrayList<String>();
// list中只能放String, 不能放其它类型的元素介绍⼀下缓存击穿,缓存穿透是⼀般由什么 系统原因造成?解决⽅式有哪
些?
缓存雪崩:当⼤量缓存数据在同⼀时间过期(失效)或者 Redis 故障宕机时,如果此时有⼤量
的⽤⼾请求,都⽆法在 Redis 中处理,于是全部请求都直接访问数据库,从⽽导致数据库的压
⼒骤增,严重的会造成数据库宕机,从⽽形成⼀系列连锁反应,造成整个系统崩溃,这就是缓
存雪崩的问题。

缓存击穿:如果缓存中的某个热点 数据过期了,此时⼤量的请求访问了该热点 数据,就⽆法从
缓存中读取,直接访问数据库,数据库很容易就被⾼并发的请求冲垮,这就是缓存击穿的问
题。

缓存穿透:当⽤⼾访问的数据,既不在缓存中,也不 在数据库中,导致请求在访问缓存时,发
现缓存缺失,再去访问数据库时,发现数据库中也 没有要访问的数据,没办法构建缓存数据,
来服 务后续的请求。那么当有⼤量这样的请求到来时,数据库的压⼒骤增,这就是缓存穿透的
问题。

缓存雪崩解决⽅案:
均匀设置过期时间:如果要给缓存数据设置过期时间,应该避免将⼤量的数据设置成同⼀个过
期时间。我们可以在对缓存数据设置过期时间时,给这些数据的过期时间加上⼀个随机数,这
样就保证数据不会在同⼀时间过期。
互斥锁:当业务线程在处理⽤⼾请求时,如果发现访问的数据不在 Redis ⾥,就加个互 斥锁,
保证同⼀时间内只有⼀个请求来构 建缓存(从数据库读取数据,再将数据更新到 Redis ⾥),当
缓存构建完成后,再释放锁。未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么
就返回空值或者默认值。实现互斥锁的时候,最好设置超时时 间,不然第⼀个请求拿到了锁,
然后这个请求发⽣了某种意外⽽⼀直阻塞,⼀直不释放锁,这时其他请求也⼀直拿不到锁,整
个系统就会出现⽆响应的现象。
后台 更新缓存:业务线程不再负责 更新缓存,缓存也不 设置有效期,⽽是让缓存“永久有效”,
并将更新缓存的⼯作交由后台 线程定时更新。
缓存击穿解决⽅案:
互斥锁⽅案,保证同⼀时间只有⼀个业 务线程更新缓存,未能获取互斥锁的请求,要么等待锁
释放后重新读取缓存,要么就返回空值或者默认值。
不给热点 数据设置过期时间,由后台 异步更新缓存,或者在热点 数据准备要过期前,提前通知
后台 线程更新缓存以及重新设置过期时间;
缓存穿透解决⽅案:
⾮法请求的限制:当有⼤量恶意请求访问不存在的数据的时候,也会发⽣缓存穿透,因此在
API ⼊⼝处我们要判断求请求参数是否合 理,请求参数是否含 有⾮法值、请求字段是否存在,
如果判断出是恶意请求就直接返回错误,避免进⼀步访问缓存和数据库。
缓存空值或者默认值:当我们线上业 务发现缓存穿透的现象时,可以针对查询的数据,在缓存
中设置⼀个空值或者默认值,这样后续请求就可以从缓存中读取到空值或者默认值,返回给应
⽤,⽽不会继续查询数据库。
布隆过滤器:我们可以在写⼊数据库数据时,使⽤布隆过滤器做个标记,然后在⽤⼾请求到来
时,业务线程确认缓存失效后,可以通过查询布隆过滤器快速判断数 据是否存在,如果不存
在,就不⽤通过查询数据库来判断数 据是否存在。即使发⽣了缓存穿透,⼤量请求只会查询
Redis 和布隆过滤器, ⽽不会查询数据库,保证了数据库能正常运⾏,Redis ⾃⾝也是⽀持布隆
过滤器的。
算法
算法题:⼀个正整数数 组,找出最⻓的不重复的⼦数组
