├── .classpath ├── .github └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .idea ├── benchmark.iml ├── codeStyleSettings.xml ├── codeStyles │ └── codeStyleConfig.xml ├── compiler.xml ├── copyright │ ├── StreamEx.xml │ └── profiles_settings.xml ├── encodings.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── libraries │ ├── Maven__junit_junit_4_13_2.xml │ ├── Maven__org_hamcrest_hamcrest_core_1_3.xml │ └── Maven__org_openjdk_jmh_jmh_core_1_21.xml ├── markdown.xml ├── modules.xml ├── streamex-16.iml ├── streamex-9.iml ├── streamex.iml └── vcs.xml ├── .project ├── .settings ├── org.eclipse.core.resources.prefs ├── org.eclipse.jdt.core.prefs ├── org.eclipse.jdt.ui.prefs ├── org.eclipse.m2e.core.prefs └── org.eclipse.pde.core.prefs ├── LICENSE ├── README.md ├── benchmark ├── pom.xml └── src │ └── java │ └── main │ └── one │ └── util │ └── streamex │ └── benchmark │ └── prefix │ ├── PrefixBenchmark.java │ ├── PrefixDoubleStreamBenchmark.java │ ├── PrefixIntStreamBenchmark.java │ ├── PrefixLongStreamBenchmark.java │ └── PrefixObjectStreamBenchmark.java ├── pom.xml ├── src ├── main │ ├── java-mr │ │ ├── 9 │ │ │ ├── module-info.java │ │ │ └── one │ │ │ │ └── util │ │ │ │ └── streamex │ │ │ │ ├── Java9Specific.java │ │ │ │ └── VerSpec.java │ │ └── 16 │ │ │ └── one │ │ │ └── util │ │ │ └── streamex │ │ │ ├── Java16Specific.java │ │ │ └── VerSpec.java │ └── java │ │ └── one │ │ └── util │ │ └── streamex │ │ ├── AbstractStreamEx.java │ │ ├── BaseStreamEx.java │ │ ├── CancellableCollector.java │ │ ├── CharSpliterator.java │ │ ├── CollapseSpliterator.java │ │ ├── CombinationSpliterator.java │ │ ├── ConstSpliterator.java │ │ ├── CrossSpliterator.java │ │ ├── DistinctSpliterator.java │ │ ├── DoubleCollector.java │ │ ├── DoubleStreamEx.java │ │ ├── EmitterSpliterator.java │ │ ├── EntryStream.java │ │ ├── HeadTailSpliterator.java │ │ ├── IfEmptySpliterator.java │ │ ├── IntCollector.java │ │ ├── IntStreamEx.java │ │ ├── Internals.java │ │ ├── Joining.java │ │ ├── Limiter.java │ │ ├── LongCollector.java │ │ ├── LongStreamEx.java │ │ ├── MergingCollector.java │ │ ├── MoreCollectors.java │ │ ├── OrderedCancellableSpliterator.java │ │ ├── PairPermutationSpliterator.java │ │ ├── PairSpliterator.java │ │ ├── PermutationSpliterator.java │ │ ├── PrefixOps.java │ │ ├── PrependSpliterator.java │ │ ├── RangeBasedSpliterator.java │ │ ├── StreamContext.java │ │ ├── StreamEx.java │ │ ├── TailConcatSpliterator.java │ │ ├── TakeDrop.java │ │ ├── TreeSpliterator.java │ │ ├── UnknownSizeSpliterator.java │ │ ├── UnorderedCancellableSpliterator.java │ │ ├── VerSpec.java │ │ ├── VersionSpecific.java │ │ ├── WithFirstSpliterator.java │ │ ├── ZipSpliterator.java │ │ └── package-info.java └── test │ └── java │ └── one │ └── util │ └── streamex │ ├── AverageLongTest.java │ ├── CharSpliteratorTest.java │ ├── CollapseSpliteratorTest.java │ ├── CombinationSpliteratorTest.java │ ├── ConstSpliteratorTest.java │ ├── CrossSpliteratorTest.java │ ├── DistinctSpliteratorTest.java │ ├── EntryStreamInternalTest.java │ ├── IfEmptySpliteratorTest.java │ ├── InternalsTest.java │ ├── LimiterTest.java │ ├── OrderedCancellableSpliteratorTest.java │ ├── PairPermutationSpliteratorTest.java │ ├── PairSpliteratorTest.java │ ├── PermutationSpliteratorTest.java │ ├── PrependSpliteratorTest.java │ ├── RangeBasedSpliteratorTest.java │ ├── StreamExInternalTest.java │ ├── TailConcatSpliteratorTest.java │ ├── TestHelpers.java │ ├── TreeSpliteratorTest.java │ ├── UnknownSizeSpliteratorTest.java │ ├── UnorderedCancellableSpliteratorTest.java │ ├── WithFirstSpliteratorTest.java │ ├── ZipSpliteratorTest.java │ └── api │ ├── BaseStreamExTest.java │ ├── CustomPoolTest.java │ ├── DoubleCollectorTest.java │ ├── DoubleStreamExTest.java │ ├── EmitterTest.java │ ├── EntryStreamTest.java │ ├── IntCollectorTest.java │ ├── IntStreamExTest.java │ ├── JoiningTest.java │ ├── LongCollectorTest.java │ ├── LongStreamExTest.java │ ├── MoreCollectorsTest.java │ ├── PrefixOpsTest.java │ ├── StreamExApiTest.java │ ├── StreamExHeadTailTest.java │ └── StreamExTest.java └── wiki ├── CHANGES.md ├── CHEATSHEET.md └── MIGRATION.md /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: "Release version" 8 | required: true 9 | passphrase: 10 | description: "GPG passphrase" 11 | required: true 12 | 13 | jobs: 14 | release: 15 | name: Release 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout sources 20 | uses: actions/checkout@v2 21 | with: 22 | fetch-depth: 0 23 | 24 | - name: Set up JDK 17 25 | uses: actions/setup-java@v2 26 | with: 27 | java-version: '17' 28 | distribution: 'temurin' 29 | cache: maven 30 | 31 | - name: Cache Maven 32 | uses: actions/cache@v2 33 | with: 34 | path: ~/.m2 35 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} 36 | restore-keys: ${{ runner.os }}-m2 37 | 38 | - name: Set version 39 | run: | 40 | VERSION=${{ github.event.inputs.version }} 41 | echo "Releasing $VERSION" 42 | mvn -B versions:set -DnewVersion=$VERSION -DgenerateBackupPoms=false -f pom.xml 43 | mvn -B versions:set -DnewVersion=$VERSION -DgenerateBackupPoms=false -f benchmark/pom.xml 44 | 45 | - name: Install gpg secret key 46 | run: | 47 | cat <(echo -e "${{ secrets.GPG_PRIVATE_KEY }}") | gpg --batch --import 48 | gpg --list-secret-keys --keyid-format LONG 49 | 50 | - name: Build 51 | run: | 52 | export GPG_TTY=$(tty) 53 | mvn --no-transfer-progress -B --file pom.xml verify -Dgpg.passphrase=${{ github.event.inputs.passphrase }} 54 | 55 | - name: Commit version 56 | run: | 57 | git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" 58 | git config --global user.name "GitHub Action" 59 | git commit -a -m "Releasing version $VERSION" 60 | git push origin 61 | 62 | - name: Release to Maven Central 63 | env: 64 | MAVEN_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 65 | MAVEN_CENTRAL_TOKEN: ${{ secrets.SONATYPE_PASSWORD }} 66 | run: | 67 | export GPG_TTY=$(tty) 68 | mvn --no-transfer-progress -B --file pom.xml \ 69 | -Drepository.url=https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git \ 70 | -Dgpg.passphrase=${{ github.event.inputs.passphrase }} \ 71 | deploy 72 | 73 | - name: Release to GitHub 74 | env: 75 | JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 76 | run: | 77 | mvn -B --file pom.xml jreleaser:release 78 | 79 | - name: JReleaser output 80 | if: always() 81 | uses: actions/upload-artifact@v2 82 | with: 83 | name: jreleaser-logs 84 | path: | 85 | target/jreleaser/trace.log 86 | target/jreleaser/output.properties 87 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Java CI with Maven 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up JDK 17 20 | uses: actions/setup-java@v2 21 | with: 22 | java-version: '17' 23 | distribution: 'temurin' 24 | cache: maven 25 | - name: Build with Maven 26 | env: 27 | COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }} 28 | run: mvn install coveralls:report -Dgpg.skip=true -DrepoToken="$COVERALLS_TOKEN" -B -V --file pom.xml 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /benchmark/target/ 3 | 4 | # Eclipse 5 | /.settings 6 | /.metadata 7 | 8 | # IntelliJ IDEA 9 | /.idea/dictionaries/ 10 | /.idea/workspace.xml 11 | /.idea/uiDesigner.xml 12 | /.idea/misc.xml 13 | /.idea/shelf 14 | /.idea/projectDependencies.xml 15 | /.idea/project-color 16 | 17 | # Mac OS/X 18 | .DS_Store 19 | -------------------------------------------------------------------------------- /.idea/benchmark.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/codeStyleSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/copyright/StreamEx.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__junit_junit_4_13_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_openjdk_jmh_jmh_core_1_21.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/markdown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/streamex-16.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/streamex-9.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/streamex.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | streamex 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.pde.PluginNature 21 | org.eclipse.jdt.core.javanature 22 | org.eclipse.m2e.core.maven2Nature 23 | 24 | 25 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/test/java=UTF-8 4 | encoding/=UTF-8 5 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /.settings/org.eclipse.pde.core.prefs: -------------------------------------------------------------------------------- 1 | BUNDLE_ROOT_PATH=target/classes 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StreamEx 0.8.3 2 | Enhancing the Java Stream API. 3 | 4 | [![Maven Central](https://img.shields.io/maven-central/v/one.util/streamex.svg)](https://maven-badges.herokuapp.com/maven-central/one.util/streamex/) 5 | [![Javadocs](https://www.javadoc.io/badge/one.util/streamex.svg)](https://www.javadoc.io/doc/one.util/streamex) 6 | [![Build Status](https://github.com/amaembo/streamex/actions/workflows/test.yml/badge.svg)](https://github.com/amaembo/streamex/actions/workflows/test.yml) 7 | [![Coverage Status](https://coveralls.io/repos/amaembo/streamex/badge.svg?branch=master&service=github)](https://coveralls.io/github/amaembo/streamex?branch=master) 8 | 9 | This library defines four classes: `StreamEx`, `IntStreamEx`, `LongStreamEx`, `DoubleStreamEx` 10 | that are fully compatible with the Java 8 stream classes and provide many useful additional methods. 11 | Also, the `EntryStream` class is provided which represents a stream of map entries and provides 12 | additional functionality for this case. Finally, there are some useful new collectors defined in `MoreCollectors` 13 | class as well as the primitive collectors concept. 14 | 15 | Full API documentation is available [here](http://amaembo.github.io/streamex/javadoc/). 16 | 17 | Take a look at the [Cheatsheet](wiki/CHEATSHEET.md) for brief introduction to StreamEx! 18 | 19 | Before updating StreamEx check the [migration notes](wiki/MIGRATION.md) and the full list of [changes](wiki/CHANGES.md). 20 | 21 | StreamEx main points are the following: 22 | 23 | * Shorter and convenient ways to do common tasks. 24 | * Better interoperability with older code. 25 | * 100% compatibility with the original JDK streams. 26 | * Friendliness for parallel processing: any new feature takes advantage of parallel streams as much as possible. 27 | * Performance and minimal overhead. Whenever StreamEx allows solving a task using less code compared to the standard JDK 28 | Stream API, it should not be significantly slower than the standard way (and sometimes it's even faster). 29 | 30 | ### Examples 31 | 32 | Collector shortcut methods (toList, toSet, groupingBy, joining, etc.) 33 | ```java 34 | List userNames = StreamEx.of(users).map(User::getName).toList(); 35 | Map> role2users = StreamEx.of(users).groupingBy(User::getRole); 36 | StreamEx.of(1,2,3).joining("; "); // "1; 2; 3" 37 | ``` 38 | 39 | Selecting stream elements of a specific type 40 | ```java 41 | public List elementsOf(NodeList nodeList) { 42 | return IntStreamEx.range(nodeList.getLength()) 43 | .mapToObj(nodeList::item).select(Element.class).toList(); 44 | } 45 | ``` 46 | 47 | Adding elements to a stream 48 | ```java 49 | public List getDropDownOptions() { 50 | return StreamEx.of(users).map(User::getName).prepend("(none)").toList(); 51 | } 52 | 53 | public int[] addValue(int[] arr, int value) { 54 | return IntStreamEx.of(arr).append(value).toArray(); 55 | } 56 | ``` 57 | 58 | Removing unwanted elements and using a stream as an Iterable: 59 | ```java 60 | public void copyNonEmptyLines(Reader reader, Writer writer) throws IOException { 61 | for(String line : StreamEx.ofLines(reader).remove(String::isEmpty)) { 62 | writer.write(line); 63 | writer.write(System.lineSeparator()); 64 | } 65 | } 66 | ``` 67 | 68 | Selecting map keys by value predicate: 69 | ```java 70 | Map nameToRole; 71 | 72 | public Set getEnabledRoleNames() { 73 | return StreamEx.ofKeys(nameToRole, Role::isEnabled).toSet(); 74 | } 75 | ``` 76 | 77 | Operating on key-value pairs: 78 | ```java 79 | public Map> invert(Map> map) { 80 | return EntryStream.of(map).flatMapValues(List::stream).invert().grouping(); 81 | } 82 | 83 | public Map stringMap(Map map) { 84 | return EntryStream.of(map).mapKeys(String::valueOf) 85 | .mapValues(String::valueOf).toMap(); 86 | } 87 | 88 | Map nameToGroup; 89 | 90 | public Map> getGroupMembers(Collection groupNames) { 91 | return StreamEx.of(groupNames).mapToEntry(nameToGroup::get) 92 | .nonNullValues().mapValues(Group::getMembers).toMap(); 93 | } 94 | ``` 95 | 96 | Pairwise differences: 97 | ```java 98 | DoubleStreamEx.of(input).pairMap((a, b) -> b-a).toArray(); 99 | ``` 100 | 101 | Support for byte/char/short/float types: 102 | ```java 103 | short[] multiply(short[] src, short multiplier) { 104 | return IntStreamEx.of(src).map(x -> x*multiplier).toShortArray(); 105 | } 106 | ``` 107 | 108 | Define a custom lazy intermediate operation recursively: 109 | ```java 110 | static StreamEx scanLeft(StreamEx input, BinaryOperator operator) { 111 | return input.headTail((head, tail) -> scanLeft(tail.mapFirst(cur -> operator.apply(head, cur)), operator) 112 | .prepend(head)); 113 | } 114 | ``` 115 | 116 | And more! 117 | 118 | ### License 119 | 120 | This project is licensed under [Apache License, version 2.0](https://www.apache.org/licenses/LICENSE-2.0) 121 | 122 | ### Installation 123 | 124 | Releases are available in [Maven Central](https://repo1.maven.org/maven2/one/util/streamex/) 125 | 126 | Before updating StreamEx check the [migration notes](wiki/MIGRATION.md) and the full list of [changes](wiki/CHANGES.md). 127 | 128 | #### Maven 129 | 130 | Add this snippet to your project's pom.xml `dependencies` section: 131 | 132 | ```xml 133 | 134 | one.util 135 | streamex 136 | 0.8.3 137 | 138 | ``` 139 | 140 | #### Gradle 141 | 142 | Add this snippet to your project's build.gradle `dependencies` section: 143 | 144 | ```groovy 145 | implementation 'one.util:streamex:0.8.3' 146 | ``` 147 | 148 | Pull requests are welcome. 149 | -------------------------------------------------------------------------------- /benchmark/pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 4.0.0 20 | 21 | one.util 22 | streamex-benchmark 23 | 0.8.3 24 | 25 | 26 | 27 | one.util 28 | streamex 29 | ${project.version} 30 | 31 | 32 | org.openjdk.jmh 33 | jmh-core 34 | 1.21 35 | 36 | 37 | org.openjdk.jmh 38 | jmh-generator-annprocess 39 | 1.21 40 | 41 | 42 | 43 | UTF-8 44 | benchmarks 45 | 46 | 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-compiler-plugin 52 | 3.1 53 | 54 | 1.8 55 | 1.8 56 | 57 | -Xlint:all 58 | 59 | -parameters 60 | 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-shade-plugin 66 | 2.2 67 | 68 | 69 | package 70 | 71 | shade 72 | 73 | 74 | ${uberjar.name} 75 | 76 | 77 | org.openjdk.jmh.Main 78 | 79 | 80 | 81 | 82 | 86 | *:* 87 | 88 | META-INF/*.SF 89 | META-INF/*.DSA 90 | META-INF/*.RSA 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /benchmark/src/java/main/one/util/streamex/benchmark/prefix/PrefixBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex.benchmark.prefix; 18 | 19 | import org.openjdk.jmh.runner.Runner; 20 | import org.openjdk.jmh.runner.RunnerException; 21 | import org.openjdk.jmh.runner.options.Options; 22 | import org.openjdk.jmh.runner.options.OptionsBuilder; 23 | 24 | public class PrefixBenchmark { 25 | public static void main(String[] args) throws RunnerException { 26 | Options opt = new OptionsBuilder() 27 | .include(PrefixIntStreamBenchmark.class.getSimpleName()) 28 | .include(PrefixLongStreamBenchmark.class.getSimpleName()) 29 | .include(PrefixDoubleStreamBenchmark.class.getSimpleName()) 30 | .include(PrefixObjectStreamBenchmark.class.getSimpleName()) 31 | .build(); 32 | 33 | new Runner(opt).run(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /benchmark/src/java/main/one/util/streamex/benchmark/prefix/PrefixDoubleStreamBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex.benchmark.prefix; 18 | 19 | import one.util.streamex.DoubleStreamEx; 20 | import org.openjdk.jmh.annotations.Benchmark; 21 | import org.openjdk.jmh.annotations.Param; 22 | import org.openjdk.jmh.annotations.Scope; 23 | import org.openjdk.jmh.annotations.State; 24 | 25 | @State(Scope.Benchmark) 26 | public class PrefixDoubleStreamBenchmark { 27 | @Param({"100000"}) 28 | private int N; 29 | 30 | @Benchmark 31 | public double[] parallelOrdered() { 32 | return DoubleStreamEx.constant(1d, N).parallel().prefix(Double::sum).toArray(); 33 | } 34 | 35 | @Benchmark 36 | public double[] parallelUnordered() { 37 | return DoubleStreamEx.constant(1d, N).unordered().parallel().prefix(Double::sum).toArray(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /benchmark/src/java/main/one/util/streamex/benchmark/prefix/PrefixIntStreamBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex.benchmark.prefix; 18 | 19 | import one.util.streamex.IntStreamEx; 20 | import org.openjdk.jmh.annotations.Benchmark; 21 | import org.openjdk.jmh.annotations.Param; 22 | import org.openjdk.jmh.annotations.Scope; 23 | import org.openjdk.jmh.annotations.State; 24 | 25 | @State(Scope.Benchmark) 26 | public class PrefixIntStreamBenchmark { 27 | @Param({"100000"}) 28 | private int N; 29 | 30 | @Benchmark 31 | public int[] parallelOrdered() { 32 | return IntStreamEx.range(N).parallel().prefix(Integer::sum).toArray(); 33 | } 34 | 35 | @Benchmark 36 | public boolean parallelOrderedShortCircuit() { 37 | return IntStreamEx.range(N).parallel().prefix(Integer::sum).anyMatch(x -> x == -1); 38 | } 39 | 40 | @Benchmark 41 | public int[] parallelUnordered() { 42 | return IntStreamEx.range(N).unordered().parallel().prefix(Integer::sum).toArray(); 43 | } 44 | 45 | @Benchmark 46 | public boolean parallelUnorderedShortCircuit() { 47 | return IntStreamEx.range(N).unordered().parallel().prefix(Integer::sum).anyMatch(x -> x == -1); 48 | } 49 | 50 | @Benchmark 51 | public int[] sequentialOrdered() { 52 | return IntStreamEx.range(N).prefix(Integer::sum).toArray(); 53 | } 54 | 55 | @Benchmark 56 | public boolean sequentialOrderedShortCircuit() { 57 | return IntStreamEx.range(N).prefix(Integer::sum).anyMatch(x -> x == -1); 58 | } 59 | 60 | @Benchmark 61 | public int[] sequentialUnordered() { 62 | return IntStreamEx.range(N).unordered().prefix(Integer::sum).toArray(); 63 | } 64 | 65 | @Benchmark 66 | public boolean sequentialUnorderedShortCircuit() { 67 | return IntStreamEx.range(N).unordered().prefix(Integer::sum).anyMatch(x -> x == -1); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /benchmark/src/java/main/one/util/streamex/benchmark/prefix/PrefixLongStreamBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex.benchmark.prefix; 18 | 19 | import one.util.streamex.LongStreamEx; 20 | import org.openjdk.jmh.annotations.Benchmark; 21 | import org.openjdk.jmh.annotations.Param; 22 | import org.openjdk.jmh.annotations.Scope; 23 | import org.openjdk.jmh.annotations.State; 24 | 25 | @State(Scope.Benchmark) 26 | public class PrefixLongStreamBenchmark { 27 | @Param({"100000"}) 28 | private int N; 29 | 30 | @Benchmark 31 | public long[] parallelOrdered() { 32 | return LongStreamEx.range(N).parallel().prefix(Long::sum).toArray(); 33 | } 34 | 35 | @Benchmark 36 | public boolean parallelOrderedShortCircuit() { 37 | return LongStreamEx.range(N).parallel().prefix(Long::sum).anyMatch(x -> x == -1); 38 | } 39 | 40 | @Benchmark 41 | public long[] parallelUnordered() { 42 | return LongStreamEx.range(N).unordered().parallel().prefix(Long::sum).toArray(); 43 | } 44 | 45 | @Benchmark 46 | public boolean parallelUnorderedShortCircuit() { 47 | return LongStreamEx.range(N).unordered().parallel().prefix(Long::sum).anyMatch(x -> x == -1); 48 | } 49 | 50 | @Benchmark 51 | public long[] sequentialOrdered() { 52 | return LongStreamEx.range(N).prefix(Long::sum).toArray(); 53 | } 54 | 55 | @Benchmark 56 | public boolean sequentialOrderedShortCircuit() { 57 | return LongStreamEx.range(N).prefix(Long::sum).anyMatch(x -> x == -1); 58 | } 59 | 60 | @Benchmark 61 | public long[] sequentialUnordered() { 62 | return LongStreamEx.range(N).unordered().prefix(Long::sum).toArray(); 63 | } 64 | 65 | @Benchmark 66 | public boolean sequentialUnorderedShortCircuit() { 67 | return LongStreamEx.range(N).unordered().prefix(Long::sum).anyMatch(x -> x == -1); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /benchmark/src/java/main/one/util/streamex/benchmark/prefix/PrefixObjectStreamBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex.benchmark.prefix; 18 | 19 | 20 | import one.util.streamex.LongStreamEx; 21 | import org.openjdk.jmh.annotations.Benchmark; 22 | import org.openjdk.jmh.annotations.Param; 23 | import org.openjdk.jmh.annotations.Scope; 24 | import org.openjdk.jmh.annotations.State; 25 | 26 | @State(Scope.Benchmark) 27 | public class PrefixObjectStreamBenchmark { 28 | @Param({"100000"}) 29 | private int N; 30 | 31 | @Benchmark 32 | public Object[] parallelOrdered() { 33 | return LongStreamEx.range(N).boxed().parallel().prefix(Long::sum).toArray(); 34 | } 35 | 36 | @Benchmark 37 | public boolean parallelOrderedShortCircuit() { 38 | return LongStreamEx.range(N).boxed().parallel().prefix(Long::sum).anyMatch(x -> x == -1); 39 | } 40 | 41 | @Benchmark 42 | public Object[] parallelUnordered() { 43 | return LongStreamEx.range(N).boxed().unordered().parallel().prefix(Long::sum).toArray(); 44 | } 45 | 46 | @Benchmark 47 | public boolean parallelUnorderedShortCircuit() { 48 | return LongStreamEx.range(N).boxed().unordered().parallel().prefix(Long::sum).anyMatch(x -> x == -1); 49 | } 50 | 51 | @Benchmark 52 | public Object[] sequentialOrdered() { 53 | return LongStreamEx.range(N).boxed().prefix(Long::sum).toArray(); 54 | } 55 | 56 | @Benchmark 57 | public boolean sequentialOrderedShortCircuit() { 58 | return LongStreamEx.range(N).boxed().prefix(Long::sum).anyMatch(x -> x == -1); 59 | } 60 | 61 | @Benchmark 62 | public Object[] sequentialUnordered() { 63 | return LongStreamEx.range(N).boxed().unordered().prefix(Long::sum).toArray(); 64 | } 65 | 66 | @Benchmark 67 | public boolean sequentialUnorderedShortCircuit() { 68 | return LongStreamEx.range(N).boxed().unordered().prefix(Long::sum).anyMatch(x -> x == -1); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java-mr/16/one/util/streamex/Java16Specific.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import java.util.function.*; 19 | import java.util.stream.DoubleStream; 20 | import java.util.stream.IntStream; 21 | import java.util.stream.LongStream; 22 | import java.util.stream.Stream; 23 | 24 | /** 25 | * @author Tagir Valeev 26 | */ 27 | /* package */ class Java16Specific extends VersionSpecific { 28 | @Override 29 | > S callWhile(AbstractStreamEx stream, Predicate predicate, boolean drop) { 30 | Stream upStream = stream.stream(); 31 | return stream.supply(drop ? upStream.dropWhile(predicate) : upStream.takeWhile(predicate)); 32 | } 33 | 34 | @Override 35 | final IntStreamEx callWhile(IntStreamEx stream, IntPredicate predicate, boolean drop) { 36 | IntStream upStream = stream.stream(); 37 | return new IntStreamEx(drop ? upStream.dropWhile(predicate) : upStream.takeWhile(predicate), stream.context); 38 | } 39 | 40 | @Override 41 | final LongStreamEx callWhile(LongStreamEx stream, LongPredicate predicate, boolean drop) { 42 | LongStream upStream = stream.stream(); 43 | return new LongStreamEx(drop ? upStream.dropWhile(predicate) : upStream.takeWhile(predicate), stream.context); 44 | } 45 | 46 | @Override 47 | final DoubleStreamEx callWhile(DoubleStreamEx stream, DoublePredicate predicate, boolean drop) { 48 | DoubleStream upStream = stream.stream(); 49 | return new DoubleStreamEx(drop ? upStream.dropWhile(predicate) : upStream.takeWhile(predicate), stream.context); 50 | } 51 | 52 | @Override 53 | IntStream ofChars(CharSequence seq) { 54 | return seq.chars(); 55 | } 56 | 57 | @Override 58 | StreamEx callMapMulti(AbstractStreamEx s, BiConsumer> mapper) { 59 | return new StreamEx<>(s.stream().mapMulti(mapper), s.context); 60 | } 61 | 62 | @Override 63 | IntStreamEx callMapMultiToInt(AbstractStreamEx s, BiConsumer mapper) { 64 | return new IntStreamEx(s.stream().mapMultiToInt(mapper), s.context); 65 | } 66 | 67 | @Override 68 | LongStreamEx callMapMultiToLong(AbstractStreamEx s, BiConsumer mapper) { 69 | return new LongStreamEx(s.stream().mapMultiToLong(mapper), s.context); 70 | } 71 | 72 | @Override 73 | DoubleStreamEx callMapMultiToDouble(AbstractStreamEx s, BiConsumer mapper) { 74 | return new DoubleStreamEx(s.stream().mapMultiToDouble(mapper), s.context); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java-mr/16/one/util/streamex/VerSpec.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex; 18 | 19 | /** 20 | * @author Tagir Valeev 21 | */ 22 | /* package */ interface VerSpec { 23 | VersionSpecific VER_SPEC = new Java16Specific(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java-mr/9/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * This library provides enhancements for Java 8 Stream API. 18 | * See package documentation for more information. 19 | */ 20 | module one.util.streamex { 21 | exports one.util.streamex; 22 | } -------------------------------------------------------------------------------- /src/main/java-mr/9/one/util/streamex/Java9Specific.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import java.util.function.DoublePredicate; 19 | import java.util.function.IntPredicate; 20 | import java.util.function.LongPredicate; 21 | import java.util.function.Predicate; 22 | import java.util.stream.DoubleStream; 23 | import java.util.stream.IntStream; 24 | import java.util.stream.LongStream; 25 | import java.util.stream.Stream; 26 | 27 | /** 28 | * @author Tagir Valeev 29 | */ 30 | /* package */ class Java9Specific extends VersionSpecific { 31 | @Override 32 | > S callWhile(AbstractStreamEx stream, Predicate predicate, boolean drop) { 33 | Stream upStream = stream.stream(); 34 | return stream.supply(drop ? upStream.dropWhile(predicate) : upStream.takeWhile(predicate)); 35 | } 36 | 37 | @Override 38 | final IntStreamEx callWhile(IntStreamEx stream, IntPredicate predicate, boolean drop) { 39 | IntStream upStream = stream.stream(); 40 | return new IntStreamEx(drop ? upStream.dropWhile(predicate) : upStream.takeWhile(predicate), stream.context); 41 | } 42 | 43 | @Override 44 | final LongStreamEx callWhile(LongStreamEx stream, LongPredicate predicate, boolean drop) { 45 | LongStream upStream = stream.stream(); 46 | return new LongStreamEx(drop ? upStream.dropWhile(predicate) : upStream.takeWhile(predicate), stream.context); 47 | } 48 | 49 | @Override 50 | final DoubleStreamEx callWhile(DoubleStreamEx stream, DoublePredicate predicate, boolean drop) { 51 | DoubleStream upStream = stream.stream(); 52 | return new DoubleStreamEx(drop ? upStream.dropWhile(predicate) : upStream.takeWhile(predicate), stream.context); 53 | } 54 | 55 | @Override 56 | IntStream ofChars(CharSequence seq) { 57 | return seq.chars(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java-mr/9/one/util/streamex/VerSpec.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex; 18 | 19 | /** 20 | * @author Tagir Valeev 21 | */ 22 | /* package */ interface VerSpec { 23 | VersionSpecific VER_SPEC = new Java9Specific(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/CancellableCollector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex; 18 | 19 | import java.util.function.Predicate; 20 | import java.util.stream.Collector; 21 | 22 | /* package */ abstract class CancellableCollector implements Collector { 23 | abstract Predicate finished(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/CharSpliterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import java.util.Spliterator; 19 | import java.util.function.Consumer; 20 | 21 | /** 22 | * @author Tagir Valeev 23 | */ 24 | /* package */class CharSpliterator implements Spliterator { 25 | private final CharSequence source; 26 | private final char delimiter; 27 | private int pos; 28 | private final int fence; 29 | private int nEmpty; 30 | private String next; 31 | private final boolean trimEmpty; 32 | 33 | CharSpliterator(CharSequence source, char delimiter, boolean trimEmpty) { 34 | this.source = source; 35 | this.delimiter = delimiter; 36 | this.fence = source.length(); 37 | this.trimEmpty = trimEmpty; 38 | } 39 | 40 | // Create prefix spliterator and update suffix fields 41 | private CharSpliterator(CharSpliterator suffix, int fence, boolean trimEmpty, int suffixNEmpty, int suffixPos) { 42 | this.source = suffix.source; 43 | this.delimiter = suffix.delimiter; 44 | this.fence = fence; 45 | this.trimEmpty = trimEmpty; 46 | 47 | this.pos = suffix.pos; 48 | suffix.pos = suffixPos; 49 | this.nEmpty = suffix.nEmpty; 50 | suffix.nEmpty = suffixNEmpty; 51 | this.next = suffix.next; 52 | suffix.next = null; 53 | } 54 | 55 | private int next(int pos) { 56 | if (pos == fence) 57 | return pos; 58 | if (source instanceof String) { 59 | int nextPos = ((String) source).indexOf(delimiter, pos); 60 | return nextPos == -1 ? fence : nextPos; 61 | } 62 | while (pos < fence) { 63 | if (source.charAt(pos) == delimiter) 64 | return pos; 65 | pos++; 66 | } 67 | return fence; 68 | } 69 | 70 | @Override 71 | public boolean tryAdvance(Consumer action) { 72 | if (nEmpty > 0) { 73 | nEmpty--; 74 | action.accept(""); 75 | return true; 76 | } 77 | if (next != null) { 78 | action.accept(next); 79 | next = null; 80 | return true; 81 | } 82 | if (pos > fence) { 83 | return false; 84 | } 85 | int nextPos = next(pos); 86 | if (trimEmpty) { 87 | while (nextPos == pos && nextPos != fence) { 88 | nEmpty++; 89 | nextPos = next(++pos); 90 | } 91 | } 92 | String str = source.subSequence(pos, nextPos).toString(); 93 | pos = nextPos + 1; 94 | if (trimEmpty && nextPos == fence && str.isEmpty()) { 95 | nEmpty = 0; // discard empty strings at the end 96 | return false; 97 | } 98 | if (nEmpty > 0) { 99 | next = str; 100 | nEmpty--; 101 | action.accept(""); 102 | } else 103 | action.accept(str); 104 | return true; 105 | } 106 | 107 | @Override 108 | public Spliterator trySplit() { 109 | int mid = (pos + fence) >>> 1; 110 | int nextPos = next(mid); 111 | if (nextPos == fence) 112 | return null; 113 | if (trimEmpty && nextPos == mid) { 114 | while (nextPos < fence && source.charAt(nextPos) == delimiter) 115 | nextPos++; 116 | return nextPos == fence ? 117 | new CharSpliterator(this, mid, true, 0, nextPos + 1) : 118 | new CharSpliterator(this, mid, false, nextPos - mid - 1, nextPos); 119 | } 120 | return new CharSpliterator(this, nextPos, false, 0, nextPos + 1); 121 | } 122 | 123 | @Override 124 | public long estimateSize() { 125 | return pos > fence ? 0 : fence - pos; 126 | } 127 | 128 | @Override 129 | public int characteristics() { 130 | return NONNULL | ORDERED; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/CombinationSpliterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex; 18 | 19 | import java.util.Spliterator; 20 | import java.util.function.Consumer; 21 | 22 | /* package */ final class CombinationSpliterator implements Spliterator { 23 | private long pos; 24 | private int[] value; 25 | private final long fence; 26 | private final int n; 27 | 28 | public CombinationSpliterator(int n, long pos, long fence, int[] value) { 29 | this.n = n; 30 | this.pos = pos; 31 | this.fence = fence; 32 | this.value = value; 33 | } 34 | 35 | @Override 36 | public void forEachRemaining(Consumer action) { 37 | long rest = pos - fence; 38 | pos = fence; 39 | while (rest > 0) { 40 | action.accept(value.clone()); 41 | if (--rest > 0) { 42 | step(value, n); 43 | } 44 | } 45 | } 46 | 47 | @Override 48 | public Spliterator trySplit() { 49 | if (pos - fence < 2) return null; 50 | long newPos = (fence + pos) >>> 1; 51 | 52 | CombinationSpliterator result = new CombinationSpliterator(n, pos, newPos, value); 53 | value = jump(newPos - 1, value.length, n); 54 | pos = newPos; 55 | return result; 56 | } 57 | 58 | @Override 59 | public long estimateSize() { 60 | return pos - fence; 61 | } 62 | 63 | @Override 64 | public int characteristics() { 65 | return DISTINCT | IMMUTABLE | NONNULL | ORDERED | SIZED | SUBSIZED; 66 | } 67 | 68 | static void step(int[] value, int n) { 69 | int i, k = value.length; 70 | for (i = k; --i >= 0 && ++value[i] >= n; ) { 71 | n--; 72 | } 73 | 74 | while (++i < k) { 75 | value[i] = value[i - 1] + 1; 76 | } 77 | } 78 | 79 | static int[] jump(long newPos, int k, int n) { 80 | int[] newValue = new int[k]; 81 | int curK = k - 1; 82 | long bound = 0; 83 | for (int i = 0; i < k; i++) { 84 | long cnk = 1; 85 | int curN = curK; 86 | while (newPos >= bound + cnk) { 87 | bound += cnk; 88 | curN++; 89 | cnk = cnk * curN / (curN - curK); 90 | } 91 | curK--; 92 | newValue[i] = n - curN - 1; 93 | } 94 | return newValue; 95 | } 96 | 97 | 98 | static long gcd(long a, long b) { 99 | while (b != 0) { 100 | long t = a % b; 101 | a = b; 102 | b = t; 103 | } 104 | return a; 105 | } 106 | 107 | /** 108 | * @param n n > k 109 | * @param k k > 0 110 | * @return CNK(n, k) 111 | */ 112 | static long cnk(int n, int k) { 113 | long size = 1; 114 | 115 | int rest = n; 116 | for (long div = 1; div <= k; ++div, --rest) { 117 | long gcd = gcd(size, div); 118 | size /= gcd; 119 | long t = rest / (div / gcd); 120 | 121 | if (size > Long.MAX_VALUE / t) { 122 | throw new UnsupportedOperationException("Number of combinations exceed Long.MAX_VALUE: unsupported"); 123 | } 124 | 125 | size *= t; 126 | } 127 | return size; 128 | } 129 | 130 | @Override 131 | public boolean tryAdvance(Consumer action) { 132 | if (pos <= fence) { 133 | return false; 134 | } 135 | action.accept(value.clone()); 136 | if (--pos > fence) { 137 | step(value, n); 138 | } 139 | return true; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/ConstSpliterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import one.util.streamex.Internals.CloneableSpliterator; 19 | 20 | import java.util.Spliterator; 21 | import java.util.function.Consumer; 22 | import java.util.function.DoubleConsumer; 23 | import java.util.function.IntConsumer; 24 | import java.util.function.LongConsumer; 25 | 26 | /** 27 | * @author Tagir Valeev 28 | * 29 | */ 30 | /* package */abstract class ConstSpliterator> extends CloneableSpliterator { 31 | long remaining; 32 | private final boolean ordered; 33 | 34 | public ConstSpliterator(long remaining, boolean ordered) { 35 | this.remaining = remaining; 36 | this.ordered = ordered; 37 | } 38 | 39 | @Override 40 | public S trySplit() { 41 | long remaining = this.remaining; 42 | if (remaining >= 2) { 43 | S clone = doClone(); 44 | remaining >>= 1; 45 | clone.remaining = remaining; 46 | this.remaining -= remaining; 47 | return clone; 48 | } 49 | return null; 50 | } 51 | 52 | @Override 53 | public long estimateSize() { 54 | return remaining; 55 | } 56 | 57 | @Override 58 | public int characteristics() { 59 | return SIZED | SUBSIZED | IMMUTABLE | (ordered ? ORDERED : 0); 60 | } 61 | 62 | static final class OfRef extends ConstSpliterator> { 63 | private final T value; 64 | 65 | OfRef(T value, long count, boolean ordered) { 66 | super(count, ordered); 67 | this.value = value; 68 | } 69 | 70 | @Override 71 | public boolean tryAdvance(Consumer action) { 72 | if (remaining <= 0) 73 | return false; 74 | action.accept(value); 75 | remaining--; 76 | return true; 77 | } 78 | 79 | @Override 80 | public void forEachRemaining(Consumer action) { 81 | for (long r = remaining; r > 0; r--) { 82 | action.accept(value); 83 | } 84 | remaining = 0; 85 | } 86 | } 87 | 88 | static final class OfInt extends ConstSpliterator implements Spliterator.OfInt { 89 | private final int value; 90 | 91 | OfInt(int value, long count, boolean ordered) { 92 | super(count, ordered); 93 | this.value = value; 94 | } 95 | 96 | @Override 97 | public boolean tryAdvance(IntConsumer action) { 98 | if (remaining <= 0) 99 | return false; 100 | action.accept(value); 101 | remaining--; 102 | return true; 103 | } 104 | 105 | @Override 106 | public void forEachRemaining(IntConsumer action) { 107 | for (long r = remaining; r > 0; r--) { 108 | action.accept(value); 109 | } 110 | remaining = 0; 111 | } 112 | } 113 | 114 | static final class OfLong extends ConstSpliterator implements Spliterator.OfLong { 115 | private final long value; 116 | 117 | OfLong(long value, long count, boolean ordered) { 118 | super(count, ordered); 119 | this.value = value; 120 | } 121 | 122 | @Override 123 | public boolean tryAdvance(LongConsumer action) { 124 | if (remaining <= 0) 125 | return false; 126 | action.accept(value); 127 | remaining--; 128 | return true; 129 | } 130 | 131 | @Override 132 | public void forEachRemaining(LongConsumer action) { 133 | for (long r = remaining; r > 0; r--) { 134 | action.accept(value); 135 | } 136 | remaining = 0; 137 | } 138 | } 139 | 140 | static final class OfDouble extends ConstSpliterator implements Spliterator.OfDouble { 141 | private final double value; 142 | 143 | OfDouble(double value, long count, boolean ordered) { 144 | super(count, ordered); 145 | this.value = value; 146 | } 147 | 148 | @Override 149 | public boolean tryAdvance(DoubleConsumer action) { 150 | if (remaining <= 0) 151 | return false; 152 | action.accept(value); 153 | remaining--; 154 | return true; 155 | } 156 | 157 | @Override 158 | public void forEachRemaining(DoubleConsumer action) { 159 | for (long r = remaining; r > 0; r--) { 160 | action.accept(value); 161 | } 162 | remaining = 0; 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/DistinctSpliterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import java.util.Comparator; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import java.util.Spliterator; 22 | import java.util.concurrent.ConcurrentHashMap; 23 | import java.util.concurrent.atomic.AtomicLong; 24 | import java.util.function.Consumer; 25 | 26 | import static one.util.streamex.Internals.Box; 27 | 28 | /* package */final class DistinctSpliterator extends Box implements Spliterator { 29 | private final Spliterator source; 30 | private AtomicLong nullCounter; 31 | private Map counts; 32 | private final long atLeast; 33 | 34 | DistinctSpliterator(Spliterator source, long atLeast, AtomicLong nullCounter, Map counts) { 35 | this.source = source; 36 | this.atLeast = atLeast; 37 | this.nullCounter = nullCounter; 38 | this.counts = counts; 39 | } 40 | 41 | DistinctSpliterator(Spliterator source, long atLeast) { 42 | this(source, atLeast, null, new HashMap<>()); 43 | } 44 | 45 | @Override 46 | public boolean tryAdvance(Consumer action) { 47 | if (nullCounter == null) { 48 | while (source.tryAdvance(this)) { 49 | if (counts.merge(a, 1L, Long::sum) == atLeast) { 50 | action.accept(a); 51 | return true; 52 | } 53 | } 54 | } else { 55 | while (source.tryAdvance(this)) { 56 | long count = a == null ? nullCounter.incrementAndGet() : counts.merge(a, 1L, Long::sum); 57 | if (count == atLeast) { 58 | action.accept(a); 59 | return true; 60 | } 61 | } 62 | } 63 | return false; 64 | } 65 | 66 | @Override 67 | public void forEachRemaining(Consumer action) { 68 | if (nullCounter == null) { 69 | source.forEachRemaining(e -> { 70 | if (counts.merge(e, 1L, Long::sum) == atLeast) { 71 | action.accept(e); 72 | } 73 | }); 74 | } else { 75 | source.forEachRemaining(e -> { 76 | long count = e == null ? nullCounter.incrementAndGet() : counts.merge(e, 1L, Long::sum); 77 | if (count == atLeast) { 78 | action.accept(e); 79 | } 80 | }); 81 | } 82 | } 83 | 84 | @Override 85 | public Spliterator trySplit() { 86 | Spliterator split = source.trySplit(); 87 | if (split == null) 88 | return null; 89 | if (counts.getClass() == HashMap.class) { 90 | if (!source.hasCharacteristics(NONNULL)) { 91 | Long current = counts.remove(null); 92 | nullCounter = new AtomicLong(current == null ? 0 : current); 93 | } 94 | counts = new ConcurrentHashMap<>(counts); 95 | } 96 | return new DistinctSpliterator<>(split, atLeast, nullCounter, counts); 97 | } 98 | 99 | @Override 100 | public long estimateSize() { 101 | return source.estimateSize(); 102 | } 103 | 104 | @Override 105 | public int characteristics() { 106 | return DISTINCT | (source.characteristics() & (NONNULL | CONCURRENT | IMMUTABLE | ORDERED | SORTED)); 107 | } 108 | 109 | @Override 110 | public Comparator getComparator() { 111 | return source.getComparator(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/HeadTailSpliterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import java.util.Spliterator; 19 | import java.util.Spliterators; 20 | import java.util.Spliterators.AbstractSpliterator; 21 | import java.util.function.BiFunction; 22 | import java.util.function.Consumer; 23 | import java.util.function.Supplier; 24 | import java.util.stream.Stream; 25 | 26 | import static one.util.streamex.Internals.Box; 27 | import static one.util.streamex.Internals.TailSpliterator; 28 | 29 | /** 30 | * @author Tagir Valeev 31 | */ 32 | /*package*/ final class HeadTailSpliterator extends AbstractSpliterator implements TailSpliterator { 33 | private Spliterator source; 34 | private BiFunction, ? extends Stream> mapper; 35 | private Supplier> emptyMapper; 36 | private Spliterator target; 37 | StreamContext context; 38 | 39 | HeadTailSpliterator(Spliterator source, BiFunction, ? extends Stream> mapper, 40 | Supplier> emptyMapper) { 41 | super(Long.MAX_VALUE, ORDERED); 42 | this.source = source; 43 | this.mapper = mapper; 44 | this.emptyMapper = emptyMapper; 45 | } 46 | 47 | @Override 48 | public boolean tryAdvance(Consumer action) { 49 | if (!init()) 50 | return false; 51 | target = TailSpliterator.tryAdvanceWithTail(target, action); 52 | if (target == null) { 53 | context = null; 54 | return false; 55 | } 56 | return true; 57 | } 58 | 59 | @Override 60 | public Spliterator tryAdvanceOrTail(Consumer action) { 61 | if (!init()) 62 | return null; 63 | Spliterator tail = target; 64 | target = null; 65 | context = null; 66 | return tail; 67 | } 68 | 69 | @Override 70 | public void forEachRemaining(Consumer action) { 71 | if (!init()) 72 | return; 73 | TailSpliterator.forEachWithTail(target, action); 74 | target = null; 75 | context = null; 76 | } 77 | 78 | @Override 79 | public Spliterator forEachOrTail(Consumer action) { 80 | return tryAdvanceOrTail(action); 81 | } 82 | 83 | private boolean init() { 84 | if (context == null) 85 | return false; 86 | if (target == null) { 87 | Box first = new Box<>(); 88 | source = TailSpliterator.tryAdvanceWithTail(source, first); 89 | Stream stream = source == null ? emptyMapper.get() : mapper.apply(first.a, StreamEx.of(source)); 90 | source = null; 91 | mapper = null; 92 | emptyMapper = null; 93 | if (stream == null) { 94 | target = Spliterators.emptySpliterator(); 95 | } else { 96 | StreamContext ctx = StreamContext.of(stream); 97 | if (ctx.closeHandler != null) 98 | context.onClose(ctx.closeHandler); 99 | target = stream.spliterator(); 100 | } 101 | } 102 | return true; 103 | } 104 | 105 | @Override 106 | public long estimateSize() { 107 | if (context == null) 108 | return 0; 109 | return (target == null ? source : target).estimateSize(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/IfEmptySpliterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex; 18 | 19 | import java.util.Spliterator; 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | import java.util.function.Consumer; 22 | 23 | /* package */ class IfEmptySpliterator extends Internals.CloneableSpliterator> { 24 | // alt == null --> decision is made 25 | private Spliterator spltr, alt; 26 | // positive = number of non-exhausted spliterators; negative = surely non-empty 27 | private final AtomicInteger state = new AtomicInteger(1); 28 | 29 | public IfEmptySpliterator(Spliterator spltr, Spliterator alt) { 30 | this.spltr = spltr; 31 | this.alt = alt; 32 | } 33 | 34 | void tryInit() { 35 | if (alt != null && spltr.hasCharacteristics(SIZED)) { 36 | if (spltr.estimateSize() == 0) { 37 | spltr = alt; 38 | } 39 | alt = null; 40 | } 41 | } 42 | 43 | @Override 44 | public boolean tryAdvance(Consumer action) { 45 | if (alt != null) { 46 | if (spltr.tryAdvance(action)) { 47 | state.set(-1); 48 | alt = null; 49 | return true; 50 | } 51 | if (drawState()) { 52 | spltr = alt; 53 | } 54 | alt = null; 55 | } 56 | return spltr.tryAdvance(action); 57 | } 58 | 59 | @Override 60 | public void forEachRemaining(Consumer action) { 61 | tryInit(); 62 | if (alt == null) { 63 | spltr.forEachRemaining(action); 64 | } else { 65 | boolean[] empty = {true}; 66 | spltr.forEachRemaining(e -> { 67 | empty[0] = false; 68 | action.accept(e); 69 | }); 70 | if (empty[0]) { 71 | if (drawState()) { 72 | (spltr = alt).forEachRemaining(action); 73 | } 74 | } else { 75 | state.set(-1); 76 | } 77 | alt = null; 78 | } 79 | } 80 | 81 | boolean drawState() { 82 | return state.updateAndGet(x -> x > 0 ? x - 1 : x) == 0; 83 | } 84 | 85 | @Override 86 | public Spliterator trySplit() { 87 | Spliterator prefix = spltr.trySplit(); 88 | if (prefix == null) { 89 | return null; 90 | } 91 | tryInit(); 92 | if (alt != null) { 93 | state.updateAndGet(x -> x > 0 ? x + 1 : x); 94 | } 95 | IfEmptySpliterator clone = doClone(); 96 | clone.spltr = prefix; 97 | return clone; 98 | } 99 | 100 | @Override 101 | public long estimateSize() { 102 | tryInit(); 103 | long size = spltr.estimateSize(); 104 | return alt != null && size == 0 ? alt.estimateSize() : size; 105 | } 106 | 107 | @Override 108 | public int characteristics() { 109 | if (alt == null) { 110 | return spltr.characteristics() & (~SORTED); 111 | } 112 | return (spltr.characteristics() & alt.characteristics() & (~SORTED)) | 113 | ((spltr.characteristics() | alt.characteristics()) & (ORDERED)); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/Limiter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import java.util.AbstractCollection; 19 | import java.util.Arrays; 20 | import java.util.Comparator; 21 | import java.util.Iterator; 22 | 23 | /** 24 | * Extracts least limit elements from the input sorting them according to the 25 | * given comparator. Works for 2 <= limit < Integer.MAX_VALUE/2. Uses 26 | * O(min(limit, inputSize)) additional memory. 27 | * 28 | * @param type of input elements 29 | * 30 | * @author Tagir Valeev 31 | */ 32 | /* package */class Limiter extends AbstractCollection { 33 | private T[] data; 34 | private final int limit; 35 | private final Comparator comparator; 36 | private int size; 37 | private boolean initial = true; 38 | 39 | @SuppressWarnings("unchecked") 40 | public Limiter(int limit, Comparator comparator) { 41 | this.limit = limit; 42 | this.comparator = comparator; 43 | this.data = (T[]) new Object[Math.min(1000, limit) * 2]; 44 | } 45 | 46 | /** 47 | * Accumulate new element 48 | * 49 | * @param t element to accumulate 50 | * 51 | * @return false if the element is definitely not included into result, so 52 | * any bigger element could be skipped as well, or true if element 53 | * will probably be included into result. 54 | */ 55 | public boolean put(T t) { 56 | if (initial) { 57 | if (size == data.length) { 58 | if (size < limit * 2) { 59 | @SuppressWarnings("unchecked") 60 | T[] newData = (T[]) new Object[Math.min(limit, size) * 2]; 61 | System.arraycopy(data, 0, newData, 0, size); 62 | data = newData; 63 | } else { 64 | Arrays.sort(data, comparator); 65 | initial = false; 66 | size = limit; 67 | } 68 | put(t); 69 | } else { 70 | data[size++] = t; 71 | } 72 | return true; 73 | } 74 | if (size == data.length) { 75 | sortTail(); 76 | } 77 | if (comparator.compare(t, data[limit - 1]) < 0) { 78 | data[size++] = t; 79 | return true; 80 | } 81 | return false; 82 | } 83 | 84 | /** 85 | * Merge other {@code Limiter} object into this (other object becomes unusable after that). 86 | * 87 | * @param ls other object to merge 88 | * @return this object 89 | */ 90 | public Limiter putAll(Limiter ls) { 91 | if (initial && (size + ls.size <= data.length)){ 92 | System.arraycopy(ls.data, 0, data, size, ls.size); 93 | size += ls.size; 94 | return this; 95 | } 96 | int i = 0; 97 | if (!ls.initial) { 98 | // sorted part 99 | for (; i < limit; i++) { 100 | if (!put(ls.data[i])) 101 | break; 102 | } 103 | i = limit; 104 | } 105 | for (; i < ls.size; i++) { 106 | put(ls.data[i]); 107 | } 108 | return this; 109 | } 110 | 111 | private void sortTail() { 112 | // size > limit here 113 | T[] d = data; 114 | int l = limit, s = size; 115 | Comparator cmp = comparator; 116 | Arrays.sort(d, l, s, cmp); 117 | if (cmp.compare(d[s - 1], d[0]) < 0) { 118 | // Common case: descending sequence 119 | // Assume size - limit <= limit here 120 | System.arraycopy(d, 0, d, s - l, 2 * l - s); 121 | System.arraycopy(d, l, d, 0, s - l); 122 | } else { 123 | // Merge presorted 0..limit-1 and limit..size-1 124 | @SuppressWarnings("unchecked") 125 | T[] buf = (T[]) new Object[l]; 126 | int i = 0, j = l, k = 0; 127 | // d[l-1] is guaranteed to be the worst element, thus no need to 128 | // check it 129 | while (i < l - 1 && k < l && j < s) { 130 | if (cmp.compare(d[i], d[j]) <= 0) { 131 | buf[k++] = d[i++]; 132 | } else { 133 | buf[k++] = d[j++]; 134 | } 135 | } 136 | if (k < l) { 137 | System.arraycopy(d, i < l - 1 ? i : j, d, k, l - k); 138 | } 139 | System.arraycopy(buf, 0, d, 0, k); 140 | } 141 | size = l; 142 | } 143 | 144 | /** 145 | * Must be called after accumulation is finished. After calling 146 | * {@code sort()} this Limiter represents the resulting collection. 147 | */ 148 | public void sort() { 149 | if (initial) 150 | Arrays.sort(data, 0, size, comparator); 151 | else if (size > limit) 152 | sortTail(); 153 | } 154 | 155 | @Override 156 | public Object[] toArray() { 157 | return Arrays.copyOfRange(data, 0, size()); 158 | } 159 | 160 | @Override 161 | public Iterator iterator() { 162 | return Arrays.asList(data).subList(0, size()).iterator(); 163 | } 164 | 165 | @Override 166 | public int size() { 167 | return initial && size < limit ? size : limit; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/MergingCollector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex; 18 | 19 | import java.util.function.BiConsumer; 20 | import java.util.function.BinaryOperator; 21 | import java.util.stream.Collector; 22 | 23 | /** 24 | * A {@code MergingCollector} is a {@code Collector} with more specific 25 | * combining algorithm. Instead of providing a combiner which can create new 26 | * partial result the {@code MergingCollector} must provide a merger which 27 | * merges the second partial result into the first one. 28 | * 29 | * @author Tagir Valeev 30 | * 31 | * @param the type of input elements to the reduction operation 32 | * @param the mutable accumulation type of the reduction operation (often 33 | * hidden as an implementation detail) 34 | * @param the result type of the reduction operation 35 | */ 36 | /* package */interface MergingCollector extends Collector { 37 | /** 38 | * A function that merges the second partial result into the first partial 39 | * result. 40 | * 41 | * @return a function that merges the second partial result into the first 42 | * partial result. 43 | */ 44 | BiConsumer merger(); 45 | 46 | /** 47 | * A function that accepts two partial results and combines them returning 48 | * either existing partial result or new one. 49 | * 50 | *

51 | * The default implementation calls the {@link #merger()} and returns the 52 | * first partial result. 53 | * 54 | * @return a function which combines two partial results into a combined 55 | * result 56 | */ 57 | @Override 58 | default BinaryOperator combiner() { 59 | BiConsumer merger = merger(); 60 | return (a, b) -> { 61 | merger.accept(a, b); 62 | return a; 63 | }; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/PairPermutationSpliterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import one.util.streamex.Internals.CloneableSpliterator; 19 | 20 | import java.util.List; 21 | import java.util.Spliterator; 22 | import java.util.function.BiFunction; 23 | import java.util.function.Consumer; 24 | 25 | /** 26 | * @author Tagir Valeev 27 | */ 28 | /* package */final class PairPermutationSpliterator extends CloneableSpliterator> { 29 | private long cur; 30 | private long limit; 31 | private final int size; 32 | private int idx1; 33 | private int idx2; 34 | private final List list; 35 | private final BiFunction mapper; 36 | 37 | public PairPermutationSpliterator(List list, BiFunction mapper) { 38 | this.list = list; 39 | this.size = list.size(); 40 | this.idx2 = 1; 41 | this.limit = size * (size - 1L) / 2; 42 | this.mapper = mapper; 43 | } 44 | 45 | @Override 46 | public long estimateSize() { 47 | return limit - cur; 48 | } 49 | 50 | @Override 51 | public int characteristics() { 52 | return ORDERED | SIZED | SUBSIZED; 53 | } 54 | 55 | /* 56 | * Calculates (int) (Math.sqrt(8 * n + 1)-1)/2 Produces exact result for any 57 | * long input from 0 to 0x1FFFFFFFC0000000L (2^61-2^30). 58 | */ 59 | static int isqrt(long n) { 60 | int x = (int) ((Math.sqrt(8.0 * n + 1.0) - 1.0) / 2.0); 61 | if (x * (x + 1L) / 2 > n) 62 | x--; 63 | return x; 64 | } 65 | 66 | @Override 67 | public Spliterator trySplit() { 68 | long size = limit - cur; 69 | if (size >= 2) { 70 | PairPermutationSpliterator clone = doClone(); 71 | clone.limit = this.cur = this.cur + size / 2; 72 | int s = this.size; 73 | long rev = s * (s - 1L) / 2 - this.cur - 1; 74 | int row = isqrt(rev); 75 | int col = (int) (rev - (row) * (row + 1L) / 2); 76 | this.idx1 = s - row - 2; 77 | this.idx2 = s - col - 1; 78 | return clone; 79 | } 80 | return null; 81 | } 82 | 83 | @Override 84 | public boolean tryAdvance(Consumer action) { 85 | if (cur == limit) 86 | return false; 87 | action.accept(mapper.apply(list.get(idx1), list.get(idx2))); 88 | cur++; 89 | if (++idx2 == size) { 90 | idx2 = ++idx1 + 1; 91 | } 92 | return true; 93 | } 94 | 95 | @Override 96 | public void forEachRemaining(Consumer action) { 97 | int idx1 = this.idx1; 98 | int idx2 = this.idx2; 99 | int size = this.size; 100 | long cur = this.cur; 101 | long limit = this.limit; 102 | while (cur < limit) { 103 | T item1 = list.get(idx1++); 104 | while (cur < limit && idx2 < size) { 105 | T item2 = list.get(idx2++); 106 | action.accept(mapper.apply(item1, item2)); 107 | cur++; 108 | } 109 | idx2 = idx1 + 1; 110 | } 111 | this.cur = this.limit; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/PermutationSpliterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex; 18 | 19 | import java.util.Spliterator; 20 | import java.util.function.Consumer; 21 | 22 | /* package */ final class PermutationSpliterator implements Spliterator { 23 | private static final long[] factorials = { 1L, 1L, 2L, 6L, 24L, 120L, 720L, 5040L, 40320L, 362880L, 24 | 3628800L, 39916800L, 479001600L, 6227020800L, 87178291200L, 1307674368000L, 20922789888000L, 25 | 355687428096000L, 6402373705728000L, 121645100408832000L, 2432902008176640000L }; 26 | 27 | private final int[] value; 28 | private long remainingSize; 29 | private final long fence; 30 | 31 | public PermutationSpliterator(int length) { 32 | Internals.checkNonNegative("Length", length); 33 | if (length >= factorials.length) 34 | throw new IllegalArgumentException("Length " + length + " is bigger than " + factorials.length 35 | + ": not supported"); 36 | this.value = new int[length]; 37 | for (int i = 0; i < length; i++) 38 | this.value[i] = i; 39 | this.fence = this.remainingSize = factorials[length]; 40 | } 41 | 42 | private PermutationSpliterator(int[] startValue, long fence, long remainingSize) { 43 | this.value = startValue; 44 | this.fence = fence; 45 | this.remainingSize = remainingSize; 46 | } 47 | 48 | @Override 49 | public boolean tryAdvance(Consumer action) { 50 | if (remainingSize == 0) 51 | return false; 52 | int[] value = this.value; 53 | action.accept(value.clone()); 54 | if (--remainingSize > 0) { 55 | step(value); 56 | } 57 | return true; 58 | } 59 | 60 | @Override 61 | public void forEachRemaining(Consumer action) { 62 | long rs = remainingSize; 63 | if (rs == 0) 64 | return; 65 | remainingSize = 0; 66 | int[] value = this.value; 67 | action.accept(value.clone()); 68 | while (--rs > 0) { 69 | step(value); 70 | action.accept(value.clone()); 71 | } 72 | } 73 | 74 | private static void step(int[] value) { 75 | int r = value.length - 1, k = r - 1; 76 | while (value[k] > value[k + 1]) 77 | k--; 78 | int vk = value[k], l = r; 79 | while (vk > value[l]) 80 | l--; 81 | value[k] = value[l]; 82 | value[l] = vk; 83 | for (k++; k < r; k++, r--) { 84 | int tmp = value[k]; 85 | value[k] = value[r]; 86 | value[r] = tmp; 87 | } 88 | } 89 | 90 | @Override 91 | public Spliterator trySplit() { 92 | if (remainingSize <= 1) 93 | return null; 94 | int[] newValue = value.clone(); 95 | int used = -1; // clear bit = used position 96 | long newRemainingSize = remainingSize / 2; 97 | long newPos = fence - (remainingSize -= newRemainingSize); 98 | long s = newPos; 99 | for (int i = 0; i < value.length; i++) { 100 | long f = factorials[value.length - i - 1]; 101 | int rem = (int) (s / f); 102 | s %= f; 103 | int idx = -1; 104 | while (rem >= 0) { 105 | idx = Integer.numberOfTrailingZeros(used >> (idx + 1)) + idx + 1; 106 | rem--; 107 | } 108 | used &= ~(1 << idx); 109 | value[i] = idx; 110 | } 111 | return new PermutationSpliterator(newValue, newPos, newRemainingSize); 112 | } 113 | 114 | @Override 115 | public long estimateSize() { 116 | return remainingSize; 117 | } 118 | 119 | @Override 120 | public int characteristics() { 121 | return ORDERED | DISTINCT | NONNULL | IMMUTABLE | SIZED | SUBSIZED; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/PrependSpliterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import one.util.streamex.Internals.TailSpliterator; 19 | 20 | import java.util.Spliterator; 21 | import java.util.function.Consumer; 22 | 23 | /** 24 | * @author Tagir Valeev 25 | * 26 | * @param type of the elements 27 | */ 28 | /* package */class PrependSpliterator implements TailSpliterator { 29 | private Spliterator source; 30 | private T element; 31 | private int mode; 32 | 33 | public PrependSpliterator(Spliterator source, T element) { 34 | this.source = source; 35 | this.element = element; 36 | this.mode = source.estimateSize() < Long.MAX_VALUE - 1 ? 1 : 2; 37 | } 38 | 39 | @Override 40 | public boolean tryAdvance(Consumer action) { 41 | if (mode == 0) 42 | return source.tryAdvance(action); 43 | action.accept(element); 44 | element = null; 45 | mode = 0; 46 | return true; 47 | } 48 | 49 | @Override 50 | public Spliterator tryAdvanceOrTail(Consumer action) { 51 | if (mode == 0) { 52 | Spliterator s = source; 53 | source = null; 54 | return s; 55 | } 56 | action.accept(element); 57 | element = null; 58 | mode = 0; 59 | return this; 60 | } 61 | 62 | @Override 63 | public void forEachRemaining(Consumer action) { 64 | if (mode != 0) 65 | action.accept(element); 66 | element = null; 67 | mode = 0; 68 | source.forEachRemaining(action); 69 | } 70 | 71 | @Override 72 | public Spliterator forEachOrTail(Consumer action) { 73 | if (mode != 0) { 74 | action.accept(element); 75 | } 76 | Spliterator s = source; 77 | element = null; 78 | mode = 0; 79 | source = null; 80 | return s; 81 | } 82 | 83 | @Override 84 | public Spliterator trySplit() { 85 | if (mode == 0) 86 | return source.trySplit(); 87 | mode = 0; 88 | return new ConstSpliterator.OfRef<>(element, 1, true); 89 | } 90 | 91 | @Override 92 | public long estimateSize() { 93 | long size = source.estimateSize(); 94 | return mode == 0 || size == Long.MAX_VALUE ? size : size + 1; 95 | } 96 | 97 | @Override 98 | public int characteristics() { 99 | switch (mode) { 100 | case 1: 101 | return source.characteristics() & (ORDERED | SIZED | SUBSIZED); 102 | case 2: 103 | return source.characteristics() & ORDERED; 104 | default: 105 | return source.characteristics(); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/StreamContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import java.util.concurrent.ForkJoinPool; 19 | import java.util.function.Function; 20 | import java.util.function.Supplier; 21 | import java.util.stream.BaseStream; 22 | 23 | /** 24 | * This class controls stream execution mode (parallel/sequential), custom FJP 25 | * and close handlers. 26 | * 27 | *

28 | * Fields are package-private and mutable, but it's forbidden to change them 29 | * from outside this class. 30 | * 31 | *

32 | * For performance reasons shared objects SEQUENTIAL and PARALLEL are used: then 33 | * have no custom FJP and no close handler. If custom FJP or close handler is 34 | * requested for shared object, a new object is created, otherwise the current 35 | * one is modified. 36 | * 37 | * @author Tagir Valeev 38 | */ 39 | /* package */class StreamContext { 40 | static final StreamContext SEQUENTIAL = new StreamContext(false); 41 | static final StreamContext PARALLEL = new StreamContext(true); 42 | 43 | boolean parallel; 44 | ForkJoinPool fjp; 45 | Runnable closeHandler; 46 | 47 | private StreamContext(boolean parallel) { 48 | this.parallel = parallel; 49 | } 50 | 51 | T terminate(Supplier terminalOperation) { 52 | return fjp.submit(terminalOperation::get).join(); 53 | } 54 | 55 | T terminate(U value, Function terminalOperation) { 56 | return fjp.submit(() -> terminalOperation.apply(value)).join(); 57 | } 58 | 59 | StreamContext parallel() { 60 | if (this == SEQUENTIAL) 61 | return PARALLEL; 62 | this.parallel = true; 63 | this.fjp = null; 64 | return this; 65 | } 66 | 67 | StreamContext sequential() { 68 | if (this == PARALLEL) 69 | return SEQUENTIAL; 70 | this.parallel = false; 71 | this.fjp = null; 72 | return this; 73 | } 74 | 75 | StreamContext parallel(ForkJoinPool fjp) { 76 | StreamContext context = detach(); 77 | context.parallel = true; 78 | context.fjp = fjp; 79 | return context; 80 | } 81 | 82 | StreamContext detach() { 83 | if (this == PARALLEL || this == SEQUENTIAL) 84 | return new StreamContext(parallel); 85 | return this; 86 | } 87 | 88 | StreamContext onClose(Runnable r) { 89 | StreamContext context = detach(); 90 | context.closeHandler = compose(context.closeHandler, r); 91 | return context; 92 | } 93 | 94 | void close() { 95 | if (closeHandler != null) { 96 | Runnable r = closeHandler; 97 | closeHandler = null; 98 | r.run(); 99 | } 100 | } 101 | 102 | static Runnable compose(Runnable r1, Runnable r2) { 103 | if (r1 == null) 104 | return r2; 105 | return () -> { 106 | try { 107 | r1.run(); 108 | } catch (Throwable t1) { 109 | try { 110 | r2.run(); 111 | } catch (Throwable t2) { 112 | t1.addSuppressed(t2); 113 | } 114 | throw t1; 115 | } 116 | r2.run(); 117 | }; 118 | } 119 | 120 | StreamContext combine(BaseStream other) { 121 | if (other == null) 122 | return this; 123 | StreamContext otherStrategy = of(other); 124 | StreamContext result = this; 125 | if (other.isParallel() && !parallel) 126 | result = parallel(); 127 | if (otherStrategy.closeHandler != null) 128 | result = result.onClose(otherStrategy.closeHandler); 129 | return result; 130 | } 131 | 132 | static StreamContext of(BaseStream stream) { 133 | if (stream instanceof BaseStreamEx) 134 | return ((BaseStreamEx) stream).context; 135 | return new StreamContext(stream.isParallel()).onClose(stream::close); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/TailConcatSpliterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import one.util.streamex.Internals.TailSpliterator; 19 | 20 | import java.util.Spliterator; 21 | import java.util.function.Consumer; 22 | 23 | /** 24 | * @author Tagir Valeev 25 | * 26 | * @param type of the elements 27 | */ 28 | /* package */class TailConcatSpliterator implements TailSpliterator { 29 | private Spliterator left, right; 30 | private int characteristics; 31 | private long size; 32 | 33 | @SuppressWarnings("unchecked") 34 | public TailConcatSpliterator(Spliterator left, Spliterator right) { 35 | this.left = (Spliterator) left; 36 | this.right = (Spliterator) right; 37 | this.characteristics = left.characteristics() & right.characteristics() & (ORDERED | SIZED | SUBSIZED); 38 | this.size = left.estimateSize() + right.estimateSize(); 39 | if (this.size < 0) { 40 | this.size = Long.MAX_VALUE; 41 | this.characteristics &= (~SIZED) & (~SUBSIZED); 42 | } 43 | } 44 | 45 | @Override 46 | public boolean tryAdvance(Consumer action) { 47 | if (left != null) { 48 | if (left.tryAdvance(action)) { 49 | if (size > 0 && size != Long.MAX_VALUE) 50 | size--; 51 | return true; 52 | } 53 | left = null; 54 | } 55 | if (right != null) 56 | right = TailSpliterator.tryAdvanceWithTail(right, action); 57 | return right != null; 58 | } 59 | 60 | @Override 61 | public Spliterator tryAdvanceOrTail(Consumer action) { 62 | if (left == null || !left.tryAdvance(action)) { 63 | Spliterator s = right; 64 | right = null; 65 | return s; 66 | } 67 | if (size > 0 && size != Long.MAX_VALUE) 68 | size--; 69 | return this; 70 | } 71 | 72 | @Override 73 | public void forEachRemaining(Consumer action) { 74 | if (left != null) 75 | left.forEachRemaining(action); 76 | if (right != null) 77 | TailSpliterator.forEachWithTail(right, action); 78 | } 79 | 80 | @Override 81 | public Spliterator forEachOrTail(Consumer action) { 82 | if (left != null) 83 | left.forEachRemaining(action); 84 | Spliterator s = right; 85 | right = null; 86 | return s; 87 | } 88 | 89 | @Override 90 | public Spliterator trySplit() { 91 | if (left == null) 92 | return right.trySplit(); 93 | Spliterator s = left; 94 | left = null; 95 | return s; 96 | } 97 | 98 | @Override 99 | public long estimateSize() { 100 | if (left == null) 101 | return right == null ? 0 : right.estimateSize(); 102 | return size; 103 | } 104 | 105 | @Override 106 | public int characteristics() { 107 | return characteristics; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/UnorderedCancellableSpliterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import java.util.Spliterator; 19 | import java.util.concurrent.ConcurrentLinkedQueue; 20 | import java.util.concurrent.atomic.AtomicBoolean; 21 | import java.util.concurrent.atomic.AtomicInteger; 22 | import java.util.function.*; 23 | 24 | import static one.util.streamex.Internals.CancelException; 25 | import static one.util.streamex.Internals.CloneableSpliterator; 26 | 27 | /** 28 | * @author Tagir Valeev 29 | */ 30 | /* package */class UnorderedCancellableSpliterator extends CloneableSpliterator> { 31 | private volatile Spliterator source; 32 | private final BiConsumer accumulator; 33 | private final Predicate cancelPredicate; 34 | private final Supplier supplier; 35 | private final ConcurrentLinkedQueue partialResults = new ConcurrentLinkedQueue<>(); 36 | private final AtomicBoolean cancelled = new AtomicBoolean(false); 37 | private final AtomicInteger nPeers = new AtomicInteger(1); 38 | private final BinaryOperator combiner; 39 | private boolean flag; 40 | 41 | UnorderedCancellableSpliterator(Spliterator source, Supplier supplier, BiConsumer accumulator, 42 | BinaryOperator combiner, Predicate cancelPredicate) { 43 | this.source = source; 44 | this.supplier = supplier; 45 | this.accumulator = accumulator; 46 | this.combiner = combiner; 47 | this.cancelPredicate = cancelPredicate; 48 | } 49 | 50 | private boolean checkCancel(A acc) { 51 | if (cancelPredicate.test(acc)) { 52 | if (cancelled.compareAndSet(false, true)) { 53 | flag = true; 54 | return true; 55 | } 56 | } 57 | if (cancelled.get()) { 58 | flag = false; 59 | return true; 60 | } 61 | return false; 62 | } 63 | 64 | private boolean handleCancel(Consumer action, A acc) { 65 | source = null; 66 | if (flag) { 67 | action.accept(acc); 68 | return true; 69 | } 70 | return false; 71 | } 72 | 73 | @Override 74 | public boolean tryAdvance(Consumer action) { 75 | Spliterator source = this.source; 76 | if (source == null || cancelled.get()) { 77 | this.source = null; 78 | return false; 79 | } 80 | A acc = supplier.get(); 81 | if (checkCancel(acc)) 82 | return handleCancel(action, acc); 83 | try { 84 | source.forEachRemaining(t -> { 85 | accumulator.accept(acc, t); 86 | if (checkCancel(acc)) 87 | throw new CancelException(); 88 | }); 89 | } catch (CancelException ex) { 90 | return handleCancel(action, acc); 91 | } 92 | A result = acc; 93 | while (true) { 94 | A acc2 = partialResults.poll(); 95 | if (acc2 == null) 96 | break; 97 | result = combiner.apply(result, acc2); 98 | if (checkCancel(result)) 99 | return handleCancel(action, result); 100 | } 101 | partialResults.offer(result); 102 | this.source = null; 103 | if (nPeers.decrementAndGet() == 0) { 104 | result = partialResults.poll(); 105 | // non-cancelled finish 106 | while (true) { 107 | A acc2 = partialResults.poll(); 108 | if (acc2 == null) 109 | break; 110 | result = combiner.apply(result, acc2); 111 | if (cancelPredicate.test(result)) 112 | break; 113 | } 114 | this.source = null; 115 | action.accept(result); 116 | return true; 117 | } 118 | return false; 119 | } 120 | 121 | @Override 122 | public void forEachRemaining(Consumer action) { 123 | tryAdvance(action); 124 | } 125 | 126 | @Override 127 | public Spliterator trySplit() { 128 | if (source == null || cancelled.get()) { 129 | source = null; 130 | return null; 131 | } 132 | Spliterator prefix = source.trySplit(); 133 | if (prefix == null) { 134 | return null; 135 | } 136 | UnorderedCancellableSpliterator result = doClone(); 137 | result.source = prefix; 138 | nPeers.incrementAndGet(); 139 | return result; 140 | } 141 | 142 | @Override 143 | public long estimateSize() { 144 | return source == null ? 0 : source.estimateSize(); 145 | } 146 | 147 | @Override 148 | public int characteristics() { 149 | return source == null ? SIZED : 0; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/VerSpec.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex; 18 | 19 | /** 20 | * @author Tagir Valeev 21 | */ 22 | /* package */ interface VerSpec { 23 | VersionSpecific VER_SPEC = new VersionSpecific(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/VersionSpecific.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import java.nio.CharBuffer; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.Spliterator; 22 | import java.util.function.*; 23 | import java.util.stream.IntStream; 24 | 25 | /** 26 | * @author Tagir Valeev 27 | */ 28 | /* package */ class VersionSpecific { 29 | 30 | > S callWhile(AbstractStreamEx stream, Predicate predicate, boolean drop) { 31 | Spliterator spltr = stream.spliterator(); 32 | return stream.supply( 33 | spltr.hasCharacteristics(Spliterator.ORDERED) ? new TakeDrop.TDOfRef<>(spltr, drop, false, predicate) 34 | : new TakeDrop.UnorderedTDOfRef(spltr, drop, false, predicate)); 35 | } 36 | 37 | IntStreamEx callWhile(IntStreamEx stream, IntPredicate predicate, boolean drop) { 38 | return stream.delegate(new TakeDrop.TDOfInt(stream.spliterator(), drop, false, predicate)); 39 | } 40 | 41 | LongStreamEx callWhile(LongStreamEx stream, LongPredicate predicate, boolean drop) { 42 | return stream.delegate(new TakeDrop.TDOfLong(stream.spliterator(), drop, false, predicate)); 43 | } 44 | 45 | DoubleStreamEx callWhile(DoubleStreamEx stream, DoublePredicate predicate, boolean drop) { 46 | return stream.delegate(new TakeDrop.TDOfDouble(stream.spliterator(), drop, false, predicate)); 47 | } 48 | 49 | StreamEx callMapMulti(AbstractStreamEx s, BiConsumer> mapper) { 50 | return s.flatCollection(e -> { 51 | List result = new ArrayList<>(); 52 | mapper.accept(e, (Consumer) result::add); 53 | return result; 54 | }); 55 | } 56 | 57 | IntStreamEx callMapMultiToInt(AbstractStreamEx s, BiConsumer mapper) { 58 | return s.flatMapToInt(e -> { 59 | Internals.IntBuffer result = new Internals.IntBuffer(); 60 | mapper.accept(e, (IntConsumer) result::add); 61 | return result.stream(); 62 | }); 63 | } 64 | 65 | LongStreamEx callMapMultiToLong(AbstractStreamEx s, BiConsumer mapper) { 66 | return s.flatMapToLong(e -> { 67 | Internals.LongBuffer result = new Internals.LongBuffer(); 68 | mapper.accept(e, (LongConsumer) result::add); 69 | return result.stream(); 70 | }); 71 | } 72 | 73 | DoubleStreamEx callMapMultiToDouble(AbstractStreamEx s, BiConsumer mapper) { 74 | return s.flatMapToDouble(e -> { 75 | Internals.DoubleBuffer result = new Internals.DoubleBuffer(); 76 | mapper.accept(e, (DoubleConsumer) result::add); 77 | return result.stream(); 78 | }); 79 | } 80 | 81 | IntStream ofChars(CharSequence seq) { 82 | // In JDK 8 there's only default chars() method which uses 83 | // IteratorSpliterator 84 | // In JDK 9 chars() method for most of implementations is much better 85 | return CharBuffer.wrap(seq).chars(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/one/util/streamex/WithFirstSpliterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import one.util.streamex.Internals.CloneableSpliterator; 19 | 20 | import java.util.Spliterator; 21 | import java.util.concurrent.locks.ReentrantLock; 22 | import java.util.function.BiFunction; 23 | import java.util.function.Consumer; 24 | 25 | /** 26 | * @author Tagir Valeev 27 | */ 28 | /* package */final class WithFirstSpliterator extends CloneableSpliterator> implements Consumer { 29 | private static final int STATE_NONE = 0; 30 | private static final int STATE_FIRST_READ = 1; 31 | private static final int STATE_INIT = 2; 32 | private static final int STATE_EMPTY = 3; 33 | 34 | private ReentrantLock lock; 35 | private Spliterator source; 36 | private WithFirstSpliterator prefix; 37 | private volatile T first; 38 | private volatile int state = STATE_NONE; 39 | private final BiFunction mapper; 40 | private Consumer action; 41 | 42 | WithFirstSpliterator(Spliterator source, BiFunction mapper) { 43 | this.source = source; 44 | this.mapper = mapper; 45 | } 46 | 47 | private void acquire() { 48 | if (lock != null && state == STATE_NONE) { 49 | lock.lock(); 50 | } 51 | } 52 | 53 | private void release() { 54 | if (lock != null && lock.isHeldByCurrentThread()) { 55 | lock.unlock(); 56 | } 57 | } 58 | 59 | @Override 60 | public boolean tryAdvance(Consumer action) { 61 | if (state == STATE_NONE) { 62 | acquire(); 63 | try { 64 | doInit(); 65 | } finally { 66 | release(); 67 | } 68 | } 69 | if (state == STATE_FIRST_READ) { 70 | state = STATE_INIT; 71 | action.accept(mapper.apply(first, first)); 72 | return true; 73 | } 74 | if (state != STATE_INIT) 75 | return false; 76 | this.action = action; 77 | boolean hasNext = source.tryAdvance(this); 78 | this.action = null; 79 | return hasNext; 80 | } 81 | 82 | private void doInit() { 83 | int prefixState = state; 84 | if (prefixState != STATE_NONE) 85 | return; 86 | if (prefix != null) { 87 | prefix.doInit(); 88 | prefixState = prefix.state; 89 | } 90 | if (prefixState == STATE_FIRST_READ || prefixState == STATE_INIT) { 91 | first = prefix.first; 92 | state = STATE_INIT; 93 | return; 94 | } 95 | state = source.tryAdvance(x -> first = x) ? STATE_FIRST_READ : STATE_EMPTY; 96 | } 97 | 98 | @Override 99 | public void forEachRemaining(Consumer action) { 100 | acquire(); 101 | int myState = state; 102 | this.action = action; 103 | if (myState == STATE_FIRST_READ || myState == STATE_INIT) { 104 | release(); 105 | if (myState == STATE_FIRST_READ) { 106 | state = STATE_INIT; 107 | accept(first); 108 | } 109 | source.forEachRemaining(this); 110 | this.action = null; 111 | return; 112 | } 113 | try { 114 | Consumer init = x -> { 115 | if (state == STATE_NONE) { 116 | if (prefix != null) { 117 | prefix.doInit(); 118 | } 119 | this.first = (prefix == null || prefix.state == STATE_EMPTY) ? x : prefix.first; 120 | state = STATE_INIT; 121 | } 122 | release(); 123 | }; 124 | source.forEachRemaining(init.andThen(this)); 125 | this.action = null; 126 | } finally { 127 | release(); 128 | } 129 | } 130 | 131 | @Override 132 | public Spliterator trySplit() { 133 | if (state != STATE_NONE) 134 | return null; 135 | Spliterator prefix; 136 | if (lock == null) 137 | lock = new ReentrantLock(); 138 | acquire(); 139 | try { 140 | if (state != STATE_NONE) 141 | return null; 142 | prefix = source.trySplit(); 143 | if (prefix == null) 144 | return null; 145 | WithFirstSpliterator result = doClone(); 146 | result.source = prefix; 147 | return this.prefix = result; 148 | } finally { 149 | release(); 150 | } 151 | } 152 | 153 | @Override 154 | public long estimateSize() { 155 | return source.estimateSize(); 156 | } 157 | 158 | @Override 159 | public int characteristics() { 160 | return NONNULL 161 | | (source.characteristics() & (DISTINCT | IMMUTABLE | CONCURRENT | ORDERED | (lock == null ? SIZED : 0))); 162 | } 163 | 164 | @Override 165 | public void accept(T x) { 166 | action.accept(mapper.apply(first, x)); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/AverageLongTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.math.BigDecimal; 21 | import java.math.BigInteger; 22 | import java.math.MathContext; 23 | import java.util.Arrays; 24 | import java.util.OptionalDouble; 25 | import java.util.function.BiFunction; 26 | import java.util.function.Supplier; 27 | import java.util.stream.Collector; 28 | import java.util.stream.Collectors; 29 | import java.util.stream.IntStream; 30 | import java.util.stream.LongStream; 31 | 32 | import static one.util.streamex.Internals.AverageLong; 33 | import static one.util.streamex.TestHelpers.repeat; 34 | import static one.util.streamex.TestHelpers.withRandom; 35 | import static org.junit.Assert.assertEquals; 36 | import static org.junit.Assert.assertFalse; 37 | 38 | /** 39 | * @author Tagir Valeev 40 | */ 41 | public class AverageLongTest { 42 | 43 | @Test 44 | public void testAverageLongNoOverflow() { 45 | AverageLong avg = new AverageLong(); 46 | assertFalse(avg.result().isPresent()); 47 | avg.accept(1); 48 | avg.accept(2); 49 | avg.accept(3); 50 | assertEquals(2.0, avg.result().getAsDouble(), 0.0); 51 | 52 | avg.accept(2); 53 | avg.accept(-4); 54 | avg.accept(8); 55 | assertEquals(2.0, avg.result().getAsDouble(), 0.0); 56 | 57 | AverageLong avg1 = new AverageLong(); 58 | avg1.accept(-2); 59 | AverageLong avg2 = new AverageLong(); 60 | avg2.accept(-2); 61 | assertEquals(-2.0, avg1.combine(avg2).result().getAsDouble(), 0.0); 62 | 63 | withRandom(r -> { 64 | int[] input = r.ints(1000).toArray(); 65 | OptionalDouble expected = IntStream.of(input).average(); 66 | assertEquals(expected, Arrays.stream(input) 67 | .collect(AverageLong::new, AverageLong::accept, AverageLong::combine).result()); 68 | 69 | assertEquals(expected, Arrays.stream(input).parallel().collect(AverageLong::new, AverageLong::accept, 70 | AverageLong::combine).result()); 71 | }); 72 | } 73 | 74 | @Test 75 | public void testCombine() { 76 | withRandom(r -> repeat(100, i -> { 77 | AverageLong avg1 = new AverageLong(); 78 | AverageLong avg2 = new AverageLong(); 79 | long[] set1 = r.longs(100).toArray(); 80 | long[] set2 = r.longs(100).toArray(); 81 | double expected = LongStreamEx.of(set1).append(set2).boxed().collect(getBigIntegerAverager()).getAsDouble(); 82 | LongStream.of(set1).forEach(avg1::accept); 83 | LongStream.of(set2).forEach(avg2::accept); 84 | assertEquals(expected, avg1.combine(avg2).result().getAsDouble(), Math.abs(expected / 1e14)); 85 | })); 86 | } 87 | 88 | @Test 89 | public void testCompareToBigInteger() { 90 | withRandom(r -> { 91 | long[] input = LongStreamEx.of(r, 1000).toArray(); 92 | Supplier supplier = () -> Arrays.stream(input); 93 | double expected = supplier.get().boxed().collect(getBigIntegerAverager()).getAsDouble(); 94 | assertEquals(expected, supplier.get().collect(AverageLong::new, AverageLong::accept, AverageLong::combine) 95 | .result().getAsDouble(), Math.abs(expected) / 1e14); 96 | assertEquals(expected, supplier.get().parallel().collect(AverageLong::new, AverageLong::accept, 97 | AverageLong::combine).result().getAsDouble(), Math.abs(expected) / 1e14); 98 | }); 99 | } 100 | 101 | private static Collector getBigIntegerAverager() { 102 | BiFunction finisher = (BigInteger sum, Long cnt) -> cnt == 0L ? OptionalDouble 103 | .empty() 104 | : OptionalDouble.of(new BigDecimal(sum).divide(BigDecimal.valueOf(cnt), MathContext.DECIMAL64) 105 | .doubleValue()); 106 | return MoreCollectors.pairing(Collectors.reducing(BigInteger.ZERO, 107 | BigInteger::valueOf, BigInteger::add), Collectors.counting(), finisher); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/CharSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.Arrays; 21 | import java.util.Spliterator; 22 | 23 | import static one.util.streamex.TestHelpers.*; 24 | import static org.junit.Assert.*; 25 | 26 | /** 27 | * @author Tagir Valeev 28 | */ 29 | public class CharSpliteratorTest { 30 | @Test 31 | public void testBasics() { 32 | CharSpliterator spliterator = new CharSpliterator("abcd,efgh", ',', false); 33 | assertTrue(spliterator.hasCharacteristics(Spliterator.ORDERED)); 34 | assertTrue(spliterator.hasCharacteristics(Spliterator.NONNULL)); 35 | assertFalse(spliterator.hasCharacteristics(Spliterator.SORTED)); 36 | assertFalse(spliterator.hasCharacteristics(Spliterator.SIZED)); 37 | assertEquals(9, spliterator.estimateSize()); 38 | assertTrue(spliterator.tryAdvance(a -> { 39 | })); 40 | assertEquals(4, spliterator.estimateSize()); 41 | assertTrue(spliterator.tryAdvance(a -> { 42 | })); 43 | assertEquals(0, spliterator.estimateSize()); 44 | } 45 | 46 | @Test 47 | public void testSpliterator() { 48 | // Empty string is processed differently by CharSpliterator, but this is 49 | // fixed in StreamEx.split 50 | checkSpliterator("split", Arrays.asList(), () -> new CharSpliterator("", ',', true)); 51 | checkSpliterator("split", Arrays.asList(""), () -> new CharSpliterator("", ',', false)); 52 | withRandom(r -> { 53 | String[] inputs = { ",", "abcd,e,f,gh,,,i,j,kl,,,,,,", ",", "abcdasdfgsdfgsdfgsdfgsdfgsdgdfsgs", 54 | "abcdasdfgsdfgsdfgsdfgsdfgsdgdfsgs,", "abcdasdfgs,dfgsdfgsdfgsdfgsdgdfsgs", 55 | "abcd,e,f,gh,,,i,j,kl,,,,,,x", "abcd,e,f,gh,,,i,j,kl,,,,,,x,", 56 | IntStreamEx.of(r, 0, 3).limit(r.nextInt(1000) + 1).elements(new int[] { ',', 'a', 'b' }).charsToString() }; 57 | for (String input : inputs) { 58 | checkSpliterator(input, Arrays.asList(input.split(",")), () -> new CharSpliterator(input, ',', true)); 59 | checkSpliterator(input, Arrays.asList(input.split(",", -1)), () -> new CharSpliterator(input, ',', false)); 60 | } 61 | }); 62 | } 63 | 64 | @Test 65 | public void testTrySplit() { 66 | String input = "a,b,c,d,e,f,g,h"; 67 | CharSpliterator spliterator = new CharSpliterator(input, ',', false); 68 | assertEquals(-1, spliterator.getExactSizeIfKnown()); 69 | assertEquals(15, spliterator.estimateSize()); 70 | Spliterator prefix = spliterator.trySplit(); 71 | assertEquals(7, prefix.estimateSize()); 72 | assertEquals(7, spliterator.estimateSize()); 73 | prefix = spliterator.trySplit(); 74 | assertEquals(3, prefix.estimateSize()); 75 | assertEquals(3, spliterator.estimateSize()); 76 | consumeElement(spliterator, "g"); 77 | consumeElement(spliterator, "h"); 78 | consumeElement(prefix, "e"); 79 | consumeElement(prefix, "f"); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/CollapseSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.*; 21 | import java.util.Map.Entry; 22 | import java.util.function.BiConsumer; 23 | import java.util.function.Function; 24 | import java.util.function.Supplier; 25 | import java.util.stream.Stream; 26 | 27 | import static one.util.streamex.TestHelpers.*; 28 | import static org.junit.Assert.assertEquals; 29 | import static org.junit.Assert.assertNotNull; 30 | 31 | /** 32 | * @author Tagir Valeev 33 | * 34 | */ 35 | public class CollapseSpliteratorTest { 36 | private static void splitEquals(Spliterator source, BiConsumer, Spliterator> consumer) { 37 | Spliterator right = new CollapseSpliterator<>(Objects::equals, Function.identity(), Internals 38 | .selectFirst(), Internals.selectFirst(), source); 39 | Spliterator left = right.trySplit(); 40 | assertNotNull(left); 41 | consumer.accept(left, right); 42 | } 43 | 44 | @Test 45 | public void testSimpleSplit() { 46 | List input = Arrays.asList(1, 1, 1, 2, 2, 2, 2, 2); 47 | splitEquals(input.spliterator(), (left, right) -> { 48 | List result = new ArrayList<>(); 49 | left.forEachRemaining(result::add); 50 | right.forEachRemaining(result::add); 51 | assertEquals(Arrays.asList(1, 2), result); 52 | }); 53 | splitEquals(input.spliterator(), (left, right) -> { 54 | List result = new ArrayList<>(); 55 | List resultRight = new ArrayList<>(); 56 | right.forEachRemaining(resultRight::add); 57 | left.forEachRemaining(result::add); 58 | result.addAll(resultRight); 59 | assertEquals(Arrays.asList(1, 2), result); 60 | }); 61 | input = IntStreamEx.of(new Random(1), 100, 1, 10).sorted().boxed().toList(); 62 | splitEquals(input.spliterator(), (left, right) -> { 63 | List result = new ArrayList<>(); 64 | List resultRight = new ArrayList<>(); 65 | for (int i = 0; i < 10; i++) { 66 | left.tryAdvance(result::add); 67 | right.tryAdvance(resultRight::add); 68 | } 69 | result.addAll(resultRight); 70 | assertEquals(IntStreamEx.range(1, 10).boxed().toList(), result); 71 | }); 72 | input = IntStreamEx.constant(100, 100).append(2).prepend(1).boxed().toList(); 73 | splitEquals(StreamEx.of(input).without(100).parallel().spliterator(), (left, right) -> { 74 | List result = new ArrayList<>(); 75 | left.forEachRemaining(result::add); 76 | right.forEachRemaining(result::add); 77 | assertEquals(Arrays.asList(1, 2), result); 78 | }); 79 | input = Arrays.asList(0, 0, 1, 1, 1, 1, 4, 6, 6, 3, 3, 10); 80 | splitEquals(Stream.concat(Stream.empty(), input.parallelStream()).spliterator(), (left, right) -> { 81 | List result = new ArrayList<>(); 82 | left.forEachRemaining(result::add); 83 | right.forEachRemaining(result::add); 84 | assertEquals(Arrays.asList(0, 1, 4, 6, 3, 10), result); 85 | }); 86 | } 87 | 88 | @Test 89 | public void testNonIdentity() { 90 | checkNonIdentity(Arrays.asList(1, 2, 5, 6, 7, 8, 10, 11, 15)); 91 | checkNonIdentity(IntStreamEx.range(3, 100).prepend(1).boxed().toList()); 92 | } 93 | 94 | private static void checkNonIdentity(List input) { 95 | checkSpliterator("collpase", () -> new CollapseSpliterator>( 96 | (a, b) -> (b - a == 1), a -> new AbstractMap.SimpleEntry<>(a, a), 97 | (acc, a) -> new AbstractMap.SimpleEntry<>(acc.getKey(), a), (a, b) -> new AbstractMap.SimpleEntry<>(a 98 | .getKey(), b.getValue()), input.spliterator())); 99 | } 100 | 101 | @Test 102 | public void testMultiSplit() { 103 | List input = Arrays.asList(0, 0, 1, 1, 1, 1, 4, 6, 6, 3, 3, 10); 104 | multiSplit(input::spliterator); 105 | multiSplit(() -> Stream.concat(Stream.empty(), input.parallelStream()).spliterator()); 106 | } 107 | 108 | private static void multiSplit(Supplier> inputSpliterator) throws AssertionError { 109 | withRandom(r -> repeat(100, n -> { 110 | Spliterator spliterator = new CollapseSpliterator<>(Objects::equals, Function.identity(), 111 | Internals.selectFirst(), Internals.selectFirst(), inputSpliterator.get()); 112 | List result = new ArrayList<>(); 113 | List> spliterators = new ArrayList<>(); 114 | spliterators.add(spliterator); 115 | for (int i = 0; i < 8; i++) { 116 | Spliterator split = spliterators.get(r.nextInt(spliterators.size())).trySplit(); 117 | if (split != null) 118 | spliterators.add(split); 119 | } 120 | Collections.shuffle(spliterators, r); 121 | repeat(spliterators.size(), i -> spliterators.get(i - 1).forEachRemaining(result::add)); 122 | assertEquals(6, result.size()); 123 | })); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/CombinationSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.Spliterator; 21 | 22 | import static java.util.Spliterator.*; 23 | import static org.junit.Assert.*; 24 | 25 | public class CombinationSpliteratorTest { 26 | @Test 27 | public void testStepJump() { 28 | int[][] nk = { 29 | {1, 1}, 30 | {1, 10}, 31 | {9, 10}, 32 | {5, 10}, 33 | {2, 5}, 34 | {3, 5}, 35 | {8, 16}, 36 | {7, 20}, 37 | {15, 20}, 38 | {20, 20} 39 | }; 40 | for (int[] ints : nk) { 41 | int k = ints[0]; 42 | int n = ints[1]; 43 | int[] values = IntStreamEx.range(k).toArray(); 44 | long size = CombinationSpliterator.cnk(n, k); 45 | assertArrayEquals("n=" + n + ", k=" + k, values, CombinationSpliterator.jump(size - 1, k, n)); 46 | for (long cur = 1; cur < size; cur++) { 47 | CombinationSpliterator.step(values, n); 48 | assertArrayEquals("n=" + n + ", k=" + k + ", cur = " + cur, values, CombinationSpliterator.jump(size - 1 - cur, k, n)); 49 | } 50 | } 51 | } 52 | 53 | @Test 54 | public void testCharacteristics() { 55 | Spliterator spliterator = StreamEx.ofCombinations(10, 5).spliterator(); 56 | assertEquals(DISTINCT | IMMUTABLE | NONNULL | ORDERED | SIZED | SUBSIZED, spliterator.characteristics()); 57 | assertEquals(252, spliterator.estimateSize()); 58 | } 59 | 60 | @Test 61 | public void testTrySplit() { 62 | Spliterator spliterator = StreamEx.ofCombinations(5, 5).spliterator(); 63 | assertEquals(1, spliterator.estimateSize()); 64 | assertNull(spliterator.trySplit()); 65 | 66 | spliterator = StreamEx.ofCombinations(2, 1).spliterator(); 67 | assertEquals(2, spliterator.estimateSize()); 68 | assertNotNull(spliterator.trySplit()); 69 | 70 | spliterator = StreamEx.ofCombinations(2, 1).spliterator(); 71 | assertEquals(2, spliterator.estimateSize()); 72 | boolean[] readZero = {false}; 73 | assertTrue(spliterator.tryAdvance(x -> readZero[0] = x[0] == 0)); 74 | assertTrue(readZero[0]); 75 | assertNull(spliterator.trySplit()); 76 | 77 | spliterator = StreamEx.ofCombinations(4, 1).spliterator(); 78 | assertEquals(4, spliterator.estimateSize()); 79 | assertNotNull(spliterator.trySplit()); 80 | assertEquals(2, spliterator.estimateSize()); 81 | assertNotNull(spliterator.trySplit()); 82 | assertEquals(1, spliterator.estimateSize()); 83 | assertNull(spliterator.trySplit()); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/ConstSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import one.util.streamex.ConstSpliterator.OfRef; 19 | import org.junit.Test; 20 | 21 | import java.util.Collections; 22 | import java.util.Spliterator; 23 | 24 | import static one.util.streamex.TestHelpers.checkSpliterator; 25 | import static one.util.streamex.TestHelpers.consumeElement; 26 | import static org.junit.Assert.*; 27 | 28 | /** 29 | * @author Tagir Valeev 30 | */ 31 | public class ConstSpliteratorTest { 32 | @Test 33 | public void testConstant() { 34 | checkSpliterator("ref", Collections.nCopies(100, "val"), () -> new ConstSpliterator.OfRef<>("val", 100, false)); 35 | checkSpliterator("ref", Collections.nCopies(100, Integer.MIN_VALUE), () -> new ConstSpliterator.OfInt( 36 | Integer.MIN_VALUE, 100, false)); 37 | checkSpliterator("ref", Collections.nCopies(100, Long.MIN_VALUE), () -> new ConstSpliterator.OfLong( 38 | Long.MIN_VALUE, 100, false)); 39 | checkSpliterator("ref", Collections.nCopies(100, Double.MIN_VALUE), () -> new ConstSpliterator.OfDouble( 40 | Double.MIN_VALUE, 100, false)); 41 | } 42 | 43 | @Test 44 | public void testCharacteristics() { 45 | OfRef spltr = new ConstSpliterator.OfRef<>("val", 4, true); 46 | assertTrue(spltr.hasCharacteristics(Spliterator.ORDERED)); 47 | assertTrue(spltr.hasCharacteristics(Spliterator.SIZED)); 48 | assertTrue(spltr.hasCharacteristics(Spliterator.SUBSIZED)); 49 | assertTrue(spltr.hasCharacteristics(Spliterator.IMMUTABLE)); 50 | assertFalse(new ConstSpliterator.OfRef<>("val", 4, false).hasCharacteristics(Spliterator.ORDERED)); 51 | } 52 | 53 | @Test 54 | public void testSplit() { 55 | OfRef spltr = new ConstSpliterator.OfRef<>("val", 4, true); 56 | assertEquals(4, spltr.getExactSizeIfKnown()); 57 | spltr = spltr.trySplit(); 58 | assertEquals(2, spltr.getExactSizeIfKnown()); 59 | spltr = spltr.trySplit(); 60 | assertEquals(1, spltr.getExactSizeIfKnown()); 61 | assertNull(spltr.trySplit()); 62 | consumeElement(spltr, "val"); 63 | assertEquals(0, spltr.getExactSizeIfKnown()); 64 | assertNull(spltr.trySplit()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/CrossSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.*; 21 | 22 | import static one.util.streamex.TestHelpers.checkSpliterator; 23 | import static org.junit.Assert.*; 24 | 25 | /** 26 | * @author Tagir Valeev 27 | */ 28 | public class CrossSpliteratorTest { 29 | @Test 30 | public void testCrossToList() { 31 | for (int limit : new int[] { 1, 2, 4, 9 }) { 32 | List> input = Collections.nCopies(3, IntStreamEx.range(limit).boxed().toList()); 33 | List> expected = IntStreamEx.range(limit * limit * limit).mapToObj( 34 | i -> Arrays.asList(i / limit / limit, i / limit % limit, i % limit)).toList(); 35 | checkSpliterator("cross", expected, () -> new CrossSpliterator.ToList<>(input)); 36 | } 37 | } 38 | 39 | @Test 40 | public void testCrossReduce() { 41 | for (int limit : new int[] { 1, 2, 4, 9 }) { 42 | List> input = Collections.nCopies(3, IntStreamEx.range(limit).boxed().toList()); 43 | List expected = IntStreamEx.range(limit * limit * limit).mapToObj( 44 | i -> "" + (i / limit / limit) + (i / limit % limit) + (i % limit)).toList(); 45 | checkSpliterator("cross", expected, () -> new CrossSpliterator.Reducing<>(input, "", (s, b) -> s + b)); 46 | } 47 | } 48 | 49 | @Test 50 | public void testBigSize() { 51 | List> input = new ArrayList<>(); 52 | input.add(IntStreamEx.rangeClosed(1, 20).boxed().toList()); 53 | input.addAll(Collections.nCopies(18, IntStreamEx.rangeClosed(1, 10).boxed().toList())); 54 | Spliterator> spltr = new CrossSpliterator.ToList<>(input); 55 | assertFalse(spltr.hasCharacteristics(Spliterator.SIZED)); 56 | assertEquals(Long.MAX_VALUE, spltr.estimateSize()); 57 | spltr.trySplit(); 58 | assertFalse(spltr.hasCharacteristics(Spliterator.SIZED)); 59 | assertEquals(Long.MAX_VALUE, spltr.estimateSize()); 60 | spltr.trySplit(); 61 | assertTrue(spltr.hasCharacteristics(Spliterator.SIZED)); 62 | assertEquals(5_000_000_000_000_000_000L, spltr.estimateSize()); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/DistinctSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.Arrays; 21 | import java.util.HashSet; 22 | import java.util.Set; 23 | import java.util.Spliterator; 24 | import java.util.stream.IntStream; 25 | 26 | import static one.util.streamex.TestHelpers.checkSpliterator; 27 | import static org.junit.Assert.assertEquals; 28 | import static org.junit.Assert.assertTrue; 29 | 30 | /** 31 | * @author Tagir Valeev 32 | */ 33 | public class DistinctSpliteratorTest { 34 | 35 | @Test 36 | public void testSpliterator() { 37 | checkSpliterator("Distinct2", Arrays.asList("b"), () -> new DistinctSpliterator<>(Arrays.asList("a", null, "b", 38 | "c", "b", null, "c", "b").spliterator(), 3)); 39 | checkSpliterator("Distinct34", Arrays.asList(0), () -> new DistinctSpliterator<>(IntStream.range(0, 100).map( 40 | x -> x % 3).boxed().spliterator(), 34)); 41 | 42 | assertEquals(Spliterator.DISTINCT | Spliterator.ORDERED, new DistinctSpliterator<>(Arrays.asList("a", null, 43 | "b", "c", "b", null, "c", "b").spliterator(), 3).characteristics()); 44 | assertEquals(Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.SORTED 45 | | Spliterator.NONNULL, new DistinctSpliterator<>(IntStream.range(0, 100).spliterator(), 3) 46 | .characteristics()); 47 | assertEquals(100, new DistinctSpliterator<>(IntStream.range(0, 100).spliterator(), 3).estimateSize()); 48 | } 49 | 50 | @Test 51 | public void testAdvanceSplit() { 52 | DistinctSpliterator ds = new DistinctSpliterator<>(Arrays.asList("a", null, "b", "c", "b", null, "c", 53 | "b").spliterator(), 2); 54 | Set result = new HashSet<>(); 55 | assertTrue(ds.tryAdvance(result::add)); 56 | assertTrue(ds.tryAdvance(result::add)); 57 | Spliterator prefix = ds.trySplit(); 58 | prefix.forEachRemaining(result::add); 59 | ds.forEachRemaining(result::add); 60 | assertEquals(StreamEx.of(null, "b", "c").toSet(), result); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/EntryStreamInternalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.FixMethodOrder; 19 | import org.junit.Test; 20 | import org.junit.runners.MethodSorters; 21 | 22 | import java.util.LinkedHashMap; 23 | import java.util.Map; 24 | 25 | import static org.junit.Assert.assertSame; 26 | 27 | /** 28 | * @author Tagir Valeev 29 | */ 30 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 31 | public class EntryStreamInternalTest { 32 | 33 | @Test 34 | public void testCreate() { 35 | EntryStream stream = EntryStream.of(createMap()); 36 | assertSame(stream.stream(), EntryStream.of(stream).stream()); 37 | assertSame(stream.stream(), EntryStream.of(StreamEx.of(EntryStream.of(stream))).stream()); 38 | } 39 | 40 | private static Map createMap() { 41 | Map data = new LinkedHashMap<>(); 42 | data.put("a", 1); 43 | data.put("bb", 22); 44 | data.put("ccc", 33); 45 | return data; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/IfEmptySpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.*; 22 | import java.util.function.Supplier; 23 | 24 | import static one.util.streamex.TestHelpers.checkSpliterator; 25 | import static one.util.streamex.TestHelpers.consumeElement; 26 | import static org.junit.Assert.*; 27 | 28 | public class IfEmptySpliteratorTest { 29 | @Test 30 | public void testSpliterator() { 31 | List data = IntStreamEx.range(1000).boxed().toList(); 32 | checkSpliterator("++", data, () -> new IfEmptySpliterator<>(data.spliterator(), data.spliterator())); 33 | checkSpliterator("-+", data, () -> new IfEmptySpliterator<>(Spliterators.emptySpliterator(), data.spliterator())); 34 | checkSpliterator("+-", data, () -> new IfEmptySpliterator<>(data.spliterator(), Spliterators.emptySpliterator())); 35 | checkSpliterator("--", Collections.emptyList(), () -> new IfEmptySpliterator<>(Spliterators.emptySpliterator(), Spliterators.emptySpliterator())); 36 | } 37 | 38 | @Test 39 | public void testCharacteristics() { 40 | Spliterator sortedSpltr = StreamEx.of("foo", "bar", "baz").sorted().ifEmpty( 41 | StreamEx.of("foo", "bar", "baz").sorted()).spliterator(); 42 | assertFalse(sortedSpltr.hasCharacteristics(Spliterator.SORTED)); 43 | consumeElement(sortedSpltr, "bar"); 44 | assertFalse(sortedSpltr.hasCharacteristics(Spliterator.SORTED)); 45 | assertTrue(StreamEx.of("foo", "bar", "baz").ifEmpty(StreamEx.of(new HashSet<>())).spliterator() 46 | .hasCharacteristics(Spliterator.ORDERED)); 47 | List list = Collections.singletonList("foo"); 48 | Spliterator spliterator = StreamEx.empty().ifEmpty(list.stream()).spliterator(); 49 | consumeElement(spliterator, "foo"); 50 | assertEquals(list.spliterator().characteristics(), spliterator.characteristics()); 51 | } 52 | 53 | @Test 54 | public void testSize() { 55 | Spliterator spliterator = StreamEx.of("foo", "bar", "baz").ifEmpty("qux").spliterator(); 56 | assertEquals(3, spliterator.getExactSizeIfKnown()); 57 | consumeElement(spliterator, "foo"); 58 | assertEquals(2, spliterator.getExactSizeIfKnown()); 59 | assertEquals(1, StreamEx.empty().ifEmpty("qux").spliterator().getExactSizeIfKnown()); 60 | } 61 | 62 | @Test 63 | public void testFiltered() { 64 | List data = IntStreamEx.range(1000).boxed().toList(); 65 | Supplier> allMatch = () -> data.parallelStream().filter(x -> x >= 0).spliterator(); 66 | Supplier> noneMatch = () -> data.parallelStream().filter(x -> x < 0).spliterator(); 67 | Supplier> lastMatch = () -> data.parallelStream().filter(x -> x == 999).spliterator(); 68 | checkSpliterator("++", data, () -> new IfEmptySpliterator<>(allMatch.get(), allMatch.get())); 69 | checkSpliterator("l+", Collections.singletonList(999), () -> new IfEmptySpliterator<>(lastMatch.get(), allMatch.get())); 70 | checkSpliterator("-+", data, () -> new IfEmptySpliterator<>(noneMatch.get(), allMatch.get())); 71 | checkSpliterator("+-", data, () -> new IfEmptySpliterator<>(allMatch.get(), noneMatch.get())); 72 | checkSpliterator("--", Collections.emptyList(), () -> new IfEmptySpliterator<>(noneMatch.get(), noneMatch.get())); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/InternalsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import one.util.streamex.Internals.PairBox; 19 | import org.junit.Test; 20 | 21 | import java.util.*; 22 | 23 | import static one.util.streamex.Internals.ArrayCollection; 24 | import static one.util.streamex.Internals.PartialCollector; 25 | import static org.junit.Assert.*; 26 | 27 | /** 28 | * Tests for non-public APIs in StreamExInternals 29 | * 30 | * @author Tagir Valeev 31 | */ 32 | public class InternalsTest { 33 | @Test 34 | public void testArrayCollection() { 35 | Collection collection = new ArrayCollection(new Object[] { "1", "2" }); 36 | List list = new LinkedList<>(collection); 37 | assertEquals("1", list.get(0)); 38 | assertEquals("2", list.get(1)); 39 | List list2 = new ArrayList<>(collection); 40 | assertEquals("1", list.get(0)); 41 | assertEquals("2", list.get(1)); 42 | assertEquals(list2, list); 43 | Set set = new HashSet<>(collection); 44 | assertTrue(set.contains("1")); 45 | assertTrue(set.contains("2")); 46 | assertEquals(2, set.size()); 47 | } 48 | 49 | @Test(expected = UnsupportedOperationException.class) 50 | public void testPartialCollector() { 51 | PartialCollector.intSum().accumulator(); 52 | } 53 | 54 | @SuppressWarnings({"SimplifiableAssertion", "EqualsBetweenInconvertibleTypes"}) 55 | @Test 56 | public void testPairBoxEquals() { 57 | PairBox boxOneTwo = new PairBox<>(1, 2); 58 | PairBox boxTwoTwo = new PairBox<>(2, 2); 59 | PairBox boxTwoOne = new PairBox<>(2, 1); 60 | PairBox boxOneOne = new PairBox<>(1, 1); 61 | assertFalse(boxOneOne.equals(null)); 62 | assertFalse(boxOneOne.equals("")); 63 | assertEquals(boxOneTwo, boxTwoTwo); 64 | assertNotEquals(boxOneTwo, boxTwoOne); 65 | assertNotEquals(boxOneTwo, boxOneOne); 66 | assertEquals(boxTwoOne, boxOneOne); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/LimiterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.*; 21 | import java.util.stream.Collectors; 22 | import java.util.stream.IntStream; 23 | 24 | import static org.junit.Assert.assertArrayEquals; 25 | import static org.junit.Assert.assertEquals; 26 | 27 | /** 28 | * @author Tagir Valeev 29 | */ 30 | public class LimiterTest { 31 | @Test 32 | public void testLimiter() { 33 | Comparator cmp = Comparator.nullsFirst(Comparator.comparingInt(String::length)); 34 | exerciseLimiter("str", Arrays.asList("abc", "abgdc", "abd", "a", "fgssdfg", "sfsvsx", null, "wrffvs", 35 | "xcvbxvcb", "sffg", "abe", "adf", "abh"), cmp); 36 | for (int i : new int[] { 10, 100, 1000, 10000, 100000 }) { 37 | List ascending = IntStream.range(0, i).boxed().collect(Collectors.toList()); 38 | exerciseLimiter("asc, nat, " + i, ascending, Comparator.naturalOrder()); 39 | exerciseLimiter("asc, dec, " + i, ascending, Comparator.comparingInt(x -> x / 10)); 40 | List descending = IntStream.range(0, i).mapToObj(x -> ~x).collect(Collectors.toList()); 41 | exerciseLimiter("desc, nat, " + i, descending, Comparator.naturalOrder()); 42 | exerciseLimiter("desc, dec, " + i, descending, Comparator.comparingInt(x -> x / 10)); 43 | List random = new Random(1).ints(i).boxed().collect(Collectors.toList()); 44 | exerciseLimiter("rnd, nat, " + i, random, Comparator.naturalOrder()); 45 | exerciseLimiter("rnd, dec, " + i, random, Comparator.comparingInt(x -> x / 10)); 46 | List randomRange = new Random(1).ints(i, -1000, 1000).boxed().collect(Collectors.toList()); 47 | exerciseLimiter("rnd2, nat, " + i, randomRange, Comparator.naturalOrder()); 48 | exerciseLimiter("rnd2, dec, " + i, randomRange, Comparator.comparingInt(x -> x / 10)); 49 | } 50 | List list = IntStreamEx.range(100000).boxed().toList(); 51 | exerciseLimiter("big", list, list, 50000, Comparator.naturalOrder()); 52 | exerciseLimiter("big", list, list, 49999, Comparator.naturalOrder()); 53 | exerciseLimiter("big", list, list, 10000, Comparator.naturalOrder()); 54 | exerciseLimiter("big", list, list, Integer.MAX_VALUE / 3, Comparator.naturalOrder()); 55 | exerciseLimiter("big", list, list, Integer.MAX_VALUE / 2, Comparator.naturalOrder()); 56 | } 57 | 58 | public static void exerciseLimiter(String msg, Collection input, Comparator comp) { 59 | for (int limit : new int[] { 0, 1, 2, 5, 10, 20, 100, 1000 }) { 60 | List expected = new ArrayList<>(input); 61 | expected.sort(comp); 62 | exerciseLimiter(msg, expected, input, limit, comp); 63 | } 64 | } 65 | 66 | public static void exerciseLimiter(String msg, List expected, Collection input, int limit, Comparator comp) { 67 | List subList = limit >= expected.size() ? expected : expected.subList(0, limit); 68 | List actual = input.stream().collect(MoreCollectors.least(comp, limit)); 69 | assertEquals("Mismatch (sequential), " + msg + ", limit=" + limit, subList, actual); 70 | actual = input.parallelStream().collect(MoreCollectors.least(comp, limit)); 71 | assertEquals("Mismatch (parallel), " + msg + ", limit=" + limit, subList, actual); 72 | } 73 | 74 | @Test 75 | public void testDirect() { 76 | Limiter limiter = new Limiter<>(5, Comparator.naturalOrder()); 77 | limiter.put(1); 78 | limiter.put(2); 79 | limiter.put(3); 80 | limiter.put(4); 81 | assertEquals(4, limiter.size()); 82 | limiter.put(5); 83 | assertEquals(5, limiter.size()); 84 | limiter.put(6); 85 | assertEquals(5, limiter.size()); 86 | Iterator iterator = limiter.iterator(); 87 | assertEquals(Arrays.asList(1, 2, 3, 4, 5), StreamEx.of(iterator).toList()); 88 | } 89 | 90 | @Test 91 | public void testDirectPutAll() { 92 | Limiter limiter = new Limiter<>(3, Comparator.naturalOrder()); 93 | Limiter limiter2 = new Limiter<>(3, Comparator.naturalOrder()); 94 | limiter.put(6); 95 | limiter.put(5); 96 | limiter.put(4); 97 | limiter2.put(3); 98 | limiter2.put(1); 99 | limiter2.put(0); 100 | Limiter result = limiter.putAll(limiter2); 101 | result.put(2); 102 | result.sort(); 103 | assertArrayEquals(new Object[] {0, 1, 2}, result.toArray()); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/OrderedCancellableSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Collections; 22 | import java.util.List; 23 | import java.util.function.BiConsumer; 24 | import java.util.function.BinaryOperator; 25 | import java.util.function.Predicate; 26 | import java.util.function.Supplier; 27 | 28 | import static one.util.streamex.TestHelpers.checkSpliterator; 29 | 30 | /** 31 | * @author Tagir Valeev 32 | */ 33 | public class OrderedCancellableSpliteratorTest { 34 | @Test 35 | public void testSpliterator() { 36 | int limit = 10; 37 | Supplier> s = ArrayList::new; 38 | BiConsumer, Integer> a = (acc, t) -> { 39 | if (acc.size() < limit) 40 | acc.add(t); 41 | }; 42 | BinaryOperator> c = (a1, a2) -> { 43 | a2.forEach(t -> a.accept(a1, t)); 44 | return a1; 45 | }; 46 | Predicate> p = acc -> acc.size() >= limit; 47 | List input = IntStreamEx.range(30).boxed().toList(); 48 | List expected = IntStreamEx.range(limit).boxed().toList(); 49 | checkSpliterator("head-short-circuit", Collections.singletonList(expected), 50 | () -> new OrderedCancellableSpliterator<>(input.spliterator(), s, a, c, p)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/PairPermutationSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.AbstractMap.SimpleEntry; 21 | import java.util.Arrays; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.Spliterator; 25 | 26 | import static one.util.streamex.TestHelpers.checkSpliterator; 27 | import static org.junit.Assert.assertEquals; 28 | import static org.junit.Assert.assertTrue; 29 | 30 | /** 31 | * @author Tagir Valeev 32 | */ 33 | public class PairPermutationSpliteratorTest { 34 | @Test 35 | public void testSqrt() { 36 | for (int rev = 0; rev < 1000; rev++) { 37 | int row = (int) (Math.sqrt(8 * rev + 1) - 1) / 2; 38 | int row2 = PairPermutationSpliterator.isqrt(rev); 39 | assertEquals(row, row2); 40 | } 41 | for (int row : new int[]{1_000_000_000, 2_000_000_000, Integer.MAX_VALUE - 1, Integer.MAX_VALUE}) { 42 | assertEquals(row, PairPermutationSpliterator.isqrt(row * (row + 1L) / 2)); 43 | assertEquals(row - 1, PairPermutationSpliterator.isqrt(row * (row + 1L) / 2 - 1)); 44 | } 45 | } 46 | 47 | @Test 48 | public void testCharacteristics() { 49 | PairPermutationSpliterator spltr = new PairPermutationSpliterator<>(Arrays.asList(1, 2, 3), 50 | Integer::sum); 51 | assertTrue(spltr.hasCharacteristics(Spliterator.ORDERED)); 52 | assertTrue(spltr.hasCharacteristics(Spliterator.SIZED)); 53 | assertTrue(spltr.hasCharacteristics(Spliterator.SUBSIZED)); 54 | assertEquals(3, spltr.getExactSizeIfKnown()); 55 | } 56 | 57 | @Test 58 | public void testSpliterator() { 59 | for (int i : IntStreamEx.rangeClosed(2, 13).boxed()) { 60 | List input = IntStreamEx.range(i).boxed().toList(); 61 | List> expected = IntStreamEx.range(i) 62 | .>flatMapToObj( 63 | a -> IntStreamEx.range(a + 1, i).mapToObj(b -> new SimpleEntry<>(a, b))).toList(); 64 | checkSpliterator("#" + i, expected, () -> new PairPermutationSpliterator<>(input, SimpleEntry::new)); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/PairSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import one.util.streamex.PairSpliterator.PSOfRef; 19 | import org.junit.Test; 20 | 21 | import java.util.Arrays; 22 | import java.util.Spliterator; 23 | 24 | import static one.util.streamex.TestHelpers.checkSpliterator; 25 | import static one.util.streamex.TestHelpers.withRandom; 26 | import static org.junit.Assert.assertEquals; 27 | import static org.junit.Assert.assertTrue; 28 | 29 | /** 30 | * @author Tagir Valeev 31 | */ 32 | public class PairSpliteratorTest { 33 | @Test 34 | public void testSpliterator() { 35 | withRandom(r -> { 36 | int[] ints = IntStreamEx.of(r, 100).toArray(); 37 | long[] longs = LongStreamEx.of(r, 100).toArray(); 38 | double[] doubles = DoubleStreamEx.of(r, 100).toArray(); 39 | 40 | checkSpliterator("ref", () -> new PairSpliterator.PSOfRef<>((a, b) -> (a - b), Arrays.spliterator(ints))); 41 | checkSpliterator("int", () -> new PairSpliterator.PSOfInt((a, b) -> (a - b), null, Arrays.spliterator(ints), PairSpliterator.MODE_PAIRS)); 42 | checkSpliterator("long", () -> new PairSpliterator.PSOfLong((a, b) -> (a - b), null, Arrays.spliterator(longs), PairSpliterator.MODE_PAIRS)); 43 | checkSpliterator("double", () -> new PairSpliterator.PSOfDouble((a, b) -> (a - b), null, Arrays.spliterator(doubles), PairSpliterator.MODE_PAIRS)); 44 | 45 | // mapFirst 46 | checkSpliterator("ref", IntStreamEx.of(ints, 1, ints.length).boxed().prepend(ints[0] + 2).toList(), 47 | () -> new PairSpliterator.PSOfRef<>(a -> a + 2, Arrays.spliterator(ints), true)); 48 | checkSpliterator("int", IntStreamEx.of(ints, 1, ints.length).boxed().prepend(ints[0] + 2).toList(), 49 | () -> new PairSpliterator.PSOfInt((a, b) -> b, a -> a + 2, Arrays.spliterator(ints), PairSpliterator.MODE_MAP_FIRST)); 50 | checkSpliterator("long", LongStreamEx.of(longs, 1, longs.length).boxed().prepend(longs[0] + 2).toList(), 51 | () -> new PairSpliterator.PSOfLong((a, b) -> b, a -> a + 2, Arrays.spliterator(longs), PairSpliterator.MODE_MAP_FIRST)); 52 | checkSpliterator("double", DoubleStreamEx.of(doubles, 1, doubles.length).boxed().prepend(doubles[0] + 2).toList(), 53 | () -> new PairSpliterator.PSOfDouble((a, b) -> b, a -> a + 2, Arrays.spliterator(doubles), PairSpliterator.MODE_MAP_FIRST)); 54 | 55 | // mapLast 56 | checkSpliterator("ref", IntStreamEx.of(ints, 0, ints.length - 1).boxed().append(ints[ints.length - 1] + 2).toList(), 57 | () -> new PairSpliterator.PSOfRef<>(a -> a + 2, Arrays.spliterator(ints), false)); 58 | checkSpliterator("int", IntStreamEx.of(ints, 0, ints.length - 1).boxed().append(ints[ints.length - 1] + 2).toList(), 59 | () -> new PairSpliterator.PSOfInt((a, b) -> a, a -> a + 2, Arrays.spliterator(ints), PairSpliterator.MODE_MAP_LAST)); 60 | checkSpliterator("long", LongStreamEx.of(longs, 0, longs.length - 1).boxed().append(longs[longs.length - 1] + 2).toList(), 61 | () -> new PairSpliterator.PSOfLong((a, b) -> a, a -> a + 2, Arrays.spliterator(longs), PairSpliterator.MODE_MAP_LAST)); 62 | checkSpliterator("double", DoubleStreamEx.of(doubles, 0, doubles.length - 1).boxed().append(doubles[doubles.length - 1] + 2).toList(), 63 | () -> new PairSpliterator.PSOfDouble((a, b) -> a, a -> a + 2, Arrays.spliterator(doubles), PairSpliterator.MODE_MAP_LAST)); 64 | }); 65 | } 66 | 67 | @Test 68 | public void testCharacteristics() { 69 | PSOfRef ps = new PairSpliterator.PSOfRef<>((a, b) -> (a - b), IntStreamEx.range(100).spliterator()); 70 | assertTrue(ps.hasCharacteristics(Spliterator.SIZED)); 71 | assertTrue(ps.hasCharacteristics(Spliterator.ORDERED)); 72 | assertTrue(ps.hasCharacteristics(Spliterator.IMMUTABLE)); 73 | assertEquals(99, ps.getExactSizeIfKnown()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/PermutationSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.Random; 23 | import java.util.Spliterator; 24 | 25 | import static one.util.streamex.TestHelpers.repeat; 26 | import static one.util.streamex.TestHelpers.withRandom; 27 | import static org.junit.Assert.*; 28 | 29 | public class PermutationSpliteratorTest { 30 | private static final String PERMUTATIONS_4 = "0123,0132,0213,0231,0312,0321," + "1023,1032,1203,1230,1302,1320," 31 | + "2013,2031,2103,2130,2301,2310," + "3012,3021,3102,3120,3201,3210"; 32 | private static final String PERMUTATIONS_3 = "012,021,102,120,201,210"; 33 | 34 | private static List collect(Spliterator spliterator) { 35 | List strings = new ArrayList<>(); 36 | spliterator.forEachRemaining(i -> strings.add(IntStreamEx.of(i).mapToObj(String::valueOf).joining())); 37 | spliterator.forEachRemaining(i -> fail("Should not have any more elements")); 38 | assertFalse(spliterator.tryAdvance(i -> fail("Should not have any more elements"))); 39 | return strings; 40 | } 41 | 42 | private static void collectRandomSplit(Spliterator spliterator, Random r, List strings) { 43 | if (spliterator.estimateSize() == 0) 44 | return; 45 | int n = r.nextInt((int) spliterator.estimateSize()) + 1; 46 | for (int i = 0; i < n; i++) { 47 | if (!spliterator.tryAdvance(is -> strings.add(IntStreamEx.of(is).mapToObj(String::valueOf).joining()))) 48 | return; 49 | } 50 | Spliterator prefix = spliterator.trySplit(); 51 | if (prefix != null) 52 | collectRandomSplit(prefix, r, strings); 53 | collectRandomSplit(spliterator, r, strings); 54 | } 55 | 56 | @Test(expected = IllegalArgumentException.class) 57 | public void testOverflow() { 58 | new PermutationSpliterator(21); 59 | } 60 | 61 | @Test(expected = IllegalArgumentException.class) 62 | public void testUnderflow() { 63 | new PermutationSpliterator(-1); 64 | } 65 | 66 | @Test 67 | public void testAdvance3() { 68 | Spliterator spliterator = new PermutationSpliterator(3); 69 | assertEquals(PERMUTATIONS_3, String.join(",", collect(spliterator))); 70 | } 71 | 72 | @Test 73 | public void testAdvance4() { 74 | Spliterator spliterator = new PermutationSpliterator(4); 75 | assertEquals(PERMUTATIONS_4, String.join(",", collect(spliterator))); 76 | } 77 | 78 | @Test 79 | public void testSplit3() { 80 | Spliterator spliterator = new PermutationSpliterator(3); 81 | Spliterator prefix = spliterator.trySplit(); 82 | assertNotNull(prefix); 83 | assertEquals(3, spliterator.getExactSizeIfKnown()); 84 | assertEquals(3, prefix.getExactSizeIfKnown()); 85 | List strings = collect(prefix); 86 | assertEquals(3, strings.size()); 87 | strings.addAll(collect(spliterator)); 88 | assertEquals(PERMUTATIONS_3, String.join(",", strings)); 89 | } 90 | 91 | @Test 92 | public void testSplit3Random() { 93 | withRandom(r -> repeat(100, i -> { 94 | List strings = new ArrayList<>(); 95 | collectRandomSplit(new PermutationSpliterator(3), r, strings); 96 | assertEquals(String.valueOf(i), PERMUTATIONS_3, String.join(",", strings)); 97 | })); 98 | } 99 | 100 | @Test 101 | public void testSplit4() { 102 | Spliterator spliterator = new PermutationSpliterator(4); 103 | Spliterator prefix = spliterator.trySplit(); 104 | assertNotNull(prefix); 105 | assertEquals(12, spliterator.getExactSizeIfKnown()); 106 | assertEquals(12, prefix.getExactSizeIfKnown()); 107 | List strings = collect(prefix); 108 | assertEquals(12, strings.size()); 109 | strings.addAll(collect(spliterator)); 110 | assertEquals(PERMUTATIONS_4, String.join(",", strings)); 111 | } 112 | 113 | @Test 114 | public void testSplit4Random() { 115 | withRandom(r -> repeat(100, i -> { 116 | List strings = new ArrayList<>(); 117 | collectRandomSplit(new PermutationSpliterator(4), r, strings); 118 | assertEquals(String.valueOf(i), PERMUTATIONS_4, String.join(",", strings)); 119 | })); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/PrependSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.Spliterator; 21 | import java.util.stream.IntStream; 22 | import java.util.stream.LongStream; 23 | 24 | import static one.util.streamex.TestHelpers.checkSpliterator; 25 | import static org.junit.Assert.*; 26 | 27 | /** 28 | * @author Tagir Valeev 29 | */ 30 | public class PrependSpliteratorTest { 31 | @Test 32 | public void testSpliterator() { 33 | checkSpliterator("prepend", IntStreamEx.range(100).boxed().toList(), () -> new PrependSpliterator<>(IntStream 34 | .range(1, 100).spliterator(), 0)); 35 | assertTrue(new PrependSpliterator<>(IntStream.range(1, 100).spliterator(), 0) 36 | .hasCharacteristics(Spliterator.SIZED)); 37 | assertTrue(new PrependSpliterator<>(LongStream.range(0, Long.MAX_VALUE - 2).spliterator(), 0L) 38 | .hasCharacteristics(Spliterator.SIZED)); 39 | assertFalse(new PrependSpliterator<>(LongStream.range(0, Long.MAX_VALUE - 1).spliterator(), 0L) 40 | .hasCharacteristics(Spliterator.SIZED)); 41 | assertFalse(new PrependSpliterator<>(LongStream.range(0, Long.MAX_VALUE).spliterator(), 0L) 42 | .hasCharacteristics(Spliterator.SIZED)); 43 | assertEquals(Long.MAX_VALUE - 1, new PrependSpliterator<>( 44 | LongStream.range(0, Long.MAX_VALUE - 2).spliterator(), 0L).estimateSize()); 45 | assertEquals(Long.MAX_VALUE, 46 | new PrependSpliterator<>(LongStream.range(0, Long.MAX_VALUE - 1).spliterator(), 0L).estimateSize()); 47 | assertEquals(Long.MAX_VALUE, new PrependSpliterator<>(LongStream.range(0, Long.MAX_VALUE).spliterator(), 0L) 48 | .estimateSize()); 49 | 50 | PrependSpliterator spltr = new PrependSpliterator<>(IntStream.range(1, 100).spliterator(), 0); 51 | spltr.tryAdvance(x -> assertEquals(0, (int) x)); 52 | assertTrue(spltr.hasCharacteristics(Spliterator.SORTED)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/RangeBasedSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.Arrays; 21 | import java.util.List; 22 | 23 | import static one.util.streamex.TestHelpers.checkSpliterator; 24 | 25 | /** 26 | * @author Tagir Valeev 27 | */ 28 | public class RangeBasedSpliteratorTest { 29 | private static final List list10 = IntStreamEx.range(10).boxed().toList(); 30 | 31 | @Test 32 | public void testAsEntry() { 33 | List list = IntStreamEx.range(10).boxed().toList(); 34 | checkSpliterator("asEntry", () -> new RangeBasedSpliterator.AsEntry<>(list)); 35 | } 36 | 37 | @Test 38 | public void testOfSublists() { 39 | List list = IntStreamEx.range(10).boxed().toList(); 40 | checkSpliterator("ofSubLists", Arrays.asList(Arrays.asList(0, 1), Arrays.asList(2, 3), Arrays.asList(4, 5), 41 | Arrays.asList(6, 7), Arrays.asList(8, 9)), () -> new RangeBasedSpliterator.OfSubLists<>(list, 2, 2)); 42 | checkSpliterator("ofSubLists", Arrays.asList(Arrays.asList(0, 1, 2), Arrays.asList(2, 3, 4), Arrays.asList(4, 43 | 5, 6), Arrays.asList(6, 7, 8), Arrays.asList(8, 9)), () -> new RangeBasedSpliterator.OfSubLists<>(list, 3, 44 | 2)); 45 | } 46 | 47 | @Test 48 | public void testOfByte() { 49 | byte[] input = IntStreamEx.range(10).toByteArray(); 50 | checkSpliterator("ofByte", list10, () -> new RangeBasedSpliterator.OfByte(0, 10, input)); 51 | } 52 | 53 | @Test 54 | public void testOfChar() { 55 | char[] input = IntStreamEx.range(10).toCharArray(); 56 | checkSpliterator("ofChar", list10, () -> new RangeBasedSpliterator.OfChar(0, 10, input)); 57 | } 58 | 59 | @Test 60 | public void testOfShort() { 61 | short[] input = IntStreamEx.range(10).toShortArray(); 62 | checkSpliterator("ofShort", list10, () -> new RangeBasedSpliterator.OfShort(0, 10, input)); 63 | } 64 | 65 | @Test 66 | public void testOfFloat() { 67 | float[] input = IntStreamEx.range(10).asDoubleStream().toFloatArray(); 68 | checkSpliterator("ofFloat", () -> new RangeBasedSpliterator.OfFloat(0, 10, input)); 69 | } 70 | 71 | @Test 72 | public void testZipRef() { 73 | List l1 = IntStreamEx.range(10).boxed().toList(); 74 | List l2 = StreamEx.split("abcdefghij", "").toList(); 75 | checkSpliterator("zipRef", () -> new RangeBasedSpliterator.ZipRef<>(0, 10, (i, s) -> i + s, l1, l2)); 76 | } 77 | 78 | @Test 79 | public void testZipInt() { 80 | int[] a = IntStreamEx.range(10).toArray(); 81 | int[] b = IntStreamEx.range(10, 20).toArray(); 82 | checkSpliterator("zipInt", () -> new RangeBasedSpliterator.ZipInt(0, 10, (x, y) -> x * y, a, b)); 83 | } 84 | 85 | @Test 86 | public void testZipLong() { 87 | long[] a = LongStreamEx.range(10).toArray(); 88 | long[] b = LongStreamEx.range(10, 20).toArray(); 89 | checkSpliterator("zipLong", () -> new RangeBasedSpliterator.ZipLong(0, 10, (x, y) -> x * y, a, b)); 90 | } 91 | 92 | @Test 93 | public void testZipDouble() { 94 | double[] a = LongStreamEx.range(10).asDoubleStream().toArray(); 95 | double[] b = LongStreamEx.range(10, 20).asDoubleStream().toArray(); 96 | checkSpliterator("zipDouble", () -> new RangeBasedSpliterator.ZipDouble(0, 10, (x, y) -> x * y, a, b)); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/StreamExInternalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.FixMethodOrder; 19 | import org.junit.Test; 20 | import org.junit.runners.MethodSorters; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.Spliterator; 25 | 26 | import static java.util.Arrays.asList; 27 | import static org.junit.Assert.assertEquals; 28 | import static org.junit.Assert.assertSame; 29 | 30 | /** 31 | * @author Tagir Valeev 32 | */ 33 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 34 | public class StreamExInternalTest { 35 | @Test 36 | public void testCreate() { 37 | StreamEx stream = StreamEx.of("foo", "bar"); 38 | assertSame(stream.stream(), StreamEx.of(stream).stream()); 39 | } 40 | 41 | @Test 42 | public void testDropWhile() { 43 | // Test that in JDK9 operation is propagated to JDK dropWhile method. 44 | String simpleName = VerSpec.VER_SPEC.getClass().getSimpleName(); 45 | boolean hasDropWhile = simpleName.equals("Java9Specific") || simpleName.equals("Java16Specific"); 46 | Spliterator spliterator = StreamEx.of("aaa", "b", "cccc").dropWhile(x -> x.length() > 1).spliterator(); 47 | assertEquals(hasDropWhile, !spliterator.getClass().getSimpleName().equals("TDOfRef")); 48 | } 49 | 50 | @Test 51 | public void testSplit() { 52 | assertEquals(CharSpliterator.class, StreamEx.split("a#a", "\\#").spliterator().getClass()); 53 | } 54 | 55 | @Test 56 | public void testMapMulti() { 57 | boolean hasMapMulti = VerSpec.VER_SPEC.getClass().getSimpleName().equals("Java16Specific"); 58 | List data = new ArrayList<>(); 59 | StreamEx.of(10, 20, 30).mapMulti((e, cons) -> { 60 | data.add("MM: "+e); 61 | cons.accept("T: " + e); 62 | data.add("MM: " +(e + 1)); 63 | cons.accept("T: " + (e + 1)); 64 | }).into(data); 65 | // When polyfill is used, results of mapMulti are buffered, so the order of execution is different 66 | List expected = hasMapMulti ? 67 | asList("MM: 10", "T: 10", "MM: 11", "T: 11", "MM: 20", "T: 20", "MM: 21", "T: 21", "MM: 30", "T: 30", "MM: 31", "T: 31") : 68 | asList("MM: 10", "MM: 11", "T: 10", "T: 11", "MM: 20", "MM: 21", "T: 20", "T: 21", "MM: 30", "MM: 31", "T: 30", "T: 31"); 69 | assertEquals(expected, data); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/TailConcatSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.*; 21 | import java.util.concurrent.ConcurrentLinkedDeque; 22 | 23 | import static one.util.streamex.TestHelpers.consumeElement; 24 | import static org.junit.Assert.*; 25 | 26 | /** 27 | * @author Tagir Valeev 28 | * 29 | */ 30 | public class TailConcatSpliteratorTest { 31 | @Test 32 | public void testCharacteristics() { 33 | TailConcatSpliterator spltr = new TailConcatSpliterator<>(IntStreamEx.range(1000).spliterator(), IntStreamEx.range(1000).spliterator()); 34 | assertTrue(spltr.hasCharacteristics(Spliterator.SIZED)); 35 | assertTrue(spltr.hasCharacteristics(Spliterator.SUBSIZED)); 36 | assertTrue(spltr.hasCharacteristics(Spliterator.ORDERED)); 37 | assertEquals(2000, spltr.getExactSizeIfKnown()); 38 | 39 | spltr = new TailConcatSpliterator<>(Spliterators.emptySpliterator(), Spliterators.emptySpliterator()); 40 | assertTrue(spltr.hasCharacteristics(Spliterator.SIZED)); 41 | assertEquals(0, spltr.getExactSizeIfKnown()); 42 | 43 | spltr = new TailConcatSpliterator<>(IntStreamEx.range(1000).spliterator(), new HashSet<>(Arrays.asList(1, 2, 3)).spliterator()); 44 | assertTrue(spltr.hasCharacteristics(Spliterator.SIZED)); 45 | assertFalse(spltr.hasCharacteristics(Spliterator.SUBSIZED)); 46 | assertFalse(spltr.hasCharacteristics(Spliterator.ORDERED)); 47 | assertEquals(1003, spltr.getExactSizeIfKnown()); 48 | spltr.tryAdvance(x -> assertEquals(0, (int) x)); 49 | assertEquals(1002, spltr.getExactSizeIfKnown()); 50 | 51 | TailConcatSpliterator longSpltr = new TailConcatSpliterator<>(LongStreamEx.range(Long.MAX_VALUE / 2 + 1) 52 | .spliterator(), LongStreamEx.range(Long.MAX_VALUE / 2 + 1).spliterator()); 53 | assertFalse(longSpltr.hasCharacteristics(Spliterator.SIZED)); 54 | assertFalse(longSpltr.hasCharacteristics(Spliterator.SUBSIZED)); 55 | assertTrue(longSpltr.hasCharacteristics(Spliterator.ORDERED)); 56 | assertEquals(Long.MAX_VALUE, longSpltr.estimateSize()); 57 | longSpltr.tryAdvance(x -> assertEquals(0L, (long) x)); 58 | assertEquals(Long.MAX_VALUE, longSpltr.estimateSize()); 59 | 60 | Queue q = new ConcurrentLinkedDeque<>(); 61 | spltr = new TailConcatSpliterator<>(q.spliterator(), q.spliterator()); 62 | q.add(1); 63 | assertFalse(longSpltr.hasCharacteristics(Spliterator.SIZED)); 64 | consumeElement(spltr, 1); 65 | consumeElement(spltr, 1); 66 | assertFalse(spltr.tryAdvance(x -> fail("Should not happen"))); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/TreeSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.Arrays; 21 | import java.util.List; 22 | import java.util.Map.Entry; 23 | import java.util.stream.Stream; 24 | 25 | import static one.util.streamex.TestHelpers.checkSpliterator; 26 | 27 | /** 28 | * @author Tagir Valeev 29 | * 30 | */ 31 | public class TreeSpliteratorTest { 32 | @Test 33 | public void testPlainSpliterator() { 34 | List expected = Arrays.asList("", "a", "aa", "aaa", "aaaa", "aaab", "aab", "aaba", "aabb", "ab", "aba", 35 | "abaa", "abab", "abb", "abba", "abbb", "b", "ba", "baa", "baaa", "baab", "bab", "baba", "babb", "bb", "bba", 36 | "bbaa", "bbab", "bbb", "bbba", "bbbb"); 37 | checkSpliterator("tree", expected, () -> new TreeSpliterator.Plain<>("", s -> s.length() == 4 ? null 38 | : Stream.of("a", "b").map(s::concat))); 39 | } 40 | 41 | @Test 42 | public void testDepthSpliterator() { 43 | List> expected = StreamEx.of("", "a", "aa", "ab", "ac", "b", "ba", "bb", "bc", "c", "ca", 44 | "cb", "cc").mapToEntry(String::length).invert().toList(); 45 | checkSpliterator("tree", expected, () -> new TreeSpliterator.Depth<>("", (depth, s) -> depth == 2 ? null 46 | : Stream.of("a", "b", "c").map(s::concat), 0)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/UnknownSizeSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package one.util.streamex; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.List; 22 | import java.util.Spliterator; 23 | import java.util.Spliterators; 24 | import java.util.concurrent.atomic.AtomicInteger; 25 | import java.util.stream.StreamSupport; 26 | 27 | import static one.util.streamex.TestHelpers.checkSpliterator; 28 | import static org.junit.Assert.assertEquals; 29 | import static org.junit.Assert.assertTrue; 30 | 31 | public class UnknownSizeSpliteratorTest { 32 | @Test 33 | public void testSplit() { 34 | List input = IntStreamEx.range(100).boxed().toList(); 35 | Spliterator spliterator = new UnknownSizeSpliterator.USOfRef<>(input.iterator()); 36 | assertEquals(Long.MAX_VALUE, spliterator.estimateSize()); 37 | AtomicInteger count = new AtomicInteger(); 38 | assertTrue(spliterator.tryAdvance(count::addAndGet)); 39 | assertTrue(spliterator.tryAdvance(count::addAndGet)); 40 | assertEquals(1, count.get()); 41 | assertEquals(Long.MAX_VALUE, spliterator.estimateSize()); 42 | Spliterator spliterator2 = spliterator.trySplit(); 43 | assertTrue(spliterator.tryAdvance(count::addAndGet)); 44 | assertTrue(spliterator2.tryAdvance(count::addAndGet)); 45 | assertEquals(54, count.get()); 46 | } 47 | 48 | @Test 49 | public void testRefSpliterator() { 50 | for (int size : new int[] { 1, 5, 100, 1000, 1023, 1024, 1025, 2049 }) { 51 | List input = IntStreamEx.range(size).boxed().toList(); 52 | checkSpliterator(String.valueOf(size), input, () -> new UnknownSizeSpliterator.USOfRef<>(input.iterator())); 53 | } 54 | } 55 | 56 | @Test 57 | public void testIntSpliterator() { 58 | for (int size : new int[] { 1, 5, 100, 1000, 1023, 1024, 1025, 2049 }) { 59 | int[] input = IntStreamEx.range(size).toArray(); 60 | checkSpliterator(String.valueOf(size), IntStreamEx.of(input).boxed().toList(), 61 | () -> new UnknownSizeSpliterator.USOfInt(Spliterators.iterator(Spliterators.spliterator(input, 0)))); 62 | } 63 | } 64 | 65 | @Test 66 | public void testLongSpliterator() { 67 | for (int size : new int[] { 1, 5, 100, 1000, 1023, 1024, 1025, 2049 }) { 68 | long[] input = LongStreamEx.range(size).toArray(); 69 | checkSpliterator(String.valueOf(size), LongStreamEx.of(input).boxed().toList(), 70 | () -> new UnknownSizeSpliterator.USOfLong(Spliterators.iterator(Spliterators.spliterator(input, 0)))); 71 | } 72 | } 73 | 74 | @Test 75 | public void testDoubleSpliterator() { 76 | for (int size : new int[] { 1, 5, 100, 1000, 1023, 1024, 1025, 2049 }) { 77 | double[] input = LongStreamEx.range(size).asDoubleStream().toArray(); 78 | checkSpliterator(String.valueOf(size), DoubleStreamEx.of(input).boxed().toList(), 79 | () -> new UnknownSizeSpliterator.USOfDouble(Spliterators.iterator(Spliterators.spliterator(input, 0)))); 80 | } 81 | } 82 | 83 | @Test 84 | public void testAsStream() { 85 | List input = IntStreamEx.range(100).boxed().toList(); 86 | assertEquals(4950, StreamSupport.stream(new UnknownSizeSpliterator.USOfRef<>(input.iterator()), false) 87 | .mapToInt(x -> x).sum()); 88 | assertEquals(4950, StreamSupport.stream(new UnknownSizeSpliterator.USOfRef<>(input.iterator()), true).mapToInt( 89 | x -> x).sum()); 90 | 91 | input = IntStreamEx.range(5000).boxed().toList(); 92 | assertEquals(12497500, StreamSupport.stream(new UnknownSizeSpliterator.USOfRef<>(input.iterator()), false) 93 | .mapToInt(x -> x).sum()); 94 | assertEquals(12497500, StreamSupport.stream(new UnknownSizeSpliterator.USOfRef<>(input.iterator()), true) 95 | .mapToInt(x -> x).sum()); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/UnorderedCancellableSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.Arrays; 21 | import java.util.Collections; 22 | import java.util.Spliterator; 23 | import java.util.function.BiConsumer; 24 | import java.util.function.BinaryOperator; 25 | import java.util.function.Predicate; 26 | import java.util.function.Supplier; 27 | 28 | import static one.util.streamex.TestHelpers.checkSpliterator; 29 | import static org.junit.Assert.assertEquals; 30 | import static org.junit.Assert.assertTrue; 31 | 32 | /** 33 | * @author Tagir Valeev 34 | */ 35 | public class UnorderedCancellableSpliteratorTest { 36 | private static class BoxedInteger { 37 | int value; 38 | 39 | public BoxedInteger(int value) { 40 | this.value = value; 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | return value; 46 | } 47 | 48 | @Override 49 | public boolean equals(Object obj) { 50 | return obj != null && getClass() == obj.getClass() && value == ((BoxedInteger) obj).value; 51 | } 52 | } 53 | 54 | @Test 55 | public void testSpliterator() { 56 | Supplier s = () -> new BoxedInteger(0xFFFFFFFF); 57 | BiConsumer a = (acc, t) -> acc.value &= t; 58 | BinaryOperator c = (a1, a2) -> new BoxedInteger(a1.value & a2.value); 59 | Predicate p = acc -> acc.value == 0; 60 | Supplier> supplier = () -> new UnorderedCancellableSpliterator<>(Arrays.asList( 61 | 0b11100, 0b01110, 0b00011, 0b11010).spliterator(), s, a, c, p); 62 | checkSpliterator("intersecting-short-circuit", Collections.singletonList(new BoxedInteger(0)), supplier); 63 | Spliterator spliterator = supplier.get(); 64 | assertEquals(4, spliterator.estimateSize()); 65 | assertTrue(spliterator.tryAdvance(x -> { 66 | })); 67 | assertEquals(0, spliterator.estimateSize()); 68 | assertTrue(spliterator.hasCharacteristics(Spliterator.SIZED)); 69 | checkSpliterator("intersecting-ok", Collections.singletonList(new BoxedInteger(0b111)), 70 | () -> new UnorderedCancellableSpliterator<>(Collections.nCopies(100, 0b111).spliterator(), s, a, c, p)); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/WithFirstSpliteratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.AbstractMap; 21 | import java.util.Spliterator; 22 | import java.util.Spliterators; 23 | import java.util.stream.Stream; 24 | 25 | import static one.util.streamex.TestHelpers.checkSpliterator; 26 | import static one.util.streamex.TestHelpers.consumeElement; 27 | import static org.junit.Assert.*; 28 | 29 | /** 30 | * @author Tagir Valeev 31 | */ 32 | public class WithFirstSpliteratorTest { 33 | @Test 34 | public void testSpliterator() { 35 | checkSpliterator("withFirst", EntryStream.of(0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5).toList(), 36 | () -> new WithFirstSpliterator<>(Stream.of(0, 1, 2, 3, 4, 5).spliterator(), 37 | AbstractMap.SimpleImmutableEntry::new)); 38 | checkSpliterator("withFirstFlatMap", EntryStream.of(0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5).toList(), 39 | () -> new WithFirstSpliterator<>(Stream.of(0, 2, 4).flatMap(x -> Stream.of(x, x + 1)).parallel() 40 | .spliterator(), AbstractMap.SimpleImmutableEntry::new)); 41 | } 42 | 43 | @Test 44 | public void testCharacteristics() { 45 | WithFirstSpliterator spltr = new WithFirstSpliterator<>(Stream.of(6, 1, 2, 3, 4, 5) 46 | .spliterator(), Integer::sum); 47 | assertTrue(spltr.hasCharacteristics(Spliterator.SIZED)); 48 | assertEquals(6, spltr.getExactSizeIfKnown()); 49 | consumeElement(spltr, 12); 50 | assertEquals(5, spltr.getExactSizeIfKnown()); 51 | consumeElement(spltr, 7); 52 | assertEquals(4, spltr.getExactSizeIfKnown()); 53 | 54 | spltr = new WithFirstSpliterator<>(Spliterators.emptySpliterator(), Integer::sum); 55 | assertTrue(spltr.hasCharacteristics(Spliterator.SIZED)); 56 | assertEquals(0, spltr.getExactSizeIfKnown()); 57 | assertFalse(spltr.tryAdvance(x -> fail("Should not happen"))); 58 | assertEquals(0, spltr.getExactSizeIfKnown()); 59 | 60 | WithFirstSpliterator longSpltr = new WithFirstSpliterator<>(LongStreamEx.range(Long.MAX_VALUE) 61 | .spliterator(), Long::sum); 62 | assertTrue(longSpltr.hasCharacteristics(Spliterator.SIZED)); 63 | assertEquals(Long.MAX_VALUE, longSpltr.getExactSizeIfKnown()); 64 | consumeElement(longSpltr, 0L); 65 | assertEquals(Long.MAX_VALUE - 1, longSpltr.getExactSizeIfKnown()); 66 | 67 | longSpltr = new WithFirstSpliterator<>(LongStreamEx.range(-1, Long.MAX_VALUE) 68 | .spliterator(), Long::sum); 69 | assertFalse(longSpltr.hasCharacteristics(Spliterator.SIZED)); 70 | assertEquals(Long.MAX_VALUE, longSpltr.estimateSize()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/api/BaseStreamExTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex.api; 17 | 18 | import one.util.streamex.IntStreamEx; 19 | import one.util.streamex.LongStreamEx; 20 | import one.util.streamex.StreamEx; 21 | import org.junit.FixMethodOrder; 22 | import org.junit.Test; 23 | import org.junit.runners.MethodSorters; 24 | 25 | import java.util.ArrayList; 26 | import java.util.Arrays; 27 | import java.util.List; 28 | import java.util.Spliterator; 29 | import java.util.concurrent.atomic.AtomicBoolean; 30 | import java.util.function.Function; 31 | import java.util.stream.Stream; 32 | 33 | import static org.junit.Assert.*; 34 | 35 | /** 36 | * @author Tagir Valeev 37 | */ 38 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 39 | public class BaseStreamExTest { 40 | @Test 41 | public void testSpliterator() { 42 | Spliterator spltr = Arrays.spliterator(new Integer[]{1, 2, 3}); 43 | StreamEx s = StreamEx.of(spltr); 44 | assertFalse(s.isParallel()); 45 | assertSame(spltr, s.spliterator()); 46 | } 47 | 48 | @Test(expected = IllegalStateException.class) 49 | public void testSpliteratorConsumed() { 50 | StreamEx s = StreamEx.of(1, 2, 3); 51 | assertNotNull(s.spliterator()); 52 | s.spliterator(); 53 | } 54 | 55 | @Test(expected = IllegalStateException.class) 56 | public void testStreamConsumed() { 57 | StreamEx s = StreamEx.of(1, 2, 3); 58 | assertNotNull(s.spliterator()); 59 | s.count(); 60 | } 61 | 62 | @Test 63 | public void testClose() { 64 | List closeHandlers = new ArrayList<>(); 65 | StreamEx stream = StreamEx.of(Stream.of(1, 2, 3).onClose(() -> closeHandlers.add("Orig stream"))) 66 | .onClose(() -> closeHandlers.add("StreamEx")) 67 | .map(x -> x * 2) 68 | .onClose(() -> closeHandlers.add("After map")) 69 | .pairMap(Integer::sum) 70 | .onClose(() -> closeHandlers.add("After pairMap")) 71 | .append(4) 72 | .onClose(() -> closeHandlers.add("After append")) 73 | .prepend(Stream.of(5).onClose(() -> closeHandlers.add("Prepended Stream"))) 74 | .prepend(StreamEx.of(6).onClose(() -> closeHandlers.add("Prepended StreamEx"))); 75 | assertEquals(Arrays.asList(6, 5, 6, 10, 4), stream.toList()); 76 | assertTrue(closeHandlers.isEmpty()); 77 | stream.close(); 78 | assertEquals(Arrays.asList("Orig stream", "StreamEx", "After map", "After pairMap", "After append", 79 | "Prepended Stream", "Prepended StreamEx"), closeHandlers); 80 | closeHandlers.clear(); 81 | stream.close(); 82 | assertTrue(closeHandlers.isEmpty()); 83 | } 84 | 85 | @Test 86 | public void testCloseException() { 87 | AtomicBoolean flag = new AtomicBoolean(); 88 | Function ex = str -> () -> { 89 | throw new IllegalStateException(str); 90 | }; 91 | StreamEx stream = StreamEx.of(Stream.of(1, 2, 3).onClose(ex.apply("Orig stream"))) 92 | .onClose(ex.apply("StreamEx")) 93 | .map(x -> x * 2) 94 | .onClose(() -> flag.set(true)) 95 | .pairMap(Integer::sum) 96 | .onClose(ex.apply("After pairMap")) 97 | .append(4) 98 | .onClose(ex.apply("After append")) 99 | .prepend(Stream.of(5).onClose(ex.apply("Prepended Stream"))) 100 | .prepend(StreamEx.of(6).onClose(ex.apply("Prepended StreamEx"))); 101 | assertEquals(Arrays.asList(6, 5, 6, 10, 4), stream.toList()); 102 | try { 103 | stream.close(); 104 | } catch (IllegalStateException e) { 105 | assertEquals("Orig stream", e.getMessage()); 106 | assertEquals(Arrays.asList("StreamEx", "After pairMap", "After append", "Prepended Stream", 107 | "Prepended StreamEx"), StreamEx.of(e.getSuppressed()).map(IllegalStateException.class::cast).map( 108 | Throwable::getMessage).toList()); 109 | assertTrue(flag.get()); 110 | return; 111 | } 112 | fail("No exception"); 113 | } 114 | 115 | @Test 116 | public void testChain() { 117 | assertArrayEquals(new double[] { 2, 2, 6, 2, 6 }, 118 | IntStreamEx.of(3, 4, 5).chain(IntStreamEx::boxed) 119 | .chain(s -> s.flatMapToLong(LongStreamEx::range)) 120 | .chain(s -> s.filter(n -> n % 2 != 0).asDoubleStream()) 121 | .chain(s -> s.map(x -> x * 2)).toArray(), 0.0); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/api/PrefixOpsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex.api; 17 | 18 | import one.util.streamex.IntStreamEx; 19 | import one.util.streamex.LongStreamEx; 20 | import one.util.streamex.StreamEx; 21 | import org.junit.Test; 22 | 23 | import java.util.Arrays; 24 | import java.util.HashSet; 25 | import java.util.Set; 26 | import java.util.Spliterator; 27 | import java.util.function.IntConsumer; 28 | import java.util.function.LongConsumer; 29 | 30 | import static one.util.streamex.TestHelpers.consumeElement; 31 | import static org.junit.Assert.*; 32 | 33 | public class PrefixOpsTest { 34 | @Test 35 | public void testNoSplitAfterAdvance() { 36 | Spliterator spliterator = StreamEx.constant("a", 100).unordered().prefix(String::concat).spliterator(); 37 | consumeElement(spliterator, "a"); 38 | consumeElement(spliterator, "aa"); 39 | consumeElement(spliterator, "aaa"); 40 | consumeElement(spliterator, "aaaa"); 41 | assertNull(spliterator.trySplit()); 42 | } 43 | 44 | @Test 45 | public void testIntNoSplitAfterAdvance() { 46 | Spliterator.OfInt spliterator = IntStreamEx.constant(1, 100).unordered().prefix(Integer::sum).spliterator(); 47 | consumeElement(spliterator, 1); 48 | consumeElement(spliterator, 2); 49 | consumeElement(spliterator, 3); 50 | consumeElement(spliterator, 4); 51 | assertNull(spliterator.trySplit()); 52 | } 53 | 54 | @Test 55 | public void testLongNoSplitAfterAdvance() { 56 | Spliterator.OfLong spliterator = LongStreamEx.constant(1, 100).unordered().prefix(Long::sum).spliterator(); 57 | consumeElement(spliterator, 1L); 58 | consumeElement(spliterator, 2L); 59 | consumeElement(spliterator, 3L); 60 | consumeElement(spliterator, 4L); 61 | assertNull(spliterator.trySplit()); 62 | } 63 | 64 | @Test 65 | public void testForEachAfterSplitAndAdvance() { 66 | Spliterator spliterator = StreamEx.constant("a", 5).unordered().prefix(String::concat).spliterator(); 67 | Spliterator spliterator2 = spliterator.trySplit(); 68 | assertNotNull(spliterator2); 69 | Set remainingElements = new HashSet<>(Arrays.asList("a", "aa", "aaa", "aaaa", "aaaaa")); 70 | consumeElement(spliterator, remainingElements); 71 | consumeElement(spliterator2, remainingElements); 72 | spliterator.forEachRemaining(x -> assertTrue(remainingElements.remove(x))); 73 | spliterator2.forEachRemaining(x -> assertTrue(remainingElements.remove(x))); 74 | assertEquals(0, remainingElements.size()); 75 | } 76 | 77 | @Test 78 | public void testIntForEachAfterSplitAndAdvance() { 79 | Spliterator.OfInt spliterator = IntStreamEx.constant(1, 5).unordered().prefix(Integer::sum).spliterator(); 80 | Spliterator.OfInt spliterator2 = spliterator.trySplit(); 81 | assertNotNull(spliterator2); 82 | Set remainingElements = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5)); 83 | consumeElement(spliterator, remainingElements); 84 | consumeElement(spliterator2, remainingElements); 85 | spliterator.forEachRemaining((IntConsumer) x -> assertTrue(remainingElements.remove(x))); 86 | spliterator2.forEachRemaining((IntConsumer) x -> assertTrue(remainingElements.remove(x))); 87 | assertEquals(0, remainingElements.size()); 88 | } 89 | 90 | @Test 91 | public void testLongForEachAfterSplitAndAdvance() { 92 | Spliterator.OfLong spliterator = LongStreamEx.constant(1, 5).unordered().prefix(Long::sum).spliterator(); 93 | Spliterator.OfLong spliterator2 = spliterator.trySplit(); 94 | assertNotNull(spliterator2); 95 | Set remainingElements = new HashSet<>(Arrays.asList(1L, 2L, 3L, 4L, 5L)); 96 | consumeElement(spliterator, remainingElements); 97 | consumeElement(spliterator2, remainingElements); 98 | spliterator.forEachRemaining((LongConsumer) x -> assertTrue(remainingElements.remove(x))); 99 | spliterator2.forEachRemaining((LongConsumer) x -> assertTrue(remainingElements.remove(x))); 100 | assertEquals(0, remainingElements.size()); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/test/java/one/util/streamex/api/StreamExApiTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015, 2024 StreamEx contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package one.util.streamex.api; 17 | 18 | import one.util.streamex.StreamEx; 19 | import org.junit.Test; 20 | 21 | import java.util.List; 22 | import java.util.function.BiFunction; 23 | import java.util.function.Function; 24 | 25 | import static java.util.Arrays.asList; 26 | import static org.junit.Assert.assertArrayEquals; 27 | import static org.junit.Assert.assertEquals; 28 | 29 | /** 30 | * @author Tagir Valeev 31 | */ 32 | public class StreamExApiTest { 33 | @Test 34 | public void testMap() { 35 | BiFunction, Function, StreamEx> streamMapper = StreamEx::map; 36 | assertEquals(asList(2, 4, 10), streamMapper.apply(StreamEx.of(1, 2, 5), x -> x * 2).toList()); 37 | } 38 | 39 | @Test 40 | public void testAppend() { 41 | List input = asList("a", "b", "c"); 42 | assertEquals(input, input.stream().map(StreamEx::of).reduce(StreamEx::append).get().toList()); 43 | } 44 | 45 | @Test 46 | public void testPrepend() { 47 | List input = asList("a", "b", "c"); 48 | List expected = asList("c", "b", "a"); 49 | assertEquals(expected, input.stream().map(StreamEx::of).reduce(StreamEx::prepend).get().toList()); 50 | } 51 | 52 | @Test 53 | public void testMapMulti() { 54 | List result = StreamEx.of("abc", "def", "gh") 55 | .mapMulti((s, cons) -> s.chars() 56 | .mapToObj(ch -> ""+(char)ch).forEach(cons)) 57 | .toList(); 58 | assertEquals(asList("a", "b", "c", "d", "e", "f", "g", "h"), result); 59 | } 60 | 61 | @Test 62 | public void testMapMultiToInt() { 63 | int[] result = StreamEx.of("abc", "def", "gh") 64 | .mapMultiToInt((s, cons) -> s.chars().forEach(cons)) 65 | .toArray(); 66 | assertArrayEquals(new int[] {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}, result); 67 | } 68 | 69 | @Test 70 | public void testMapMultiToLong() { 71 | long[] result = StreamEx.of("abc", "def", "gh") 72 | .mapMultiToLong((s, cons) -> s.chars().asLongStream().forEach(cons)) 73 | .toArray(); 74 | assertArrayEquals(new long[] {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}, result); 75 | } 76 | 77 | @Test 78 | public void testMapMultiToDouble() { 79 | double[] result = StreamEx.of("abc", "def", "gh") 80 | .mapMultiToDouble((s, cons) -> s.chars().asDoubleStream().forEach(cons)) 81 | .toArray(); 82 | assertArrayEquals(new double[] {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}, result, 0.0); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /wiki/MIGRATION.md: -------------------------------------------------------------------------------- 1 | # Migration between StreamEx releases 2 | 3 | This document describes StreamEx changes which may break the backwards compatibility. For full list of changes see [CHANGES.md](CHANGES.md). 4 | 5 | ### 0.8.0 6 | Issue#244: To align with Java 16 `Stream.toList` specification, we no more guarantee that the results of 7 | `AbstractStreamEx.toList` and `AbstractStreamEx.toSet` are mutable. They are still mutable by default 8 | but this will change in future release. You can switch their implementation to immutable, using temporary 9 | system property `-Dstreamex.default.immutable=true` and test how your code behaves. If you actually need mutability, 10 | use new methods `AbstractStreamEx.toMutableList` and `AbstractStreamEx.toMutableSet`. 11 | 12 | ### 0.7.3 13 | Issue#219: Now many collectors defined in `MoreCollectors` may throw NullPointerException more eagerly, before the 14 | collector is used. Also, `MoreCollectors.last` now throws NullPointerException if the last element is null (this 15 | makes it conformant to `MoreCollectors.first`). 16 | 17 | ### 0.7.0 18 | Issue#194: Method `skipOrdered` is removed, which may break source and binary compatibility if in was used. Use simply `skip` instead. 19 | PR#200: Overloads added to `EntryStream.allMatch/anyMatch/noneMatch` which accept a `BiPredicate`. Such overloads may cause 20 | source incompatibility in rare case when a method reference was used which is either bound to var-arg method or 21 | has several overloads. E.g. assuming `boolean allNull(Object... objects)` now `EntryStream.of(...).allMatch(Utils::allNull)` 22 | won't compile anymore. To work-around this issue use a lambda instead of method reference. 23 | 24 | ### 0.6.0 25 | 26 | Issue#67: Now `StreamEx.withFirst()` as well as `StreamEx.withFirst(BinaryOperator)` include `(first, first)` pair. If you used these operations in StreamEx 0.5.3-0.5.5, you should update the existing code: replace `.withFirst()` with `.withFirst().skip(1)`. 27 | 28 | ### 0.5.5 29 | 30 | Issue#41: As `StreamEx.without(T...)` was added, the existing code may become ambiguous now. If you've used `.without(null)` before, replace it with dedicated `.nonNull()` operation. 31 | 32 | Issue#63: `DoubleStreamEx.reverseSorted()` may change the order of non-canonical `NaN` values (actually sorting them). 33 | 34 | ### 0.5.3 35 | 36 | Issue#52: `StreamEx.append(T...)` and `prepend(T...)` are final now, so if you extend `StreamEx` class, you cannot override them anymore. 37 | 38 | ### 0.5.0 39 | 40 | Issue#8: The package `javax.util.streamex` is renamed to `one.util.streamex`. The OSGi bundle name and Maven groupId are changed correspondingly. To migrate to StreamEx 0.5.0 you should: 41 | 42 | * Replace every occurrence of `javax.util.streamex` to `one.util.streamex` in your Java files and OSGi manifests. 43 | * Replace `io.github.amaembo` groupID with `one.util` in your build files (pom.xml, ivy.xml, build.gradle, etc.) 44 | --------------------------------------------------------------------------------