Java stream distinct findAny anyMatch实现 :DistinctOp、FindOp、MatchOp

DistinctOps

DistinctOps 是一个专门用于实现 Stream.distinct() 操作的工厂类。正如它的名字所示,它的核心职责就是创建能够去除流中重复元素的操作。distinct() 是一个有状态的中间操作 (stateful intermediate operation),这意味着它通常需要看到所有元素才能决定哪些元素可以进入下一阶段,这使得它的实现比无状态操作(如 filtermap)要复杂得多。

ReferencePipeline(这个本身就是顶层的stream)中调用

    @Overridepublic final Stream<P_OUT> distinct() {return DistinctOps.makeRef(this);}

下面我们分几个部分来详细解析 DistinctOps 类。

类的定位和结构

  • final class DistinctOps: 这是一个 final 类,并且构造函数是 private 的,表明它是一个纯粹的工具类(Utility Class),不能被继承或实例化。所有的功能都通过静态方法 makeRef 提供。

  • makeRef(AbstractPipeline<?, T, ?> upstream): 这是该类的唯一入口。当调用 stream.distinct() 时,底层实际上就是调用了这个 DistinctOps.makeRef() 方法。它接收上游的 Pipeline 作为输入,然后返回一个新的 Pipeline 阶段,这个新阶段就包含了去重逻辑。

    // ... existing code ...
    static <T> ReferencePipeline<T, T> makeRef(AbstractPipeline<?, T, ?> upstream) {return new ReferencePipeline.StatefulOp<T, T>(upstream, StreamShape.REFERENCE,StreamOpFlag.IS_DISTINCT | StreamOpFlag.NOT_SIZED) {// ... 具体的实现 ...};
    }
    // ... existing code ...
    

    这里它创建了一个 ReferencePipeline.StatefulOp 的匿名子类实例。这立即告诉我们几点重要信息:

    1. distinct() 是一个有状态操作 (StatefulOp)。
    2. 它向上游声明了 IS_DISTINCT 标志,表示从这个阶段输出的流是已经去重的。
    3. 它还声明了 NOT_SIZED 标志,因为去重后元素的数量是未知的,除非处理完所有元素。

核心实现:串行 vs 并行,有序 vs 无序

distinct() 的复杂性在于它必须根据流的 并行性 (parallel)  有序性 (ordered) 采取截然不同的策略。DistinctOps 内部通过重写 StatefulOp 的几个关键方法来处理这些不同情况。

串行执行 (opWrapSink)

这是最简单的情况,对应的是流的串行 (sequential) 执行。逻辑在 opWrapSink 方法中实现。

// ... existing code ...@OverrideSink<T> opWrapSink(int flags, Sink<T> sink) {Objects.requireNonNull(sink);if (StreamOpFlag.DISTINCT.isKnown(flags)) {// 如果上游已经去重,什么都不做,直接返回下游的 Sinkreturn sink;} else if (StreamOpFlag.SORTED.isKnown(flags)) {// 优化:如果流已排序,只需和前一个元素比较return new Sink.ChainedReference<>(sink) {boolean seenNull;T lastSeen;// ...@Overridepublic void accept(T t) {if (t == null) {if (!seenNull) { /* ... */ }} else if (lastSeen == null || !t.equals(lastSeen)) {downstream.accept(lastSeen = t);}}};} else {// 通用情况:使用 Set 来记录见过的元素return new Sink.ChainedReference<>(sink) {Set<T> seen;@Overridepublic void begin(long size) {seen = new HashSet<>();downstream.begin(-1);}// ...@Overridepublic void accept(T t) {if (seen.add(t)) { // Set.add() 返回 true 表示添加成功(即之前没有)downstream.accept(t);}}};}}
// ... existing code ...

逻辑分析:

  • 已去重 (DISTINCT.isKnown): 如果流已经被标记为去重的,distinct() 就成了一个空操作(no-op),直接把元素传给下游。
  • 已排序 (SORTED.isKnown): 这是一个非常重要的优化。如果流是排序的,那么重复的元素必然是相邻的。因此,我们不需要一个 Set 来存储所有见过的元素,只需要记住上一个元素 (lastSeen) 即可。这极大地降低了空间复杂度。
  • 通用情况: 对于无序的流,唯一的办法就是用一个 HashSet 来存储所有已经见过的元素。每次来一个新元素,尝试添加到 Set 中。如果 add 方法返回 true,说明这是个新元素,就把它传递给下游。

并行执行 (opEvaluateParallel)

这是最复杂的情况,对应流的并行 (parallel) 执行。并行执行需要一个屏障 (barrier) 操作,即必须处理完所有分片(Spliterator)的数据,合并结果后才能继续。

// ... existing code ...@Override<P_IN> Node<T> opEvaluateParallel(PipelineHelper<T> helper,Spliterator<P_IN> spliterator,IntFunction<T[]> generator) {if (StreamOpFlag.DISTINCT.isKnown(helper.getStreamAndOpFlags())) {// 已去重,空操作return helper.evaluate(spliterator, false, generator);}else if (StreamOpFlag.ORDERED.isKnown(helper.getStreamAndOpFlags())) {// 有序并行:退化为串行处理模式来保证顺序return reduce(helper, spliterator);}else {// 无序并行:最高效的并行模式AtomicBoolean seenNull = new AtomicBoolean(false);ConcurrentHashMap<T, Boolean> map = new ConcurrentHashMap<>();// ... 并行地将所有元素放入 ConcurrentHashMap 来去重forEachOp.evaluateParallel(helper, spliterator);// ...return Nodes.node(keys);}}
// ... existing code ...

逻辑分析:

  • 有序并行 (ORDERED.isKnown): 为了保证元素的顺序,并行 distinct 无法做到真正的“懒加载”。它必须收集所有元素,在一个地方完成去重,然后再把结果交给下游。这里的 reduce 方法内部使用了 LinkedHashSet 来保证顺序,这实际上是一个代价高昂的屏障操作。
  • 无序并行: 这是并行 distinct 性能最好的场景。因为它不关心顺序,所以可以充分利用并行性。
    • 它使用 ConcurrentHashMap 作为共享的 Set(因为 ConcurrentHashMap 是线程安全的,而 HashSet 不是)。
    • 每个线程都把自己分片中的元素尝试放入这个共享的 map 中。putIfAbsent 是一个原子操作,可以保证只有一个线程能成功放入一个特定的元素。
    • 所有线程完成后,map 的 keySet 就是最终去重后的结果集。
  • ConcurrentHashMap 有一个限制:它的 key 和 value 都不能为 null。
    为了处理流中可能存在的 null 元素,代码使用了一个额外的 AtomicBoolean seenNull 标志位。如果流中出现了 null,就将这个标志设为 true。

并行懒加载 (opEvaluateParallelLazy)

这个方法用于支持可以“懒加载”的并行操作。

// ... existing code ...@Override<P_IN> Spliterator<T> opEvaluateParallelLazy(PipelineHelper<T> helper, Spliterator<P_IN> spliterator) {if (StreamOpFlag.DISTINCT.isKnown(helper.getStreamAndOpFlags())) {// ...}else if (StreamOpFlag.ORDERED.isKnown(helper.getStreamAndOpFlags())) {// 有序流不支持懒加载,必须退化为屏障操作return reduce(helper, spliterator).spliterator();}else {// 无序流可以懒加载return new StreamSpliterators.DistinctSpliterator<>(helper.wrapSpliterator(spliterator));}}
// ... existing code ...

逻辑分析:

  • 对于有序流,distinct 无法实现懒加载,因为它需要看到所有元素才能确定最终顺序。所以它退化成了 opEvaluateParallel 中的 reduce 逻辑,收集所有元素再返回一个新的 Spliterator
  • 对于无序流,可以实现懒加载(逐个处理)。它返回一个特制的 DistinctSpliterator,这个 Spliterator 在内部包装了原始的 Spliterator,并在 tryAdvance 时进行去重判断。

MatchOps

MatchOps 是 Stream API 内部一个至关重要的工厂类,它专门负责创建和管理 短路(short-circuiting) 的终端操作,也就是我们常用的 anyMatch()allMatch()noneMatch()。首先看类的定义:

final class MatchOps {private MatchOps() { }
//...

和 SortedOpsReduceOps 类似,它是一个包可见的、拥有私有构造函数的 final 类。这表明它是一个内部使用的工具工厂,专门用于创建 TerminalOp(终端操作)的实例。

它的核心职责是:根据用户调用的匹配方法(anyMatch 等)和传入的谓词(Predicate),构建一个能够高效执行匹配逻辑的终端操作。

短路(Short-circuiting)是理解 MatchOps 的关键。anyMatchallMatchnoneMatch 都是短路操作。这意味着它们不一定需要处理流中的所有元素就能得出最终结果。

  • anyMatch(p): 只要找到一个满足谓词 p 的元素,结果就确定为 true,无需再检查后续元素。
  • allMatch(p): 只要找到一个不满足谓词 p 的元素,结果就确定为 false,无需再检查后续元素。
  • noneMatch(p): 只要找到一个满足谓词 p 的元素,结果就确定为 false,无需再检查后续元素。

这种“提前退出”的能力就是短路,它能极大地提升在某些数据场景下的执行效率。

MatchKind 枚举:统一匹配逻辑

为了用一套统一的逻辑来处理这三种不同的匹配规则,MatchOps 设计了一个非常精巧的枚举 MatchKind

// ... existing code ...enum MatchKind {/** Do any elements match the predicate? */ANY(true, true),/** Do all elements match the predicate? */ALL(false, false),/** Do no elements match the predicate? */NONE(true, false);private final boolean stopOnPredicateMatches;private final boolean shortCircuitResult;private MatchKind(boolean stopOnPredicateMatches,boolean shortCircuitResult) {this.stopOnPredicateMatches = stopOnPredicateMatches;this.shortCircuitResult = shortCircuitResult;}}
// ... existing code ...

这个枚举通过两个布尔值,巧妙地描述了三种匹配行为的共性与差异:

  • stopOnPredicateMatches: 当谓词(predicate)的计算结果为 true 时,是否应该停止处理?
    • ANY: 是。找到一个匹配的就停。
    • ALL: 否。找到一个匹配的还不够,必须继续找,直到找到不匹配的或者遍历完。
    • NONE: 是。找到一个匹配的就停(因为结果已经确定是 false)。
  • shortCircuitResult: 如果发生了短路(提前停止),那么最终的结果应该是什么?
    • ANYtrue
    • ALLfalse
    • NONEfalse

通过这个枚举,后续的实现代码(如下面的 MatchSink)就可以写出通用的逻辑,而无需为 anyallnone 分别写 if-else 分支。

工厂方法与 MatchSink

MatchOps 提供了一系列 make... 工厂方法,用于为不同类型的流(StreamIntStream 等)创建匹配操作。我们以 makeRef 为例:

// ... existing code ...public static <T> TerminalOp<T, Boolean> makeRef(Predicate<? super T> predicate,MatchKind matchKind) {Objects.requireNonNull(predicate);Objects.requireNonNull(matchKind);class MatchSink extends BooleanTerminalSink<T> {MatchSink() {super(matchKind);}@Overridepublic void accept(T t) {if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) {stop = true;value = matchKind.shortCircuitResult;}}}return new MatchOp<>(StreamShape.REFERENCE, matchKind, MatchSink::new);}
// ... existing code ...
  1. 它接收一个 Predicate 和一个 MatchKind
  2. 内部定义了一个局部类 MatchSink,它继承自 BooleanTerminalSinkSink 是流中处理元素的末端。
  3. MatchSink 的 accept(T t) 方法是核心逻辑所在:
    • !stop: 检查是否已经有其他元素触发了短路。如果已经 stop,则直接忽略当前元素。
    • predicate.test(t) == matchKind.stopOnPredicateMatches: 这是一个非常优雅的判断。它将当前元素的匹配结果 (true 或 false) 与 MatchKind 中定义的“停止条件”进行比较。
      • 对于 ANYstopOnPredicateMatches 是 true。当 predicate.test(t) 为 true 时,条件成立。
      • 对于 ALLstopOnPredicateMatches 是 false。当 predicate.test(t) 为 false 时,条件成立。
      • 对于 NONEstopOnPredicateMatches 是 true。当 predicate.test(t) 为 true 时,条件成立。
    • 如果条件成立,就设置 stop = true 来通知上游停止发送数据,并设置 value = matchKind.shortCircuitResult 来记录短路时的结果。
  4. 最后,它创建一个 MatchOp 实例并返回。MatchOp 是 TerminalOp 的一个具体实现,它封装了 MatchKind 和用于创建 MatchSink 的 Supplier

并行执行:MatchTask

对于并行流,MatchOp 的 evaluateParallel 方法会创建一个 MatchTask

// ... existing code ...@SuppressWarnings("serial")private static final class MatchTask<P_IN, P_OUT>extends AbstractShortCircuitTask<P_IN, P_OUT, Boolean, MatchTask<P_IN, P_OUT>> {
// ... (constructors) ...@Overrideprotected Boolean doLeaf() {boolean b = helper.wrapAndCopyInto(op.sinkSupplier.get(), spliterator).getAndClearState();if (b == op.matchKind.shortCircuitResult)shortCircuit(b);return null;}@Overrideprotected Boolean getEmptyResult() {return !op.matchKind.shortCircuitResult;}}
// ... existing code ...

MatchTask 继承自 AbstractShortCircuitTask,这是一个专为并行短路操作设计的 Fork/Join 任务。

  • doLeaf(): 这是叶子任务执行的逻辑。它会对分配给自己的那一小部分数据执行匹配操作。
    • helper.wrapAndCopyInto(...): 执行匹配,得到这部分数据的结果 b
    • if (b == op.matchKind.shortCircuitResult): 判断这个局部结果 b 是否就是最终的短路结果。例如,对于 anyMatch,如果这个叶子任务发现了一个匹配项,它的结果 b 就会是 true,这恰好等于 ANY 的 shortCircuitResult
    • shortCircuit(b): 如果发现了可以导致整个流短路的结果,它会调用 shortCircuit。这个方法会通知 Fork/Join 框架,一个最终结果已经找到了,所有其他正在运行的、或者尚未开始的 MatchTask 都可以被取消了。这实现了并行的短路。
  • getEmptyResult(): 如果所有任务都正常执行完毕,没有发生短路,那么这个方法返回最终的结果。例如,对于 allMatch,如果所有元素都匹配,没有发生短路,最终结果就是 true,即 !op.matchKind.shortCircuitResult (!false)。

总结

MatchOps 是一个设计精良的内部工厂,它通过以下方式优雅地实现了 anyMatchallMatchnoneMatch

  1. MatchKind 枚举: 用两个布尔标志统一了三种匹配模式的逻辑,避免了重复代码。
  2. MatchSink: 作为串行执行的核心,它利用 MatchKind 的配置来实现通用的、可短路的元素处理逻辑。
  3. MatchTask: 作为并行执行的核心,它利用 AbstractShortCircuitTask 的能力,在 Fork/Join 框架下实现了高效的并行短路,一旦任何一个子任务找到了决定性的结果,就能迅速终止整个计算。

FindOps

它与我们之前讨论的 MatchOps 非常相似,但专注于实现 findFirst() 和 findAny() 这两个终端操作。FindOps 是一个内部使用的工厂类,其核心职责是创建用于“查找”操作的终端操作(TerminalOp)实例

final class FindOps {private FindOps() { }
//...

和 MatchOps 一样,它也是一个 final 的、拥有私有构造函数的工具类,专门用于 Stream API 内部。它处理的 findFirst() 和 findAny() 都是短路操作,一旦找到符合条件的元素,就可以立即终止流的处理。

findFirst() vs findAny()

这是理解 FindOps 的关键区别点:

  • findFirst(): 必须返回流中遭遇顺序(encounter order)的第一个元素。这是一个有序操作。在并行流中,即使其他线程更快地找到了一个元素,也必须等待遭遇顺序更靠前的任务完成,以确保返回的是“最左边”的那个结果。
  • findAny(): 可以返回流中的任意一个元素。这是一个无序操作。在并行流中,它允许任何一个线程只要找到了一个元素,就可以立即短路整个计算,而无需关心这个元素在原始流中的位置。这使得 findAny() 在并行流中的性能通常优于 findFirst()

FindOps 内部通过一个布尔标志 mustFindFirst 来区分这两种行为。

FindOps 提供了一系列 make... 方法,用于为不同类型的流(对象、int、long、double)创建查找操作。

// ... existing code ...@SuppressWarnings("unchecked")public static <T> TerminalOp<T, Optional<T>> makeRef(boolean mustFindFirst) {return (TerminalOp<T, Optional<T>>)(mustFindFirst ? FindSink.OfRef.OP_FIND_FIRST : FindSink.OfRef.OP_FIND_ANY);}
// ... existing code ...

这些方法非常简洁。它们根据 mustFindFirst 参数,直接返回一个预先创建好的、静态的 TerminalOp 实例。这些实例(如 OP_FIND_FIRST)被定义在内部类 FindSink 中。这种方式避免了重复创建对象,提高了效率。

FindSink - 串行执行的核心

FindSink 是实现查找逻辑的 TerminalSink

// ... existing code ...private abstract static class FindSink<T, O> implements TerminalSink<T, O> {boolean hasValue;T value;// ...@Overridepublic void accept(T value) {if (!hasValue) {hasValue = true;this.value = value;}}@Overridepublic boolean cancellationRequested() {return hasValue;}
// ... existing code ...

它的逻辑非常直接:

  • accept(T value): 当接收到第一个元素时,将其保存在 this.value 中,并将 hasValue 标志设为 true。由于 if (!hasValue) 的判断,后续所有到达的元素都会被忽略。
  • cancellationRequested(): 一旦 hasValue 变为 true,此方法就返回 true。这会通知上游的流管道:“我已经找到结果了,请不要再发送更多元素了”,从而实现串行流的短路。

FindSink 有多个静态内部子类(OfRefOfInt 等),用于处理不同数据类型,并预先创建了 OP_FIND_FIRST 和 OP_FIND_ANY 这两个静态常量。

FindOp - 终端操作的封装

FindOp 是 TerminalOp 接口的具体实现,它封装了查找操作的所有元信息。

// ... existing code ...private static final class FindOp<T, O> implements TerminalOp<T, O> {private final StreamShape shape;final int opFlags;final O emptyValue;// ...final Supplier<TerminalSink<T, O>> sinkSupplier;FindOp(boolean mustFindFirst,StreamShape shape,O emptyValue,Predicate<O> presentPredicate,Supplier<TerminalSink<T, O>> sinkSupplier) {this.opFlags = StreamOpFlag.IS_SHORT_CIRCUIT | (mustFindFirst ? 0 : StreamOpFlag.NOT_ORDERED);// ...}
// ... existing code ...
  • opFlags: 操作标志。所有查找操作都是 IS_SHORT_CIRCUIT。对于 findAny (mustFindFirst 为 false),还会额外加上 NOT_ORDERED 标志,告知流管道可以进行无序优化。
  • evaluateSequential(...): 定义了串行执行的逻辑,即创建一个 FindSink 并让流把元素送入其中。
  • evaluateParallel(...): 定义了并行执行的逻辑,它会创建一个 FindTask 并启动它。

FindTask - 并行执行的核心

FindTask 是 AbstractShortCircuitTask 的子类,负责并行查找。

// ... existing code ...private static final class FindTask<P_IN, P_OUT, O>extends AbstractShortCircuitTask<P_IN, P_OUT, O, FindTask<P_IN, P_OUT, O>> {private final FindOp<P_OUT, O> op;private final boolean mustFindFirst;
// ...@Overrideprotected O doLeaf() {O result = helper.wrapAndCopyInto(op.sinkSupplier.get(), spliterator).get();if (!mustFindFirst) { // findAny 逻辑if (result != null)shortCircuit(result); // 找到任何一个,立即短路return null;}else { // findFirst 逻辑if (result != null) {foundResult(result); // 找到一个,需要通过 cancelLaterNodes 确保有序性return result;}elsereturn null;}}
// ...}
// ... existing code ...

doLeaf() 方法清晰地展示了 findFirst 和 findAny 在并行时的区别:

  • findAny (!mustFindFirst): 叶子任务只要处理自己的数据分片并找到了一个元素 (result != null),就立刻调用 shortCircuit(result)。这会尝试将结果写入共享的 AtomicReference,并触发全局的短路,所有其他任务都会尽快停止。
  • findFirst (mustFindFirst): 叶子任务找到一个元素后,不能直接宣布胜利。它需要调用 foundResult(result),这个方法内部会调用 cancelLaterNodes()。这会取消掉所有处理“更右边”(遭遇顺序更靠后)数据的任务,同时允许“更左边”的任务继续执行。最终,只有最左边的那个找到了结果的任务,其结果才会被采纳为最终结果。这个过程通过 onCompletion 方法中的逻辑来保证。

        private void foundResult(O answer) {if (isLeftmostNode())shortCircuit(answer);elsecancelLaterNodes();}AbstractTask::protected boolean isLeftmostNode() {@SuppressWarnings("unchecked")K node = (K) this;while (node != null) {K parent = node.getParent();if (parent != null && parent.leftChild != node)return false;node = parent;}return true;}

总结

FindOps 通过一系列精心设计的内部类,为 findFirst 和 findAny 提供了统一且高效的实现:

  1. 工厂方法 (make...): 提供简洁的入口,并复用预创建的 TerminalOp 实例。
  2. FindSink: 实现了串行查找的短路逻辑。
  3. FindOp: 封装了操作的元数据,并区分了 findFirst 和 findAny 的标志。
  4. FindTask: 作为 AbstractShortCircuitTask 的子类,它为并行查找提供了核心实现,并巧妙地利用父类的短路和取消机制,分别实现了 findAny 的“快速响应”和 findFirst 的“有序保证”。

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
    如若转载,请注明出处:http://www.pswp.cn/news/918710.shtml
    繁体地址,请注明出处:http://hk.pswp.cn/news/918710.shtml

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    锁的基本介绍

    锁 并发编程的一个最基本问题就是原子性地执行一系列指令。锁有助于直接解决这一问题。 锁的基本思想 锁就是一个变量。这个变量保存了锁在某一时刻的状态。它要么是可用的&#xff0c;表示没有线程持有锁&#xff0c;要么是被占用的&#xff0c;表示有线程持有锁&#xff0c;正…

    【读代码】开源流式语音编码器SecoustiCodec

    引言:从LLM到深度语义 在大型语言模型(LLM)驱动的语音交互时代,神经语音编解码器 (Neural Speech Codec) 扮演着至关重要的角色。它如同 LLM 的“耳朵”和“嘴巴”,负责将连续的语音波形转换为离散的、可供模型处理的 token,并将模型生成的 token 还原为自然的人声。 一…

    P5967 [POI 2016] Korale 题解

    P5967 [POI 2016] Korale 题目描述 有 nnn 个带标号的珠子&#xff0c;第 iii 个珠子的价值为 aia_iai​。 现在你可以选择若干个珠子组成项链&#xff08;也可以一个都不选&#xff09;&#xff0c;项链的价值为所有珠子的价值和。 给出所有可能的项链排序&#xff0c;先按…

    SwiftUI 页面弹窗操作

    SwiftUI 页面弹窗操作指南一、基础弹窗实现1. Alert 基础警告框2. ActionSheet 操作菜单3. Sheet 模态视图4. Popover 浮动视图二、高级自定义弹窗1. 自定义弹窗组件2. 使用自定义弹窗三、弹窗状态管理1. 使用环境对象管理弹窗2. 弹窗路由系统四、动画与过渡效果1. 自定义弹窗动…

    OpenCV图像处理2:边界填充与平滑滤波实战

    前面学了一些关于opencv图像处理的内容&#xff0c;现在继续。一 图像填充边界填充&#xff08;Border Padding&#xff09;​&#xff0c;即在图像四周添加指定宽度的像素区域。其核心函数是cv2.copyMakeBorder()&#xff0c;通过不同的填充方式&#xff08;borderType&#x…

    imx6ull-驱动开发篇22——Linux 时间管理和内核定时器

    目录 内核时间管理 系统节拍率 高/低节拍率的优缺点 jiffies 节拍数 时间绕回 时间转换函数 内核定时器 timer_list 结构体 定时器API函数 init_timer 函数 add_timer 函数 del_timer 函数 del_timer_sync 函数 mod_timer 函数 Linux 内核短延时函数 内核时间管…

    路由器数据控制管理层面安全

    数据层面&#xff1a;FPM Flexible Packet MatchingFPM是CisCOIOS新一代的ACL根据任意条件&#xff0c;无无状态的匹配数据包的头部负载&#xff0c;或者全部分析协议&#xff0c;更易于规则的创建用于替代传统ACL&#xff0c;对特定恶意流量的基础架构过滤无状态ipv4单播不支持…

    Vue内置组件全解析:从入门到面试通关

    文章目录Vue内置组件全解析&#xff1a;从入门到面试通关引言&#xff1a;为什么需要内置组件&#xff1f;一、Vue内置组件全景图二、核心内置组件详解1. <component> - 动态组件2. <transition> - 过渡动画3. <keep-alive> - 组件缓存4. <slot> - 内容…

    VUE+SPRINGBOOT从0-1打造前后端-前后台系统-会议记录

    在当今快节奏的工作环境中&#xff0c;会议记录是每个职场人士都必须要面对的任务。传统的手动记录方式不仅效率低下&#xff0c;而且容易遗漏重要信息。随着Web技术的发展&#xff0c;基于浏览器的实时语音转写技术为会议记录提供了全新的解决方案。本文将详细介绍如何利用Web…

    WEB3——水龙头,如何获得开发用的测试币、 Sepolia 测试币?

    注意&#xff1a; 有些水龙头渠道&#xff0c;要求以太坊币至少有0.01ETH,设有这个门槛&#xff0c;下面并不是所有渠道都能领取到测试币&#xff0c;有些可能对领取测试币有要求&#xff0c;如果想获得获取以太坊币的方法&#xff0c;可以看我其他的文章。 本文整理了多个免费…

    C++调试革命:时间旅行调试实战指南

    还在为C的悬垂指针、内存泄漏和并发竞态抓狂&#xff1f;让调试器学会“时光倒流” 凌晨三点&#xff0c;std::thread创建的六个线程中有一个突然吞掉了你的数据&#xff0c;valgrind只告诉你“Invalid read”&#xff0c;而时间旅行调试&#xff08;TTD&#xff09;​​ 能让你…

    mysql8.0笔记

    1.DDL数据定义语言 DDL是什么——————创建、修改、删除 数据库和表结构的命令。 基本语法 针对数据库的操作 -- 创建数据库 CREATE DATABASE 数据库名; -- 比如 CREATE DATABASE myschool; --查看所有数据库 SHOW DATABASES; --使用某个数据库 USE myschool; -- 删除数据库…

    大模型微调【1】之入门

    文章目录说明一 大模型微调技术1.1 微调基础1.2 量化概念1.3 高效微调方法LoRA&QLoRA1.4 LoRA VS QLoRA1.5 高效微调的应用场景二 主流微调工具2.1 unsloth2.2 LLama-Factory2.3 ms-SWIFT2.4 ColossalAI2.5 底层微调框架推荐2.6 模型性能评估框架EvalScope三 微调所需软硬件…

    深入解析Linux poll()系统调用

    &#x1f504; Linux poll() 系统调用详解一、poll 是干什么的&#xff1f;poll 是 Linux&#xff08;及 POSIX 标准&#xff09;中用于实现 I/O 多路复用&#xff08;I/O Multiplexing&#xff09; 的系统调用&#xff0c;它的核心作用是&#xff1a;让一个线程能够同时监视多…

    文献阅读 | PLoS ONE | SRplot:一个免费的在线平台,用于数据可视化和图形

    文献介绍文献题目&#xff1a; SRplot&#xff1a;一个免费的在线平台&#xff0c;用于数据可视化和图形 研究团队&#xff1a; Yewei Wang&#xff08;中南大学湘雅二医院&#xff09; 发表时间&#xff1a; 2023-11-09 发表期刊&#xff1a; PLoS ONE 影响因子&#xff1a; 3…

    分布式与微服务宝典

    分布式理论基础 1、分布式架构有哪些特点&#xff0c;优势和缺陷 特点&#xff1a;微服务架构的优点微服务架构的缺陷自由使用不同技术增加故障排除挑战每一个微服务都侧重于单一功能由于远程调用增加延迟支持单个可部署单元增加了配置与其他操作的工作量允许经常发布软件难以保…

    利用生成式AI与大语言模型(LLM)革新自动化软件测试 —— 测试工程师必读深度解析

    引言 自动化测试是现代软件工程的基石&#xff0c;然而&#xff0c;随着软件复杂度和迭代速度的飞速提升&#xff0c;传统自动化测试方法正面临越来越多的挑战。 近年来&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;和大语言模型&#xff08;LLM&#xff0…

    JS 与 C++ 双向通信实战:基于 WebHostViewListener 的消息处理机制

    前言在现代浏览器和桌面应用开发中&#xff0c;WebView 嵌入已经成为一种非常常见的 UI 技术方案。无论是基于 Chromium 的 CEF&#xff08;Chromium Embedded Framework&#xff09;、Qt WebEngine&#xff0c;还是自研浏览器内核&#xff0c;嵌入 WebView 都能带来极高的灵活…

    模板打印技术——Office XLS 打印模板:为政务土地确权定制的纸张替换利器—仙盟创梦IDE

    代码public static int cyberwin_replaceExcelandoutputPrint(string fisrcpathleurl, DataTable dtInfo, string despath){if (File.Exists(despath) true){//删除目标文件File.Delete(despath);}File.Copy(fisrcpathleurl, despath);string 目标文件 despath;MSEXCEL.Appli…

    可直接运行的 Playwright C# 自动化模板

    目录 目录结构 1. appsettings.json&#xff08;账号、URL、路径配置&#xff09; 2. Program.cs&#xff08;启动入口&#xff09; 3. SchedulerConfig.cs&#xff08;定时调度&#xff09; 4. SocialSecurityTask.cs&#xff08;自动报社保任务&#xff09; 5. QuerySo…