{"id":48505236,"url":"https://github.com/eloyhere/semantic-java","last_synced_at":"2026-04-07T16:02:01.053Z","repository":{"id":349637556,"uuid":"1203148092","full_name":"eloyhere/semantic-java","owner":"eloyhere","description":"Semantic-Java is a modern, maven Java stream processing framework with zero dependencies. It elegantly blends the fluency of Java Streams, the laziness of JavaScript generators, and intelligent index-based control inspired by database indexing — perfect for time-series, event streams, and high-performance data pipelines as a maven pendency.","archived":false,"fork":false,"pushed_at":"2026-04-06T22:27:12.000Z","size":118,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-06T23:15:23.676Z","etag":null,"topics":["data","functional","functional-programming","java","pipeline","stream"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eloyhere.png","metadata":{"files":{"readme":"README.cn.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-06T19:11:29.000Z","updated_at":"2026-04-06T22:21:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/eloyhere/semantic-java","commit_stats":null,"previous_names":["eloyhere/semantic-java"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/eloyhere/semantic-java","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eloyhere%2Fsemantic-java","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eloyhere%2Fsemantic-java/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eloyhere%2Fsemantic-java/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eloyhere%2Fsemantic-java/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eloyhere","download_url":"https://codeload.github.com/eloyhere/semantic-java/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eloyhere%2Fsemantic-java/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31518643,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T03:10:19.677Z","status":"ssl_error","status_checked_at":"2026-04-07T03:10:13.982Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data","functional","functional-programming","java","pipeline","stream"],"created_at":"2026-04-07T16:02:00.142Z","updated_at":"2026-04-07T16:02:01.036Z","avatar_url":"https://github.com/eloyhere.png","language":"Java","readme":"# Semantic-Java：下一代 Java 流式处理框架\n\n## 引言\n\n在当今数据驱动的时代，高效的流式数据处理已成为现代应用程序的核心需求。无论是处理实时传感器数据、分析金融时间序列，还是构建高性能事件处理管道，开发者都需要一个既强大又灵活的工具。Java 标准库中的 `Stream API` 自 Java 8 引入以来，极大地改善了集合处理体验，但其设计仍存在一些限制：严格的一次性消费、有限的索引控制、以及缺乏原生的窗口操作支持。\n\nSemantic-Java 应运而生，它是一个零依赖的现代 Maven Java 流处理框架，旨在弥补这些缺口。它巧妙地融合了 Java Streams 的流畅表达、JavaScript 生成器的惰性求值哲学，以及受数据库索引启发的智能索引控制机制。无论您是处理时间序列数据、事件流，还是构建复杂的数据转换管道，Semantic-Java 都提供了优雅而强大的解决方案。\n\n本文将深入探讨 Semantic-Java 的核心概念、设计哲学、API 使用方式以及最佳实践，帮助您全面掌握这一革新性的流处理工具。\n\n## 核心优势与设计理念\n\n### 为什么选择 Semantic-Java？\n\n1.  **零依赖与轻量级**\n    Semantic-Java 不依赖任何外部库，确保了极致的轻量和纯净。您可以轻松地将其集成到任何 Java 项目中，无需担心依赖冲突或臃肿的部署包。\n\n2.  **无缝融合多种范式**\n    - **Java Streams 的流畅性**：提供类似 `filter`、`map`、`flatMap` 等直观的操作符，让数据处理管道清晰可读。\n    - **JavaScript 生成器的惰性**：流元素仅在需要时被计算和消费，最大化内存效率，支持处理无限或超大规模数据流。\n    - **数据库索引的智能控制**：引入了独特的索引重定向 (`redirect`、`translate`、`reverse`) 机制，允许您像操作数据库记录一样，灵活地控制流中元素的逻辑位置。\n    - **原生窗口操作支持**：新增的 `WindowCollectable` 提供了原生的滑动窗口和翻滚窗口操作，极大地简化了时间序列分析和流式聚合。\n\n3.  **为现代场景量身打造**\n    框架特别适合处理**时间序列数据**（如股票报价、传感器读数）、**事件流**（如用户行为日志、消息队列）以及任何需要高性能、可组合数据转换的**管道**。\n\n4.  **丰富的原生统计支持**\n    内置了从 `Byte` 到 `BigDecimal` 等多种数值类型的专门统计类（如 `IntStatistics`、`DoubleStatistics`），开箱即用地支持求和、平均、极值等聚合计算，无需编写样板代码。\n\n### Semantic-Java vs. Java Stream API\n\n为了清晰展示 Semantic-Java 的独特价值，下表将其与 Java 标准 Stream API 进行了关键特性的对比：\n\n| 特性维度 | Java Stream API | Semantic-Java | 备注 |\n| :--- | :--- | :--- | :--- |\n| **核心抽象** | `Stream\u003cT\u003e`，一次性消费管道。 | `Semantic\u003cE\u003e`，基于 `Generator\u003cE\u003e` 的惰性流，支持丰富的状态转换。 | Semantic-Java 的生成器模型为无限流和复杂控制流提供了更好基础。 |\n| **元素索引** | **无原生支持**。元素没有关联的索引，需要外部计数器或使用 `IntStream.range` 等变通方法。 | **核心特性**。每个元素都附带一个 `long` 类型索引，并且可以通过 `redirect`, `translate`, `reverse` 进行动态控制。 | 索引控制是 Semantic-Java 处理时间序列、分页、逻辑重排等场景的“杀手锏”。 |\n| **窗口操作** | **无原生支持**。需通过第三方库（如 Apache Commons Collections 的 `ListUtils.partition`）或手动使用 `collect` 和子列表实现，代码冗长。 | **原生支持**。通过 `toWindow().slide(size, step)` 和 `toWindow().tumble(size)` 直接支持滑动窗口和翻滚窗口。 | 极大简化了移动平均、实时聚合等流式分析任务的代码。 |\n| **惰性求值** | 支持。中间操作是惰性的。 | 支持。继承了相同的惰性求值模型。 | 两者在核心执行模型上一致，有利于处理大规模数据。 |\n| **并行处理** | 成熟。基于 ForkJoin 框架，通过 `parallel()` 和 `sequential()` 切换。 | 支持。通过 `parallel()` 设置并发度，终端 `Collector` 实现并行收集。 | Java Stream 的并行实现更成熟和透明。Semantic-Java 提供了并行化能力，但更侧重于索引和窗口等高级抽象。 |\n| **依赖** | Java 标准库的一部分，零额外依赖。 | 零依赖的独立框架。 | 两者都易于集成，Semantic-Java 保持了轻量级特性。 |\n| **易用性** | 语法简洁，API 稳定，是 Java 开发者的标准技能。 | 在继承 Stream 风格 API 的基础上，引入了更强大的索引和窗口操作，需要学习新概念，但在特定领域更强大。 | 对于熟悉 Stream API 的开发者，Semantic-Java 上手较快，并能解决更复杂的问题。 |\n| **适用场景** | 通用的集合数据处理、过滤、映射、归约。 | 通用的集合处理，**尤其擅长**时间序列分析、事件流处理、需要复杂元素定位（分页、重排）、滑动/翻滚窗口计算的场景。 | Semantic-Java 是 Java Stream 的“超集”和“增强”，在特定领域提供更强大的抽象。 |\n\n**总结**：Java Stream API 是一个优秀且通用的标准工具。而 Semantic-Java 在其基础上，通过引入**索引控制**和**原生窗口操作**这两个核心抽象，显著增强了对**有序数据流**和**时间/序列敏感计算**的处理能力，是处理此类问题的专业化、现代化框架。\n\n### 核心概念解析\n\n在深入 API 之前，理解以下几个核心抽象至关重要：\n\n1.  **`Generator\u003cE\u003e` (生成器)**\n    这是整个框架的源头。它是一个函数式接口，负责按需\"生成\"流中的元素。其核心方法是 `accept`，它接收一个消费者（处理元素）和一个中断谓词（决定是否提前终止）。所有 `Semantic` 流都构建在 `Generator` 之上，这为实现惰性求值和无界流奠定了基础。\n\n2.  **`Semantic\u003cE\u003e` (语义流)**\n    这是框架的主入口和主要操作对象。它封装了一个 `Generator`，并提供了所有流式操作（如 `filter`, `map`, `distinct` 等）。每个操作都返回一个新的 `Semantic` 实例，实现了不可变性和链式调用。其内部持有 `concurrent` 属性，为并行处理预留了可能。\n\n3.  **`Collector\u003cE, A, R\u003e` (收集器)**\n    这是流的终端操作。它定义了如何将流中的元素累积到一个可变的结果容器(`A`)中，以及如何将最终容器转换为结果(`R`)。`Collectors` 工具类提供了大量静态工厂方法（如 `toList()`, `useGroup()`, `useJoin()`）来创建常见的收集器。`Collector` 支持短路操作（如 `findFirst`）和并行收集。\n\n4.  **`IndexedControl` (索引控制)**\n    这是 Semantic-Java 的\"秘密武器\"。每个流元素都附带一个 `long` 类型的索引。大部分操作（如 `filter`、`map`）会保持或传递原始索引。而 `redirect`、`translate`、`reverse` 等操作可以显式地修改这个索引。`Collector` 的累积函数 `IndexedAccumulator` 和中断判断 `IndexedInterrupt` 也都接收索引参数，使得操作可以基于元素位置进行，极大地增强了表达能力。\n\n5.  **`Collectable\u003cE\u003e` 及其子类**\n    这是可收集流的状态。`OrderedCollectable` 和 `UnorderedCollectable` 分别代表需要保持顺序和无需保持顺序的终端状态。它们提供了 `collect` 方法，接收一个 `Collector` 来执行最终的聚合。各种 `Statistics` 类（如 `IntStatistics`）继承自 `OrderedCollectable`，专门用于数值统计。\n\n6.  **`WindowCollectable\u003cE\u003e` (窗口可收集流)**\n    这是 Semantic-Java 新增的重要特性，继承自 `OrderedCollectable`。它专门用于处理窗口操作，提供了两种窗口模式：滑动窗口(`slide`)和翻滚窗口(`tumble`)。窗口操作在时间序列分析、流式聚合和模式检测中至关重要。\n\n## 快速开始\n\n### 1. 添加 Maven 依赖\n\n将以下依赖添加到您的 `pom.xml` 文件中：\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003epers.eloyhere\u003c/groupId\u003e\n    \u003cartifactId\u003esemantic-java\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.0\u003c/version\u003e \u003c!-- 请使用最新版本 --\u003e\n\u003c/dependency\u003e\n```\n\n### 2. 创建您的第一个流\n\n让我们从一个简单的例子开始，了解如何创建和消费一个流。\n\n```java\nimport pers.eloyhere.semantic.Semantic;\nimport pers.eloyhere.semantic.Collectors;\nimport java.util.List;\n\npublic class QuickStart {\n    public static void main(String[] args) {\n        // 1. 从数组创建流\n        List\u003cString\u003e names = Semantic.useFrom(new String[]{\"Alice\", \"Bob\", \"Charlie\"})\n            .toUnordered()\n            .collect(Collectors.toList());\n\n        System.out.println(names); // 输出: [Alice, Bob, Charlie]\n\n        // 2. 从集合创建流\n        List\u003cInteger\u003e numbers = Semantic.useFrom(List.of(1, 2, 3, 4, 5))\n            .toUnordered()\n            .collect(Collectors.toList());\n\n        // 3. 创建数值范围流\n        List\u003cLong\u003e range = Semantic.useRange(5, 10) // 生成 5,6,7,8,9\n            .toUnordered()\n            .collect(Collectors.toList());\n        \n        System.out.println(range); // 输出: [5, 6, 7, 8, 9]\n    }\n}\n```\n\n### 3. 基础操作链\n\n一个典型的数据处理管道包含多个中间操作和一个终端操作。\n\n```java\nimport pers.eloyhere.semantic.Semantic;\nimport pers.eloyhere.semantic.Collectors;\nimport java.util.List;\n\npublic class BasicPipeline {\n    public static void main(String[] args) {\n        List\u003cString\u003e result = Semantic.useFrom(new String[]{\"apple\", \"banana\", \"apricot\", \"cherry\", \"avocado\"})\n                .filter(fruit -\u003e fruit.startsWith(\"a\")) // 1. 过滤: 只保留以'a'开头的水果\n                .map(String::toUpperCase)                // 2. 映射: 转为大写\n                .toUnordered()\n                .collect(Collectors.toList());           // 3. 收集: 转为List\n\n        System.out.println(result); // 输出: [APPLE, APRICOT, AVOCADO]\n    }\n}\n```\n\n## 核心 API 详解\n\n### 流创建 (`Semantic` 静态工厂方法)\n\n`Semantic` 类提供了多种静态方法来创建流：\n\n- **`useFrom(Iterable\u003cE\u003e)` / `useFrom(E[])`**：从集合或数组创建流。\n- **`useRange(long start, long end)`**：创建一个连续的 `Long` 流，从 `start` (包含) 到 `end` (不包含)。非常适合生成索引或序列。\n- **`useBlob(InputStream)`**：从输入流读取字节创建 `Byte` 流。用于处理二进制数据。\n- **`useBlob(InputStream, Charset)`**：从输入流按指定字符集读取字符创建 `Character` 流。\n- **`useText(String)`**：将整个字符串作为单个元素创建流。\n- **`useText(String, String delimiter)`**：按分隔符分割字符串创建流。\n- **`useCodePoint(String)`**：按 Unicode 代码点分割字符串创建流，正确处理代理对。\n\n### 中间操作 (`Semantic\u003cE\u003e` 实例方法)\n\n中间操作是流处理的核心，它们对元素进行转换、过滤、排序等，并返回一个新的流。\n\n#### 过滤与选择\n- **`filter(Predicate\u003cE\u003e)` / `filter(BiPredicate\u003cE, Long\u003e)`**：根据元素值或元素值+索引决定是否保留。\n- **`distinct()`**：去除重复元素（基于 `equals` 和 `hashCode`）。\n- **`distinct(Comparator\u003cE\u003e)`**：根据自定义比较器去重。\n- **`limit(long n)`**：限制流最多包含 `n` 个元素。\n- **`skip(long n)`**：跳过前 `n` 个元素。\n- **`sub(long start, long end)`**：截取索引在 `(start, end)` 区间内的元素。\n- **`takeWhile(Predicate/BP)`**：**惰性**地从开头开始取元素，直到条件不满足为止。这是与 `filter` 的关键区别，`filter` 会检查所有元素，而 `takeWhile` 遇到第一个不满足条件的元素就会停止生成。\n- **`dropWhile(Predicate/BP)`**：**惰性**地跳过开头的元素，直到条件不满足，然后保留之后的所有元素。\n\n#### 映射与转换\n- **`map(Function\u003cE, R\u003e)` / `map(BiFunction\u003cE, Long, R\u003e)`**：将每个元素转换为另一种类型。这是最常用的操作之一。\n- **`flatMap(Function\u003cE, Semantic\u003cR\u003e\u003e)`**：将每个元素映射为一个流，然后将所有这些流\"扁平化\"为一个流。例如，将句子流映射为单词流。\n- **`flat(Function\u003cE, Semantic\u003cE\u003e\u003e)`**：`flatMap` 的特化版本，输入输出元素类型相同。\n\n#### 索引控制（核心特性）\n- **`redirect(BiFunction\u003cE, Long, Long\u003e)`**：**最强大的索引操作**。允许您为每个元素动态计算一个新的索引。这是实现复杂窗口、分组偏移的基石。\n- **`translate(long delta)`**：将所有元素的索引平移固定的值（`newIndex = oldIndex + delta`）。\n- **`reverse()`**：反转所有元素的索引（`newIndex = -oldIndex`）。结合排序可以实现倒序。\n\n**重要提示**：索引控制操作（特别是 `redirect`）通常应在操作链的**最后调用**，因为像 `distinct`、`filter`、`sorted` 等操作可能会改变或依赖原有的索引顺序。\n\n#### 调试与副作用\n- **`peek(Consumer\u003cE\u003e)` / `peek(BiConsumer\u003cE, Long\u003e)`**：在不改变流的情况下，对每个元素执行一个操作（如打印日志）。主要用于调试。\n\n#### 连接与排序\n- **`concatenate(Semantic/Iterable/Array)`**：将当前流与另一个数据源连接起来。\n- **`sorted()`**：按元素的自然顺序排序（元素需实现 `Comparable`）。这会返回一个 `OrderedCollectable`。\n- **`sorted(Comparator\u003cE\u003e)`**：按自定义比较器排序，返回 `OrderedCollectable`。\n\n#### 并行处理\n- **`parallel()`**：将流的并发度在当前基础上加1。\n- **`parallel(long concurrent)`**：设置流的并发度。这为下游的 `Collector` 并行收集提供了提示。\n\n### 终端操作与收集\n\n流必须通过一个**终端操作**来触发计算并产生结果。在 Semantic-Java 中，这主要通过将流转换为 `Collectable` 或 `Statistics`，然后调用 `collect` 方法实现。\n\n#### 转换为可收集状态\n- **`toOrdered()`**：转换为一个保持元素顺序的可收集流。内部可能使用 `TreeMap` 基于索引排序，适用于需要顺序结果的场景。**注意**：可能产生 O(n log n) 的时间和 O(n) 的空间开销。\n- **`toUnordered()`**：转换为一个不保证顺序的可收集流。内部使用 `HashMap`，性能通常优于 `toOrdered`。当结果顺序不重要时首选此方法。\n- **`toIntStatistics()` / `toDoubleStatistics()` 等**：转换为特定数值类型的统计流。这些类继承自 `OrderedCollectable`，并额外提供了 `sum()`, `average()`, `max()`, `min()`, `count()` 等便捷的统计方法。\n- **`toWindow()`**：**新增方法**，转换为一个窗口可收集流。返回 `WindowCollectable\u003cE\u003e` 实例，支持窗口操作。\n\n#### 窗口操作 (`WindowCollectable\u003cE\u003e`)\n\n窗口操作是流处理中处理时间序列和滑动聚合的核心功能。`WindowCollectable` 继承自 `OrderedCollectable`，这意味着它内部维护了一个有序的缓冲区（基于 `TreeMap\u003cLong, E\u003e`），这使得窗口操作能够高效地进行。\n\n- **`slide(long size, long step)`**：创建滑动窗口。\n    - `size`：窗口大小，即每个窗口包含的元素数量\n    - `step`：滑动步长，即窗口每次移动的元素数量\n    - 返回一个 `Semantic\u003cSemantic\u003cE\u003e\u003e`，即一个流的流，其中每个内层流代表一个窗口\n\n- **`tumble(long size)`**：创建翻滚窗口（滑动窗口的特例，步长等于窗口大小）。\n    - `size`：窗口大小\n    - 等价于 `slide(size, size)`\n    - 窗口之间不重叠，每个元素只属于一个窗口\n\n**窗口操作示例**：\n```java\n// 计算每3个元素的滑动窗口平均值（步长为1）\nList\u003cDouble\u003e prices = Arrays.asList(100.0, 101.0, 102.0, 103.0, 104.0);\n\nList\u003cDouble\u003e movingAverages = Semantic.useFrom(prices)\n    .toWindow()  // 转换为窗口可收集流\n    .slide(3, 1) // 创建大小为3，步长为1的滑动窗口\n    .map(window -\u003e window.collect(Collectors.useReduce(0.0, Double::sum)) / 3)\n    .toUnordered()\n    .collect(Collectors.toList());\n\nSystem.out.println(movingAverages); \n// 输出: [101.0, 102.0, 103.0]\n// 解释: \n// 窗口1: [100.0, 101.0, 102.0] -\u003e 平均值 101.0\n// 窗口2: [101.0, 102.0, 103.0] -\u003e 平均值 102.0\n// 窗口3: [102.0, 103.0, 104.0] -\u003e 平均值 103.0\n```\n\n**重要提示**：窗口操作需要在流被完全物化到内存中之后才能进行，因为需要知道所有元素的位置来计算窗口。这意味着 `toWindow()` 会触发流的完全消费并将其存储到内部缓冲区中。对于无限流或非常大的流，请谨慎使用窗口操作。\n\n#### 收集器 (`Collectors` 工具类)\n\n`Collectors` 类包含了丰富的静态工厂方法，用于创建各种 `Collector`。\n\n**归约与聚合**\n- **`toList()` / `toHashSet()` / `useToTreeSet()`**：收集到标准集合。\n- **`useToHashMap(Function\u003cE,K\u003e)`**：根据键提取函数收集到 `HashMap`。\n- **`useToTreeMap(Function\u003cE,K\u003e, Function\u003cE,V\u003e)`**：根据键和值提取函数收集到排序的 `TreeMap`。\n- **`useReduce(identity, operator)`**：使用给定的初始值和结合操作符进行归约。\n- **`useCount()`**：统计元素数量。\n\n**查找与匹配**\n- **`useFindFirst()` / `useFindLast()` / `useFindAny()`**：查找第一个/最后一个/任意一个元素。\n- **`useFindAt(long index)`**：查找指定（非负）索引处的元素。\n- **`useFindNegativeAt(long index)`**：查找指定（负）索引处的元素（支持从末尾倒数的索引，如 -1 表示最后一个）。\n- **`useFindMaximum()` / `useFindMinimum()`**：查找最大/最小元素。\n- **`useAnyMatch()` / `useAllMatch()` / `useNoneMatch()`**：判断流中是否存在/全部/没有满足条件的元素。\n\n**分组与分区**\n- **`useGroup(Function\u003cE,K\u003e)`**：根据键提取函数分组，结果为 `Map\u003cK, List\u003cE\u003e\u003e`。\n- **`useGroupBy(Function\u003cE,K\u003e, Function\u003cE,V\u003e)`**：根据键和值提取函数分组，结果为 `Map\u003cK, List\u003cV\u003e\u003e`。\n- **`usePartition(long n)`**：将元素按索引模 `n` 均匀分区成 `n` 个列表。\n- **`usePartitionBy(Function\u003cE,Long\u003e)`**：根据元素值计算的分区键进行分区。\n\n**连接字符串**\n- **`useJoin()`**：用 `\", \"` 连接，并用 `[]` 包裹。\n- **`useJoin(delimiter)`**\n- **`useJoin(prefix, delimiter, suffix)`**\n\n**频率与模式**\n- **`useFrequency()`**：计算每个元素出现的频率，结果为 `Map\u003cE, Long\u003e`。\n- **`useMode()`**：找出出现次数最多的元素（众数）。\n\n**遍历**\n- **`useForEach(Consumer)`**：对每个元素执行操作，并返回处理过的元素数量。\n\n### 统计类 (`Statistics`)\n\n对于数值流，直接使用对应的 `Statistics` 类是最便捷的方式。它们内部优化了统计计算。\n\n```java\nDoubleStatistics\u003cDouble\u003e stats = Semantic.useFrom(new Double[]{1.5, 2.5, 3.5, 4.5})\n        .filter(x -\u003e x \u003e 2.0)\n        .toDoubleStatistics(); // 转换为Double统计流\n\nSystem.out.println(\"Count: \" + stats.count());\nSystem.out.println(\"Sum: \" + stats.sum());\nSystem.out.println(\"Average: \" + stats.average());\nSystem.out.println(\"Min: \" + stats.min());\nSystem.out.println(\"Max: \" + stats.max());\n\n// 当然，也可以使用collect终端操作\nDouble sum = stats.collect(Collectors.useReduce(0.0, Double::sum));\n```\n\n## 深入探讨：高级模式与最佳实践\n\n### 场景一：基于时间窗口的移动平均（使用原生窗口API）\n\n假设我们有一个按时间戳排序的股票价格流，我们想计算每3个数据点为一个窗口的移动平均。使用新增的窗口API，这变得非常简单：\n\n```java\n// 模拟股票价格数据\nList\u003cDouble\u003e prices = Arrays.asList(100.0, 101.0, 102.0, 103.0, 104.0);\n\n// 计算窗口大小为3的移动平均\nList\u003cDouble\u003e movingAverages = Semantic.useFrom(prices)\n    .toWindow()                     // 转换为窗口可收集流\n    .slide(3, 1)                    // 创建大小为3，步长为1的滑动窗口\n    .map(window -\u003e window           // 对每个窗口计算平均值\n        .toUnordered()\n        .collect(Collectors.useReduce(0.0, Double::sum)) / 3\n    )\n    .toUnordered()\n    .collect(Collectors.toList());\n\nSystem.out.println(movingAverages); // 输出: [101.0, 102.0, 103.0]\n```\n\n### 场景二：翻滚窗口聚合\n\n计算每2个元素一个窗口的翻滚窗口求和：\n\n```java\nList\u003cInteger\u003e numbers = Arrays.asList(1, 2, 3, 4, 5, 6);\n\nList\u003cInteger\u003e windowSums = Semantic.useFrom(numbers)\n    .toWindow()\n    .tumble(2)  // 翻滚窗口，大小为2\n    .map(window -\u003e window.collect(Collectors.useReduce(0, Integer::sum)))\n    .toUnordered()\n    .collect(Collectors.toList());\nSystem.out.println(windowSums); // 输出: [3, 7, 11]\n// 解释: [1+2=3, 3+4=7, 5+6=11]\n```\n\n### 场景三：分页与偏移\n\n从流中取出第2页的数据（每页10条）。\n\n```java\nList\u003cString\u003e allItems = infinity(); // 假设是一个很大的列表\n\nList\u003cString\u003e page2 = Semantic.useFrom(allItems)\n        .skip(10)   // 跳过第一页的10条\n        .limit(10)  // 取第二页的10条\n        .toUnordered()\n        .collect(Collectors.toList());\n```\n\n### 场景四：处理带逻辑位置的数据\n\n假设我们有一系列事件，但某些事件需要被插入到前面（如优先级更高的事件）。\n\n```java\nList\u003cEvent\u003e events = getEvents(); // 按时间顺序\nList\u003cEvent\u003e highPriorityEvents = getHighPriorityEvents();\n\n// 将高优先级事件\"插入\"到流的最前面\nList\u003cEvent\u003e processedStream = Semantic.useFrom(highPriorityEvents)\n        .translate(-1000000) // 给高优先级事件一个极小的索引，确保排序在最前\n        .concatenate(Semantic.useFrom(events))\n        .toOrdered() // 根据索引排序\n        .collect(Collectors.toList());\n```\n\n## 设计理念与最佳实践\n\n### 惰性求值与短路\n\n与 Java Streams 一样，Semantic-Java 的中间操作是惰性的。这意味着直到终端操作被调用，数据才会被处理。这带来了巨大的优化潜力：\n\n- **无限流**：可以表示和处理无限序列（例如，生成器生成的随机数序列），因为只在需要时才计算下一个元素。\n- **性能优化**：如果终端操作是短路操作（如 `findFirst`、`anyMatch`），并且中间操作如 `filter` 和 `takeWhile` 能够提前确定结果，那么流可能不会处理所有元素。\n\n**最佳实践**：将能过滤掉大量元素的操作（如 `filter`、`distinct`、`takeWhile`、`limit`、`skip`、`sub`）**尽早**放在操作链中，以减少后续不必要的计算。\n\n### 索引的生命周期管理\n\n理解索引的传递和覆盖规则对正确使用框架至关重要：\n\n1.  **索引来源**：创建流时，元素被赋予初始索引（通常从0开始递增）。\n2.  **索引传递**：大多数操作（如 `filter`、`map`、`peek`）会保留元素的原始索引。`filter` 虽然移除元素，但剩余元素保持其原有索引。\n3.  **索引覆盖**：某些操作会重新计算或覆盖索引：\n    - `distinct`、`flatMap`、`sorted` 等操作会产生新的索引序列。`distinct` 后的元素索引是从1开始的新序列。`sorted` 会打乱原有的物理顺序，但 `OrderedCollectable` 会根据排序后的顺序重新分配逻辑索引用于收集。\n4.  **索引重定向**：`redirect`、`translate`、`reverse` 是显式修改索引的操作。由于它们直接操作索引，**通常应放在操作链的最后**（在排序、去重等可能依赖或改变索引的操作之后），以确保得到预期的索引逻辑。\n\n**规则总结**：先过滤和转换，再排序，最后进行索引重定向。\n\n### 窗口操作的使用时机\n\n窗口操作是一个强大的特性，但需要注意其使用时机：\n\n1.  **物化要求**：窗口操作需要知道所有元素的位置，因此 `toWindow()` 会触发流的完全消费。对于非常大的数据集，请确保有足够的内存。\n2.  **性能考虑**：滑动窗口会创建多个重叠的窗口，每个窗口都是原始数据的一个视图。如果窗口很大且重叠多，可能会产生较大的开销。\n3.  **适用场景**：窗口操作最适合中等大小的数据集，或者可以分批次处理的数据流。对于无限流，考虑使用 `limit()` 限制处理的数据量。\n\n### 并行处理\n\n虽然 `Collector` 的 `collect` 方法包含了基于 `ThreadPoolExecutor` 的并行收集逻辑，`Semantic` 类也有 `concurrent` 属性和 `parallel()` 方法，但`Semantic` 的中间操作本身并未实现并行执行。并行性主要体现在终端收集器（`Collector.collect(generator, concurrent \u003e 1)`）上。收集器可以将工作负载拆分到多个线程，然后合并结果。\n\n**使用建议**：对于计算密集型的终端操作（如复杂的归约、大列表分组），且数据量足够大时，可以尝试使用 `.parallel().toUnordered()` 然后配合支持并行的 `Collector` 来获得性能提升。对于简单的操作或小数据量，顺序执行通常更高效。\n\n## 总结\n\nSemantic-Java 是一个设计精良、思想前卫的 Java 流处理框架。它通过引入**索引控制**和**原生窗口操作**等核心抽象，极大地扩展了流处理的能力边界，使其能够优雅地应对时间序列、事件流等需要复杂位置逻辑的场景。其零依赖的特性和对 Java 函数式编程的深度融合，使得集成和使用异常轻便。\n\n**核心价值回顾**：\n1.  **强大而直观的 API**：融合了熟悉的 Stream 操作、创新的索引操作和原生窗口操作。\n2.  **无与伦比的灵活性**：`redirect` 等操作赋予开发者对数据流逻辑视图的完全控制力。\n3.  **原生窗口支持**：`WindowCollectable` 提供了开箱即用的滑动窗口和翻滚窗口操作，极大地简化了时间序列分析。\n4.  **卓越的性能基础**：惰性求值和短路优化保证了处理大规模甚至无限流时的效率。\n5.  **开箱即用的工具集**：丰富的 `Collectors` 和专门的 `Statistics` 类覆盖了绝大多数终端处理需求。\n\n**与 Java Stream 的对比优势**：相较于标准的 Java Stream API，Semantic-Java 在保持相似易用性的同时，提供了**元素索引**和**原生窗口操作**这两大关键增强。这使得它在处理有序数据、时间序列分析、分页、数据重排等场景下，代码更简洁、意图更清晰、能力更强大。\n\nSemantic-Java 不仅是一个工具库，更代表了一种处理流式数据的新思路。它鼓励开发者以更声明式、更富于语义的方式来思考和构建数据管道。随着函数式编程在 Java 社区的日益普及，Semantic-Java 这样的框架无疑将在大数据处理、实时计算和系统集成等领域发挥越来越重要的作用。\n\n开始使用 Semantic-Java，重新定义您的 Java 流处理体验。\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feloyhere%2Fsemantic-java","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feloyhere%2Fsemantic-java","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feloyhere%2Fsemantic-java/lists"}