粉丝27获赞288

大家好,今天我们来学习一种,就是针对任意家务的应用,然后呢进行快速默克的一个这种,而且是无侵入式的配置化解决方案, 然后怎么弄呢?嗯,我们先讲一下,就是我们现在是怎么默克的,比如说有些应用来讲 画个图吧,比如说这也是后端业务系统 啊,有有些系统 这种在被掉进来的时候,呃,掉进来的时候呢?他怎么进行默克呢?呃,其实如果去改代码,大家显得想的就是最简单的方式啊,大家可以用这种,比如说 lp, 用 lp 来干这个事情,但是呢这种的话其实大家是需要你去改造这个业务性的工程,然后这个工程里面你要去定一些 lp, 然后呢需要针对就是每个方法或者每个一样去读一些配置,但是这些东西就是全需要我们在代码里面先写好,而且这个墨客的话,到时候大家 还得去区分环境,就是这个墨客你最好不要流到产线上去,因为墨客来讲就是能干墨客这个事情。就是除了就是我们那种 嗯,限流、垄断、降级那种去返回一些默克的数据之外,基本上对于这种默克来讲都是我们测试需要模拟的一些测试数据,所以 对于 lp 来讲他可以干这个事情,但是大家为了严谨,最好在测试环境去注入这个 lp, 这个时候大家可以利用那个呃 并 fx plus pro cs 来干这个事情,就是当属于某种环境的时候才把这个 lp 注入进去。 呃,其实大家说白了就是当你的环境属于测试环境的时候,你才将这个 lp 注入进去,然后这个 lp 在测试环境,他就能去 针对你的请求进来的时候,当出现异常的时候,或者说我一定要干漠河这个事情的时候,我才会去从某些地方去读数据。好,这个读数据从哪来?对于一些,嗯, 企业来讲,你可以简单操作,就是存储 媒介,这个存储媒介你可以读文件,也可以读数据库,也可以读配置中心,这个就看大家怎么用,反正对于测试环节来讲,只要大家用的方便, 你就放哪里,然后呢?存储每件,你既然要存这个东西,你测试就想测的方便来讲的话,那么你一定到时候会还有一个这样的页面,这个页面来干这个事情 就是页面配置,那么通过这么一处理的话,相当于,哎,我用 lp 好像也挺灵活来干这个事情的, 反正大家测试也挺嗨皮的。好,那如果我的假设我现在是一个业务系统,那我现在我假设有成百上千个应用都需要干磨合这个事情的时候, 那你这个 lp 来讲的话,呃,是不是需要大家都去改代码了?哈?你可能又有人会去想,我把这种 aop 的功能,我把它打成一个架包, 好,我打成价包的时候,嗯,我让对应的业务系统去引这个价包,那你引这个价包的时候是不是还是得让人家去改代码,是不是?所以呢?我这里现在需要,嗯,跟大家讲的就是一种,就是说 我既要实现墨客的功能,我也不需要这个业务系统改任何代码, 就是直接简单配置即可。好,那我接下来怎么弄呢?其实很简单, 就是不知道大家有没有听过这个东西,这个把它断掉,这个没用了, 就是不知道大家有没听过这个加我 aj, 加我代理,这个功能很强大,就是他 有两个主要的方法,嗯,比如说吹闷,然后呢?还有一个背见的闷, 这个是他定义的规范,这个就不需要去纠结他的名字为什么是这样的。然后呢?这个 pramam 的话, 他顾名思义就是在没有方法执行之前,比如说我们的 suv 不特应用的话,那 我们有一个慢方法,就是去正式启动,然后普瑞曼呢?就是在慢方法执行之前 之前之前是哪个之前呢?其实是在程序的时候把我们的驾包里面的文件加载到 加载出来的时候,正准备去漏的。呃,生成真正的符号引用这些事情的时候,然后呢 还可以对他做一些修改,修改完了之后,这个时候才可以正式放到内存里面去。然后 a 性成分的他 这个比较特殊,他特殊的是在于就是说 pro man 他只是在卖方法之前来干这个事情,然后 aj 的 man 呢?他可以在我的这个业务系统运行的时候 来对我的业务系统某些类,就是可以说是任何类进行重定,重庆定义,或者给他增加一些什么, 呃,方法呀,内容啊什么之类的,所以他这个还是有一点强大的。所以呢,我既然想干墨客这个事情, 所以我可以这么弄,就是比如说我采采用 pen man 来干这个事情的话,那我在我程序启动的时候,呃,我就可以将这个 有配慢的,哎,我这里写个加薄吧,这样我到时候讲的时候方便点,就是 比如说我这里有一个,嗯,墨壳 a 检测点价,好,我有个墨壳 a 件的点价这个包, 那这个包的话,呃,到时候我们只需要在加瓦干价,比如说干我的 app 点价在前面,我加瓦 a 检测, 然后叉叉什么某个目录,然后莫可干 a 检测点驾, 我只要在我的启动命令里面干这个事情,那么我就能加载这个价。那加载这个价的时候,他拍卖方法里面他有 有两个方法,一个方法是有那个也是穿因,就是那个叫叫叫叫叫因,是抓因,是 抓特抓选抓特闷, 可以选一个一个这样的类吧?这个类里面其实就是能对嗯,我们的类进行重新加载,就是他可以将 一个卡死文件然后加载进去,也可以就是针对某些类进行再次修改,修改完了之后再次加载。 好,那我们讲就是朋友们里面有这个方法不?有有有,有这个变量,那这个变量 我要执行 proman 的逻辑,那这个逻辑呢?他可以干这些事情,就是执行逻辑之前我可以去看一下我的存储每件里面有多少默克的配置,然后呢? 我拿到这些所有的默克的配置之后,然后我再来看我所有加载了加载拿到的所有的卡是有哪些。我这里有个集合 a, 这里有个集合 b, 我就看我的集合 b 有,就是相对 a 和 b 取交集吧,取完交集之后我要针对剩下的人那些 累或者方法做我的逻辑,其实做逻辑就比较简单,怎么简? 就是我既然能拿到这些类了,那我拿这些类其实我就能拿到他的所有的方法,拿到所有的方法之后,我就能在方法的前面加一些逻辑,然后方法的后面加一些逻辑,然后可到时候又有人会问,就是我怎么去给他方法前面加逻辑和方法后面加逻辑的,呃, 大家推荐大家用加瓦四层这个东西的话,嗯,比较人性化的操作。呃,他底层封装了一些 a、 a、 s、 m 比较难懂的一些 api, 所以你用加 ys 的话,他可以去,比如说银色的皮肤,银色的阿福特,然后 a、 d、 d, case 这些 app 都能帮你来针对某些类的某些方法前后来干 一些事情。就是当你这么操作之后,呃,是不是最终走完之后?以前就是以前那些累,他是没有新增的一些 方法,但是经过存,经过存储媒介里面拿到些配置之后,给他加了一些方法之后,就是变成一个新的类,变成一个新的类,然后就通过他他就可以加载进来。那加载进来之后下次再调接口的时候,其实 你认为以前的 a 类似 a 的这个文件是没有一些什么前面的逻辑,其实现在已经天然就有了。当你去 掉这个 a 类的接口的时候,爆了一场之后,他一场里面他还能做一些逻辑。就是, 呃,你的逻辑就是我要么就返回我一些默克的数据,要么就是我怕遗产,这些都可以通过存储每一这个配置里面该怎么来定义好?当我正正经经把这个默克 a 键的 价包然后写完之后,其实对于我一个后单业务系统,我要想去实现我的墨客功能的话, 其实我是不需要改代码的,我直接通过这种命令来搞定就是了。然后假设我后面还有成百上千的业务系统也需要干这个磨合的事情的时候,我只需要让运维写个下脚本,然后把 所谓的应用把这种命令给他添加一个这样的,嗯,价,价格就是价位 a 进程,这样的话我所有的应用都天然支持这种功能,只是说人家怎么配, 那配的话,那到时候这个纯种媒介就可能需要大家去想办法,就是让所有的业务系统都能去这到这个地方去配,那你到这个点配。没关系,反正我每个应用我都能去纯种媒介拿到我当前应用的那些配置,然后就 ok 了, 然后即改即生效。 呃,这里要提到一点的就是说,呃,这里是在,嗯,我们启动的时候会把这些配置加载进来,然后 我刚刚说的就是几改的,几改其身下是需要我们另外一个啊 aj 的闷来干这个事情,然后呢? aj 的闷的话, 你想干的这个事情其实还需要借助另外一个类,呃,就是就是虚拟那个意思,然后, 哎,就是加把虚拟机的意思吧,那个单词我也不记得写了,没信, 我不记得起了,就这样,就是加瓦虚拟机的意思,加瓦虚拟机的意思。这这个加瓦虚拟机呢? 他有几个方法,比如说他有 app 啊,应该是还有个 titty 还是 addthit 区了,我不记得了,然后还有个 add a 检测,呃,他这两个 app 是干干什么事情的?就是,呃, 比如说你这个业务系统正在运行,然后呢?我我还有一个应用程序, 没错,硬 应用程序 b, 应用程序 b 他跑的慢,方法, 他跑的慢,方法,他就会调用 worch 迷信 呃来做一些 api 的操作,做这些操作的时候,他就会把我的麦克 a 检测去 add 进来, add 进来之后,但是想你 add 到哪里去呢?这个时候而探险就有用了,而探险就是说我要探索到这个后端业务系统这个进程, 那然后大家想我探索到这个进程了之后,我用的 a d d a 卷头,那么我这两个 a b i 一操作完之后,其实我就能把这个摸个 a 卷头,然后怼到这个业务系统,然后真正怼到怼进去之后,他 aj 的闷的方法会执行,那执行的时候我又能去把村子,村子每一件里面东西拿出来,拿出来,然后去刷新里面的一些类,那这样的话 a 型的闷才能帮我们干这个运行时之后的一个实时更新的一个这样的动作,而普瑞闷只能在启动时刻干 这个事情。好,那今天讲的就是怎么去针对我的加瓦宴去快速默克,而且要做到无侵入式的配置,这种其实不管是在嗯 嗯一些小分子,或者说在一些大分子来讲,就是这种操作其实对我们测试很有用, 而且对于开发去模拟一些场景,就是特别是在连条的时候啊,比如说上有他写的很快,你下有没写完,你只是把借口定到那的时候,那下有什么可能等你下有写完才能干这个事情?那么没关系,我漠河的数据一 怼上去,那么上一个他就开始自己慢慢去验证一些功能,当你下游写完的时候,其实差不多真实的数据也出来了,到时候我把再把这个幕后关掉,然后就可以正常的连条了。好,那今天就讲到这,谢谢大家。

下面我们看自定义方法。什么是方法?我们以前说过,方法就是函数,方法就是功能。你比如说我们以前接触过主方法对吧? 那么我把这个一加到一百,这个功能写到方法里面,那么我运行主方法就可以输出结果。 那么今天呢,我们接触自定义方法,大家看我们这个主方法啊,那么这个功能啊,我们确实是实现了,但是他有个缺点, 这个功能他不能重用。所以今天我们讲自定义方法的目的,就是为 为了让方法重用。我们知道方法是我们这类的一个行为对吧?啊,是类的一个行为, 那么这个行为啊,我们要重用是吧?啊,我们要重用这个行为啊。所以啊,我们呢有自定义方法啊。那么这个自定义方法呀, 他包括如下内容啊,无参数的无返回值的方法,无参数的有返回值的方法, 有参数的无返回值方法,有参数的有返回值方法。还有和引用数据类型相关的方法。那么它包括返回值是 引用税类型,参数是引用税类型。那么我们学习啊,啊,最好有个顺序啊,他是足部的加难的啊,你比如说有餐有返回值,这方法是最复杂的,那么再加引用税类型啊,这方法就更复杂了啊。 首先我们看无参数无反馈值方法,那我们看语法修饰服, 因为他是无返回之的,所以我们用 word 来修饰。然后方法明。那么这个修饰服啊,就是帕布里克 private pet 是吧? 好了,这个方法呀,我们说我们要用对象来调用这个方法是吧?对象点方法明。 那么这里我给大家举个例子是吧,这个计算机, 那么计算机啊, 他有运算的功能是吧?比如说我计算一百到九百九十九之间取钱花,这以前算过是吧?那么我把它做成个方法,叫迪斯普勒这个方法 我把做成,我把它做成这个方法,那么这个方法呀, 就可以重用啊。那我怎怎样调用这个方法?我生成个对象,然后这个对象调用这个方法, 那么我再生成个对象,另外对象还可以调用这个方法,这样啊,这个方法就得到了重用嘛。我给大家演示一下, 你比如说这个一加到一百是一样的道理对吧?一样的道理,我把它注上啊, 那么我把它做成方法是吧,把它做成方法 啊。你比如说我把它做成这个卡扣雷特,这个方法 叫卡口里的计算 做成这个方法, 这个我做过了啊,我为了节省时间,我就复制了。 那么大家下去自己啊,一定要亲手敲一边啊, 我就不带他敲了,这以前呢,也敲过了是吧,就不带他敲了 好了。 那么你看我把这个方法做成一个,没有参数,没有反馈值,这么个方法,他是公有的, 那么这个方法呀,就可以重用,因为我们这对象啊,可以生成多个对吧?这对象可以生成多个。 如果呀,你按照我们原来的写这种写法,把它写到主函数里对吧?那么他不能重用。这是第一第二呢。主函数啊, 他最好作为一个启动函数对吧?我们这个主函数最好作为 触发一些功能是吧,运运行一些功能,而不是写具体的代码,那么主函数应该干这个是吧,对吧? 好了,那么我们比如生成个对对象啊, 这是第一个对象,给他起个名字叫第一啊, 那么这个对象啊,他就会有这个自定义方法,他就会有这个行为 啊,就会有这个行为,那同样道理,我们还可以搞第二对象, 对吧?所以啊,这个方法呀,可以被多个对象进行重用。那么你像你把代码直接写到这里了对吧?那么这个主方法,或者说主线程, 这个主线城市不能被重用的这个东西啊,他是不能被重用的, 他虽然都是五千零五十,但是意义不一样。那么举个例子是吧,那么我这个类理啊,我就可以运行, 我就可以调用你这个方法是吧?啊,就可以调用你这个方法。


没跑路啊兄弟们。今天是加挂实习的第十一天啊,感觉还行。呃,昨天不是说给了我一块业务吗?因为我们要换那个技术站吗?从那个 v o e 二换成那个 v o e 三, 然后今天写那个 v o e 三的时候啊,就是怎么说呢? v o e 二本来就写不明白,然后又写 v o e 三,就容易写串,你知道不?然后他那个 v o e 三,他那个就是 他前端,他前他就是他那个前端返回,他不有请求是用那个 d e f h t d p 请求,他那个成功默认返回的是那个 result, 他 直接返回那个 result 就是 什么扣的之类的,就是都没有了, 然后就这些地方我调了半天,然后给大家都问傻了。然后就是呃,他跟之前的那个 v o e 二,那个阿亚克斯那个请求不一样,他是把那一一串东西都给你返回出来,但是就是用那个 d e f h g d p, 他 就会给你返回 redo 的 只范围内个 redo 的 数族里东西。所以说你娶对象的时候是娶不到的,所以说你在点塞塞的时候就无法判断,然后前端就没有办法进行一个回血, 你把这这非常要注意,反正我是踩坑了。呃。然后再就是写后端嘛,后端就是因为他要配多数据库和多数据源嘛,他需要就是有两套 mac, 两套 mac, 但是用的都是。 呃,一张表,一个是那个,一个是那个马拉松口的,一个是奥瑞扣的,然后他需要就是奥瑞扣的,需要单独的写那个数据库语句 就是反正因为他那个表很多,就是得有百十来张,他的语句也没有,就是多特别多的能那个导出来,呃,再就是呃写那个, 呃,就比如说,嗯,你想把那个,把那个信息,把那个 talkin 跟那个多租户 id 给他做一个就是同步吧。呃,反正今天,今天开会,呃,我们说了很多关于这个的,但是,但是就是, 呃他俩就是有有一个失效,一个没失效,你知道吗?就是这种感受,呃,很恶心,呃,反正今天就干了这么多吧,拜拜。

上期我们跑通了 ai 对 话,但是你没有发现两个问题,第一,回复要等好几秒才能一次性出来,用户体验很差。第二, ai 不 记得之前说过什么,每次对话都是从零开始,今天我们把这两个问题都解决掉。本期的目标很明确两件事,第一,流势输出,让 ai 像 chat gpt 那 样一个字一个字的蹦出来。第二,上下文记 多轮对话, ai 能记住你之前说过什么?先说流式输出的原理,普通调用就是靠方法,你发送请求,等它生成完,服务器把完整回复一次性返回,内容越长等的越久。流式调用用 stream 方法,服务器边生成边推送,你 这边边收边显示,底层用的是 sse, 全称 server sent events, 服务器主动推送事件,浏览器原声支持代码怎么写?在 chatcontroller 里面加一个新的接口, get mapping 助捷路径是斜杠 stream produces 设置成 text event stream value, 返回类型是 flux, 监控号 stream, 这是响应式流方法,题里把之前的 call 换成 stream, 就 这一个单词的区别就这么简单,流式输出搞定了。解释两个关键点,第一, produces 等于 text event stream value, 这是告诉浏览器我返回的是 s s e 流,你 也等响应结束再显示,收到一点就渲染一点。第二, flux 是 spring web flux 的 核心类型,代表一个异步的数据流,光用 c u r l 测试不够直观,我们加个简单的测试页面,在 static 目录下新建 index html。 代码很简单,一个输入框,一个按钮,一个显示 区域,核心是 event source, 这是浏览器原生的 s s e。 客户端 on message 回调里把收到的数据追加到页面上,启动应用浏览器,访问 local house 的 八零八零,输入消息点,发送内容,一个字一个字蹦出来,这 就是流逝输出的效果。流逝搞定了,接下来解决记忆问题。现在 ai 每次对话都是独立的,你告诉他我叫张三,下一轮问我叫什么,他完全不知道怎么让他记住。用 chat memory chat memory 是 什么? 你可以把它理解成聊天记录存储,把对话历史存起来。 conversation id 是 绘画 id, 用来区分不同用户的对话。 message window chat memory 是 滑动窗口实现,只保留最近 n 条消息,防止内存爆掉。滑动窗口这个思想我在算法系列的八到十二级专 专门讲过,感兴趣的可以去看看。然后创建一个配置类, chat config, 点 java, 加上 configuration 注解,定义一个闭方法,返回 chat memory。 用 message window chat memory 的 builder 模式 max messages 设置成二十,保留最近二十条消息, 一般就够用了。接下来改造控制器构造方法里除了 chat client builder, 再注入 chat memory。 构建 chat client 的 时候,用 default advisors 方法传入 message chat memory。 advisors advisor 是 spring ai 的 拦截器机制, chat memory 就 通过它实现的。接口方法也要改一下,加一个 session id 参数,用来区分不同用户的对话。调用的时候用 advisors 方法把 session id 传进去。 conversation id 是 一个常量,直接静态导入就行。 流式接口也一样,加上 session id 参数和 advisors 配置代码结构完全一样,就是 call 换成了 stream。 看一下完整代码, ctrl 了,就这么多 操作方法,注入 chat memory 接口方法,传 session id, 核心就是 message chat memory advisors, 它会自动把历史消息拼到 prompt 里面,发给模型测试一下多轮对话,回到浏览器,先发送我叫张三,再发送我叫什么, ai 会回答你叫张三。它记住了,注意 session id 要保持一致,不然就是两个独立的绘画 页面里我们用了时间戳生成了一个固定的 session id, 所以 同一个页面内的对话都能记住。说几个常见问题,第一,流势没有效果,可能是浏览器有缓冲,换个浏览器试试。第二,记忆不生效,检查 session id 是 不是一致的。第三,内存溢出,把 max messages 调小一点。下期我们要做一个完整的聊天界面,还要支持 thinking 模型,让 ai 把思考过程也展示出来,像 deep sea 二一那样。有问,其评论区见,有问必答。

下面我们来看一下立扣的四十九道题目,字母异位词分组看一下题干,给你一个字母串的输入,请你将字母异位词组合在一起,可以按任意顺序返回结果列表,那这边有一个词叫做字母异位词,什么叫做字母异位词呢?我们可以把我们的这个输入拿过来看一下, 有这样的一个输入,那最终的结果,我就拿一个列表出来啊,就拿一个列表出来看一下,拿一个列表出来,它是这样子的一个,那我们能看到它有什么特征?它都是由三个 元素组成,对,三个字母组成,然后他的组成是相同的,对不对?他的三个元素是一样的,那他只是通过了重新的排列组合得到了一个新的字母串,那我们称通过相同字母重新排列组合得到的字母串,就叫做字母意味词。 字母异位词指的是我们的相同元素通过排列组合啊,然后通过排列组合得到的字母串啊,就叫做字母异位词。然后给一个案例啊,就是这个样子的 看一下,就是这个样子。好,那么它的本质我们能看到的是什么?就是它的字母,它组成是完全相同的,那它的顺序是可以进行排列组合是不同的,对不对? 好,那我如何要去做这道题目呢?我们来看一下,它就是通过字母意谓词,把字母意谓词的一组放到一起,对不对?比如说 e, a t t a t a n, t a n, 不是 放在一起的,那我就另起一个列表,对不对?我另开一个列表,那 at e, 是 的,那我就放进去,我这个列表对不对?那他的,那我这是不是在说的过程当中,我是不是就是在已经进行一个分组了?我如果把它通过一个唯一的特征,那我就放到一个组里,特征不同,我就另开一个空列表,对不对?去放它,是不是这样子? 那我如何去做呢?是不是就是我需要去将每组的意谓词找到一个共同特征啊?那找到一个共同特征,找到一个唯一的标识,去标识这么一个字母意谓词这一组,然后如果说他的他是一样的,那我就把它放到一个组里面,对不对? 那我如何去做呢?我们之前有讲到过一个数据结构, map 的 数据结构,大家还记得吗?它是 key value 的 结构,之前一直在讲这个 map set 这种哈希表的结构,哈希表的这个数据结构对不对?那我们用的最多的,其实用的其实挺多的。 map 对 不对? key value 的 结构, 那这边的 key 我 放什么呢? key 是 不是就是放我们的特征标识,对不对?是放我这一组,能够识别出我能够放到这一组的依据,对不对?每一组异类词我们需要去找到这么一个标识,我的 key 就是 标识,而我的 value 就是 说就是放进去,我就把这同组的异类词放进去, 能不能理解?比如说 at 啊?比如说 a t e 这个 a t e, 我 通过某种 key, 我 啊,我 a t e, 因为它前面没有这个 这个 key, 对 不对?那我就是说 a t e, 我 识别到了一个 key, 那 我就另开一个这个列表,然后放,把这个 a t e 放进我的 value 里面,对不对?那我就放进去了,那放进去之后呢? 放进来了之后呢?放进来了之后,下面一个是 e a t 对 不对?那我 e a t 根据某种 key, 某种标准看啊,我这个 key 跟 e a t 是 符合的,一样的,那我这个 key 跟 e a t 是 符合的,一样的,那我也放进我的列表里面,从而 key 也是一样的一个操作,放进去,最后得到了这么一个 value 的 列表,是这样子的,对,可不可以?是不是可以?那如何去找这样的一个唯一的标识去?标识能够作为我们分组的依据呢? 怎么做?这边我们给出两种解决方案啊。第一种解决方案就是利用排序法,那么排序后的次序串就是作为我的 key, 作为我的键啊,通过排序啊,这个排序跟那个排序可不一样啊,这个排序就是说我通过排序去标识我的 key, 能理解吗?就比如说它的这个 a t e e a t t e a, 它排序完应该是 a e t 对 不对?应该是 a e t, 那 我就拿这个 a e t 作为我的 key 啊,就拿我的 aet 作为我的 key, 然后去查找啊,我的 aet 跟排就是拿 ate 拿过来排序,那排序跟 aet 一 样吗?一样,那我就放进我的 byte 值里面,那 eat 排完序也是 aet, 那 么也放进我的 byte 值里面,那如果是 tan 呢?那是不是 an t? 那也放进。呃,看一下 key 一 不一样,跟 aet 一 样吗?不一样,那我就另开一个,对不对?那我就不放到我的这个 value 这里面,不放到我 aet 相应的 value 这里面,可不可以?是不是可以的?好,如何去实现?我们来看一下。首先我要去创建哈希表, 接下来我的 key 是 放我排序后的字串, 而我的 value 放的是,而我的 value 放的是意谓词。每组意谓词,也就是意谓词的列表,也就是对于我的结果集,意谓词列果列表的结果集。接下来我们去进行一个负循环, 在这个做之前,我需要将 string 类型改成 char 序组啊,因为它排序它是不能够,因为 arrays 类的 sort 方法它只能够排序组啊,它 比如说 int 序组啊, char 的 序组啊,啊,字母序组它不可以排 string 类型啊,它就是字母序组类型,它是会翻译包错的,所以我前面必须得给它转个类型, 就叫它 char area, 我转个类型转完类型之后呢?然后我再去进行 sort, 然后我对字母数组进行排序之后,它最终的因为它意味词的排序结果是相同的,那我就把这个作为我的 key, 对 不对?就把这个作为我的 key, 那 我需要去定一个 key, 定一个 key 值,去接收我的 string 类型,接收我的程序之后的结果。 然后我们做一个判断,如果说我的 key 它不存在于 contents key, 如果我的 key 它不存在于,如果我的 key 它不存在于我的哈希表当中,那我就出示画我的空列表,我就出示画我的空列表, 那如果说存在呢?如果 key 存在,那我就需要去把它给放进去,对不对?我 get a key, 然后呢?去把它放进我的,放什么 是放 s 啊?对吧?放进去,然后我直接去天津添,把这个放进我的这个哈希表当中,把我的 key 放进,把我的 s 放进相对应 key 的 value 值当中,对吧?然后最后我们 return。 聊天的时候要注意,因为我需要去把这个重新包装一下。重新包装一下, 为什么要重新包装一下呢?因为因为我的 map 点 values 就是 我的 map 点 values, 我 它的返回类型是,它的返回类型是 collection 类型 是 collection 类型。我需要把它包装成。需要把它包装成 list 啊,非要把它包装成 list 才能够进行返回,因为它最终需要的就是 list, 对 不对?好,然后我们这样子就完成了。我们来看一下, 看一下对不对?不对,是不是少了一个? ok, 这样子就完成了,这样子就完成了。那 string 拼错了, s, e, r, n 键, string 拼错了,这样子就可以了,对吧?那么有没有什么其他的方法呢?有没有什么其他的方法?这种方法就是说我将字母串进行排序,对回排序后了的字母串作为唯一的标识,然后再进行一个分读。还有没有其他的做法? 这样子是可以的,这样子也是可以的,都是对的。然后这样子也是不错的一个解法,对吧?那还有什么其他的方法? 这边我再介绍一种方法。这边我用,这边我就不删了,我就用 python 写吧,我用 python 写。呃,第二种方法叫做计数法, 什么意思呢?嗯,这边用的应该是这个计数法。计数法它的意思就是说我去统计每一个字母串中二十六个字母对不对出现的次数,然后用计数的结果作为唯一的标识。什么意思啊?什么意思?就比如说, 比如说是 e, a, t 啊,比如说 a, b, c 吧,比如说 a b c 和 b c d。 嗯, b c, b, c, a, a, b c 和 b c a。 它我最终它我需要的是我给它一个数组,我给它一个数组,我给它一个列表,存放我的这个 a 到 z, a 到 z 的 这个计数。比如说零,刚开始都是初设都是零,零,零,一直到 a 到 z 都是零啊? 比如说这是 a, 对 吧? a, 然后 a 到点点点点零, a 到 z 都是零, a 到 z, 它都是零,对不对?然后这是 b 的 位置,然后这是 c 的 位置, abc 它都是零,那所对应的我这个 a、 b、 c 呢? a、 b、 c 的 话,是不是就是这个位置记一,这个位置记一,这个位置记一,也就相当于我拿一个列表出来去标识唯一标识我的这个 a、 b、 c, 对 吧?唯一去标识我的 a、 b、 c, 唯一去标识我的 a、 b、 c, 那 这个其实也是一样的, b、 b 的 位置记一, c、 c 的 位置记一,然后 a、 a 的 位置记一,对不对?可不可以这样? 是不是可以这样子?然后将唯一的这个标识去标识我的 abc, 标识我的 bca, 那 当这个标识相同的时候,是不是就可以相当于它计数的结果相同的时候就是什么?我就把 abc、 bca 放到一组里面,那其实这样的一个思路其实也是什么?先要也是要使用哈希表进行这个分组,对不对?那前面不同的是什么?不同的其实就是它标识的 不一样,对不对?它标识的不一样,它用什么去标识?是不是用技术的结果去标识,对吧?那我们这边来写一下,用技术的结果去标识,我就把它叫做 enigma, enigma, 因为我需要一个结果 enigma 去存放我的,去存放我的结果,去存放我的结果。 首先我需要去循环我的给的 strings, 它用的是冒号。首先我要出水画长度为二十六的列表,对不对?因为我 a 到 z 嘛?而且因为它是小写啊,因为它是小写,我们可以看到它的只只包含小写的,只包含小写的。那后面我会说,如果它有大小写怎么办? 我先出水画一个列表,出水画一个列表, 抽象一个列表,然后呢?我去便利去计算它的每一个位置的缩影啊?每一个字母的缩影相当于什么意思呢? 每一个字母的缩影,比如说 a 对 应的是零, b 呢?对应的是一,这是位置啊,这是缩影。字母啊,也就相当于我将字母, 我将字母去映设为,我将字母映设为缩影,我将字母映设为缩影。 b 到 b 是 一,对吧? c 是, 然后一直到又又又调到那个东西, z 就是 我的二十五,对不对? z 就是 二十五,那如何去进行这个?我怎么怎么怎么去做呢?我怎么去做?我这边把 s 里面每一个元素都取出来,然后进行便利,然后我要做的操作是将字母映设为缩影,如果说我取出来的这个值, 然后我再到列表里面去找,找到了之后我加一,是不是这样的操作?那我怎么怎么去做呢?是不是我可以利用它的阿斯玛值?如果说它因为 a 的 阿斯玛值是九十七,对不对? a 的 阿斯玛值是九十七, 那如何映射为零呢?是不是减去九十七?那 b 是 不是减 b 的 话是九十八?减去九十七就可以了,然后 z 的 话减去九十七,二十五,是不是可以这样子?这样子就可以好做一些。 呃,我们拿当前位置的 asc, 当前元素的 asc 码值减去 九十七啊,或者说减去当前就是减去第一个值嘛?第一个值是 a 嘛?就九十七的,就 a 的 asc 码就是九十七,对吧?然后如果说它,比如说我 count 目前,比如说是 a, 对 吧?那也就是说我拿 e 的 位置,拿 e 的 位置,也就是 a 的 位置,对吧?字母 a, 那 就可以在这个位置就加,等于就加一嘛,我这个位置就计数为一嘛,对吧?然后如果说它出现第二次,那我就 二次出现的话,就是再加一,也就是一加一等于二,是不是这样子,对吧?那么这边后面,那我计数完了,对不对?我拿到了一个列表,那这么一个列表,我要将它作为我的 key, 我 要作为我的 key, 有 一个条件 t 的 话要作为作为,要有一个条件,就是它必须,它是不是必须得可哈希啊,对不对?那可哈希的话,我的列表可哈希吗?列表不可哈希,对不对?而什么可哈希在 python 里面是不是原组可哈希啊,原组是不可变的,对不对?那我是不是需要去转换一下它的类型? 就要转换一下它的类型,我需要把它变成 tempo 类型,将我的 count 变为这样子的话,它作为我的 key, 这样子 tempo 类型,它作为我的 key 是 可哈西的,那它就可以作为我的 key, 作为我的键, 是不是?那如果说我的 key not in 我 的这个列表里面,不在我的这个结果集里面,那么跟 java 是 一样的做法,就刚刚的呃,排序法是一样的做法,我就把它进行一个抽象,一个空列表, 那如果说它存在啊,这么一个 key, 我 找到了这么一个 key, 那 么我就加进去, 用 append, append s, 对 不对?这个 append s 我 把它加进去,然后最后 return 一下。 return 的 话要注意一下它的类型啊,应该也是 list, 因为我的 erratic 点 values, 它最终它这个结果拿出来的这个结果的类型你还记得吗? 它拿出来的类型是什么?它拿拿出来的类型应该是,比如说我 type 一下, type 一下应该是,应该是 ticketing value, 有 意义。 ticketing values 类型应该是 ticketing values 类型,所以说这个类型它不符合我最终要输出的列表类型啊,最终的输出的列表类型,所以说我需要把它转一下类型,这样子就可以输出了,对不对?好, 那这两个有这样子就写完了,我看一下提交一下,可不可以啊? ok 的,是可以的,是可以的,这样子是可以的。 ok 的, 那有这两种方法,这一种方法是计数法,还有一种用的是加法,写的是排序法啊,一个是用我的这个 字母串排序啊,排序后的字母串作为唯一的标识,然后再进行分组吧。那还有一种方法就是使用我的啊,统计每个字母串当中二十六个字母他的出现的次数,然后用计数的结果作为我的唯一的标识,然后用这个标识去作为我的,就作为我的 key 嘛,然后再用计数结果去进行分组,作为分组一级进行分组,这样的方法都是可以的,都是可以的。 那有时候哪个是最优解?那个是最,那要看情况。那其实说如果说你的数据量体量很大的话,那我觉得我更推荐于啊。第二 啊,技术法啊,我更推荐于技术法,因为如果说他的因为他的复杂度跟他的时间复杂度是要比排序法要来的低的,因为他技术技术法的时间复杂度应该是 o n k, 他的效率更高啊,他效率更高,因为因为加法他他这种方法就是说不是加尾排序,是排序法这个操作,他多了一个排序啊,他多了一个排序的这个复杂度,时间复杂度,因为他有一个 k log, k 的 复杂度多了一个额外的开销。所以说如果说体量数据体量比较大的话,我觉得 python 这个技术法是不错的,还是可以,还是略胜一筹,效率更高。那这边有几个问题啊, 如果说题干,因为他题干说了嘛?他说仅包含小写,我刚刚题目写的时候我也说仅包含小写,那如果说因为词当中有大写怎么解决呢?啊?有可能会问你啊,如果说他他要大写呢?他有大写又有小写,那怎么去区分呢?比如说我输出是这个样子的,对不对啊?我就拿我就拿这边, 就比如说我现在我,我是 a, b, c, a, b, c 这样子呢? 如果是这样子呢?如果是 a, b, c, a, b, c 这样子,那如果有大写字母,呃,然后要求是,呃,就是说这两个大写字母是算意味词的,就是说 a、 b, c 也好,三个 a、 b, c 都是大写也好,它们是算意味词的,它们算意味词。那我怎么去进行判别呢? 我是不是可以怎么做?我是不是可以先把它们全部进行小写,对吧?我要把它变成小写,我把它键变成小写了之后将所有大写字母转化为小写之后再进行判别,判别完了之后是不是就可以了,对不对? 对不对?是不是可以?就比如说如果是这种,这个如果是 python 里面,我在哪边要进行变化?比如说计数法当中我要变什么?比如说计数法当中我要变什么?计数法当中我这个大小写的话是在 在这边,是不是在这个后面?或者在这边,在这边要加一个 lower 的 操作点 lower, 用 lower 函数去将所有的大写字母转化为小写,然后再减,减了之后就可以了。然后还是不管是大写的还是小写的,我都已经转化为这个 c, 我 已经全部转化为小写的了, s, 对 吧? 当然这边要改一改了,这边不是 s 了,这边的 s 应该要变,比如说,比如说要变成 s e, 要变成 s e, s e 去接收 lower 函数所变成的小写字母,然后再去便利小写字母,然后便利小写字母之后还是对应的一样的缩影值,还是二十五的缩影,对不对?然后如果说是 java 呢? java 在 哪里变? java 的 话就是在,嗯,在排序之前还是排序之后变啊?那它好像对在排序之前变,对吧?用 character 它变成小写了,对吧?然后呢,我要去进行一个变写,变,把它呃小写,那小写的话就用 character 类当中的静态方法 to lower case, 用 to lower case 去进行小写,那小写完了之后再进行排序,对吧?这样子是可以的。 那么如果说这个是大小写不敏感,那如果说大小写敏感呢?也就是说如果我的 abc 和 abc 就 大小写,它,比如说 abc 和 abc 它不认,也就是说它大小写敏感,它不算异位词,它们两个根本就不算异位词,怎么办?那我就不需要转化为小写了,大小写对不对?就是小写和大写不需要转化了,那我直接处理是不?直接处理怎么处理? 我这个怎么处理?因为大小大小写它不,它敏感,那我就怎么做。是不是我需要把大,比如说大写是二十六位,大写是二十六,对吧?小写也是二十六位, 那 a 到大写的 a 到 z 是, 比如说我要把它变成五十二位,对吧?我把它变成五十二位,那,那这边要改一改了,对吧?是大写的 a 是 零,大写的 b 是 一,大写的 z 是 二十五,然后小写的 a 是 从几开始?是从二十六开始,然后一直到小写的 z 是 五十一,对不对? 对吧?这样子的话,我就把也是赋,还是一样的思路,就是中间有一点小变化,就是把赋串啊,讲字母映设为缩影,把这个缩扩展它的数组的长度啊,扩展至五十二位,然后我再去进行处理,然后还是一样的。就是计数嘛,对吧?那这个排序法怎么去做呢? 排序法怎么去做?那排序法其实也是一样的,就是排序法,就是它根本就不需要直接对原原字图去进行排序嘛?也是一样的排序,那排序它如果说相同,它根本就不需要做任何操作,对吧?如果它相同,那我就 ok, 那 我就可以,那如果不同,那就 pass, 就是 一样的。那就是说开一个空列表,对吧?其实这个就不需要动了,就是它根本不需要动, 因为它显示的不同,那它就肯定会识别出,那我就不是,我这个 key 就 不一样,那 key 不 一样,它肯定不会分到一个组里面,对吧? 所以说这是两种方法,也就是说他提干当中意味词如果他有大小写之分,并且他这个大小写,他大小写是否敏感?他敏感怎么做?不敏感怎么做?他有不同的这个解决方法。好吧,这个是需要去注意到的,所以说这这个题目我采用了两种方法。综上,我这采用两种方法,第一种是排序法,用的是 java 来写的。第二种是呃技术法,用的是 pass 来写的。 而排序法他其实两种方法都采用了哈希 map 啊,就采用了 map 的 这样的一个数据结构去做, 因为它的每一组意谓词我都找到了一个唯一的标识作为 key, 然后呢,使用 key 进行一个分组啊,用 key 进行了一个分组, 排序法呢,是用字母串的排序,用字母串排序,然后排序后的字母串作为唯一的标识,然后进行分组。而我的计数法使用的是我的 统计这个技术结果啊,统计每个字母串所出现的字母,也就是字母出现的次数,然后用这个技术的结果作为一个唯一的标识,然后去进行分组啊,所以说要了解这两道题目啊。

我问你个问题啊,就是 runble 和 colib 它的一个区别是什么呢?你知道吗?啊? runble 它是没有返回值的, colib 是 可以有返回值。呃,那返回值怎么去拿到呢?嗯, colib 接口有一个方法是括,就可以返回你想要的对象。呃,这个是定义的方法,那具体是怎么拿到返回值的?直接去调用这个括方法吗? 嗯,那拿返回值的时候,主县城会不会堵塞呢?应该不会堵塞,你这纯靠背啊,你现这现在这环境,你还想着被八股文通过面试,它是有点困难啊。简单来说, collab 呢,它是一个带返回值的任务,它自己不能直接跑在县城里, 你要想让它去执行并拿到结果呢,就需要一个关键的桥梁,也就是 future 的 这个接口。以 collab 加 future task 为例,它的工作流程可以分为三步。第一步呢是任务的一个组装,我们把写好的 collab 对 象通过构造方法传递给 future task。 这个 future task 呢非常厉害,它同时实现了 runable 和 future 的 这两个接口,这意味着它既可以被当做一个普通的任务,通过 thread 来启动,又具备了一个未来凭证的功能,让我们之后可以去获取这个结果。第二步呢是任务的执行,当我们启动现成后,现成会去执行 future task 里面 绕方法,在这个绕方法的一个内部才会真正去调用我们最初传入的那个 callable 的 一个 call 方法。等 call 方法执行完毕,返回结果后, future task 会把内部的这个结果保存起来,同时将一个状态变量标记为已完 完成。第三步是结果的获取,这个时候我们的一个主线程就可以调用 future task 的 一个 get 方法来拿到这个返回值了。这个 get 方法呢,会先去检查那个状态变量,如果任务已经完成了,他就立刻把保存的一个结果返回给你。但如果任务还在执行中,那么主线程就会在 get 方法在这里堵 塞等待,直到任务完成,这就是 callable。 如何去拿到现成返回值的,并且在没有拿到返回值之前,主线程是会堵塞的。 最后,视频配套的面试题答案都整理在评论区了。另外,如果近期面试受阻,没有面试机会,我们也有面试突击陪跑服务,可以私我咨询,从简历优化、项目包装、技术突击、面试、突击、项目业务难点亮点梳理、模拟面试、面试复盘并向内推等。