├── .github └── workflows │ ├── branch-name-check.yml │ ├── codeql-analysis.yml │ └── run-java-checkstyle.yml ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── _config.yml ├── build.gradle ├── codestyle ├── checkstyle.xml ├── idea_code_style.xml ├── pmd_main.xml └── pmd_test.xml ├── deploy.sh ├── gradle.properties.enc ├── gradle.template.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── juu 2.0.0.svg ├── private_key.gpg.enc ├── settings.gradle └── src ├── main └── java │ └── com │ └── kirekov │ └── juu │ ├── collection │ ├── ParallelStreaming.java │ ├── Streaming.java │ ├── immutable │ │ ├── Immutable.java │ │ ├── ImmutableArrayList.java │ │ ├── ImmutableCollection.java │ │ ├── ImmutableCollectionUtils.java │ │ ├── ImmutableCollectors.java │ │ ├── ImmutableHashMap.java │ │ ├── ImmutableHashSet.java │ │ ├── ImmutableList.java │ │ ├── ImmutableMap.java │ │ ├── ImmutableMapUtils.java │ │ ├── ImmutableNavigableMap.java │ │ ├── ImmutableNavigableSet.java │ │ ├── ImmutableSet.java │ │ ├── ImmutableSortedMap.java │ │ ├── ImmutableSortedSet.java │ │ ├── ImmutableTreeMap.java │ │ ├── ImmutableTreeSet.java │ │ ├── Pair.java │ │ ├── PairImpl.java │ │ ├── UnmodifiableIterator.java │ │ └── abstraction │ │ │ ├── AbstractImmutableList.java │ │ │ └── AbstractImmutableSet.java │ └── mutable │ │ ├── MutableBoolean.java │ │ ├── MutableByte.java │ │ ├── MutableChar.java │ │ ├── MutableDouble.java │ │ ├── MutableFloat.java │ │ ├── MutableInt.java │ │ ├── MutableLong.java │ │ ├── MutableShort.java │ │ └── MutableValue.java │ ├── exception │ └── EmptyContainerException.java │ ├── lambda │ ├── Action.java │ ├── CachedResultCheckedSupplier.java │ ├── CachedResultSupplier.java │ ├── CheckedFunction.java │ ├── CheckedSupplier.java │ └── TriFunction.java │ ├── measure │ ├── ExecutionResult.java │ ├── Measure.java │ ├── MeasureConverter.java │ ├── MeasureUnit.java │ └── Profiler.java │ └── monad │ └── Try.java └── test └── java └── com └── kirekov └── juu ├── collection ├── immutable │ ├── ImmutableArrayListTest.java │ ├── ImmutableCollectorsTest.java │ ├── ImmutableHashMapTest.java │ ├── ImmutableHashSetTest.java │ ├── ImmutableTest.java │ ├── ImmutableTreeMapTest.java │ ├── ImmutableTreeSetTest.java │ └── PairImplTest.java └── mutable │ ├── MutableBooleanTest.java │ ├── MutableByteTest.java │ ├── MutableCharTest.java │ ├── MutableDoubleTest.java │ ├── MutableFloatTest.java │ ├── MutableIntTest.java │ ├── MutableLongTest.java │ ├── MutableShortTest.java │ └── MutableValueTest.java ├── lambda ├── CachedResultCheckedSupplierTest.java └── CachedResultSupplierTest.java ├── measure ├── MeasureConverterTest.java ├── MeasureTest.java └── ProfilerTest.java └── monad └── TryTest.java /.github/workflows/branch-name-check.yml: -------------------------------------------------------------------------------- 1 | name: 'Branch name check' 2 | on: 3 | pull_request: 4 | types: 5 | - opened 6 | - edited 7 | - reopened 8 | - synchronize 9 | pull_request_target: 10 | types: 11 | - opened 12 | - edited 13 | - reopened 14 | - synchronize 15 | 16 | jobs: 17 | branch-name-check: 18 | name: Branch name check 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Check current branch name 22 | shell: python 23 | run: | 24 | import os 25 | import re 26 | branch = os.getenv('GITHUB_HEAD_REF') 27 | assert re.match(r"^[\d|\w]+/#\d+-.+", branch), 'The branch should be formatted as "type/#TASK_ID-description" but received {}'.format(branch) 28 | print('"{}" branch is successfully checked'.format(branch)) -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master, releases ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master, releases ] 20 | schedule: 21 | - cron: '31 14 * * 4' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'java' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.github/workflows/run-java-checkstyle.yml: -------------------------------------------------------------------------------- 1 | name: "Run Java Checkstyle" 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | checkstyle_job: 7 | runs-on: ubuntu-latest 8 | name: Checkstyle job 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v2 12 | - name: Run check style 13 | uses: nikitasavinov/checkstyle-action@master 14 | with: 15 | github_token: ${{ secrets.GITHUB_TOKEN }} 16 | reporter: 'github-pr-check' 17 | checkstyle_config: 'codestyle/checkstyle.xml' 18 | checkstyle_version: '8.42' 19 | fail_on_error: true 20 | level: error -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out/** 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | pom.xml.next 7 | release.properties 8 | dependency-reduced-pom.xml 9 | buildNumber.properties 10 | .mvn/timing.properties 11 | 12 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) 13 | !/.mvn/wrapper/maven-wrapper.jar 14 | .idea/** 15 | .idea/workspace.xml 16 | /java-useful-utils.iml 17 | .gradle/** 18 | gradle.properties 19 | build/** 20 | private_key.gpg 21 | 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | install: true 4 | 5 | git: 6 | depth: false 7 | 8 | addons: 9 | sonarcloud: 10 | organization: simonharmonicminor 11 | token: 12 | secure: WeqLn+kxvsbuN6yLGqu1U9r7aZvBkOKv7W/1Pf2dvgrTPcPvLHThrKsAbK8qfWkuoG3Y9FbmzPGH3KfiN+yszIN6OxQl6B85FrlZ/sVGUNrW9idCpohX+WncI4zT32oObZTPcQ2kq1AQQfrI0dmZ5ihqltlV/GbBEhYenXwWmNAPNJXlzR2TaF2ApOzLXjdiX/z6jmU/hZCdxxHkPyxjW0Nm1AzwNWq7s+vsI+plOJDaF9skj+7exKKJ0lxFq/hiyBEazt3HZy87OsxJ/9DhYCKwV4FmQu3cBjkWXXl3334bdF5tELyeHMxJzrpX8XXIKZke2Bu+7uES+vCkVyceavnKBEoPeiBC4CdTyuHordrg11dsrSuS63XLFap0gdbWzVi9gacjdmjoqHnjO69AJ0F4yZvHJGwPlLn5zpq4Qa41aWa5BiU7R8hOzRfqSR4xgeiDemhPJhWYsIfLQ1c4id7kYJUfX/3olq9ffDCXVKXe4UHm5cqaq91u865WdNnn1FQP4coy3D4z3thsmBnRn/Z9bH/IaP4cscOxZsXffSHHjOyLzkkYIOIMW08Bwq5WhrTk4+hHnONcSAkXPt0JYQnG1Tw7nUOQ3borNFqGxw4AUEHisnofBfYpW7ggRyj8xx7JZQI7teLPbhuvaXeFfjL9xvsI+LdOaWclZsN5Qeg= 13 | 14 | script: 15 | - "./gradlew clean build jacocoTestReport sonarqube" 16 | 17 | branches: 18 | only: 19 | - master 20 | - releases 21 | 22 | deploy: 23 | - provider: script 24 | skip_cleanup: true 25 | script: bash ./deploy.sh 26 | on: 27 | all_branches: true 28 | condition: "$TRAVIS_BRANCH =~ ^(master|releases)$" 29 | 30 | cache: 31 | directories: 32 | - "$HOME/.m2/repository" 33 | - "$HOME/.sonar/cache" 34 | - "$HOME/.gradle" 35 | - ".gradle" 36 | 37 | before_deploy: 38 | - openssl aes-256-cbc -K $encrypted_56078099ba94_key -iv $encrypted_56078099ba94_iv -in private_key.gpg.enc -out private_key.gpg -d 39 | - openssl aes-256-cbc -K $encrypted_2afbb999f001_key -iv $encrypted_2afbb999f001_iv -in gradle.properties.enc -out gradle.properties -d -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | Do you want to improve the library? 4 | Would you like to see yourself in the `README` [Authors](https://github.com/SimonHarmonicMinor/Java-Useful-Utils#authors) section? 5 | That's great! Here is a guide how to contribute. 6 | 7 | 8 | 1. Submit a new [issue](https://github.com/SimonHarmonicMinor/Java-Useful-Utils/issues) or choose the existing one. 9 | 1. Fork the repository. 10 | 1. Checkout a new branch from `master` 11 | 1. Make the changes and run `./gradlew build` command. If it succeeds, then you can go ahead. 12 | Also, you can run some specific isolated commands. `./gradlew runStaticAnalysis` performs all code quality checks, 13 | and `./gradlew test` run unit tests. 14 | 1. Push the changes and create a new pull request. 15 | Please, put `Resolves #TASK_ID` in the PR description. 16 | 17 | Also you should know that the repository uses [SonarCloud](https://sonarcloud.io/) to measure the test coverage. 18 | If it's lower than 80%, the check doesn't pass. 19 | 20 | The commits should be written according to this pattern: `[#TASK_ID] - message`. 21 | For example, `[#96] - Fixed ImmutableList.slice(int fromIndex, int toIndex) javadoc`. 22 | 23 | The branches should be named as `type/#TASK_ID-desription`. 24 | For example, `feature/#96-move-methods` 25 | 26 | The repository checks commit messages and branches names automatically on each pull request. 27 | So, any violation does not allow proceeding further. 28 | 29 | The release process is automatic. 30 | Each pull request merge to `master` branch uploads a new `DEV-SNAPSHOT` version. 31 | Whilst merge to `releases` creates a new concrete release. 32 | Merge to `releases` can only be made by the repository maintainer. 33 | 34 | That's basically everything you need to know. Feel free to contribute! -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 SimonHarmonicMinor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java Useful Utils 2 | Truly immutable collections, functional errors handling, laziness and measurement utilities. 3 | 4 | This library has no dependencies (except the test scope). 5 | 6 | If you want to contribute, check out the [Contributing Guide](./CONTRIBUTING.md). 7 | 8 | `master` branch provides the latest `DEV-SNAPSHOT` documentation. 9 | You can find the specific version info by git tags. 10 | 11 | ### Table of contents 12 | * [Quick start](#quick-start) 13 | * [Status](#status) 14 | * [Usage](#usage) 15 | * [Authors](#authors) 16 | 17 | ### Quick start 18 | 19 | You need Java 8+ to use the library. 20 | 21 | Maven: 22 | 23 | ```xml 24 | 25 | com.kirekov 26 | java-useful-utils 27 | 28 | ``` 29 | Gradle: 30 | ```groovy 31 | implementation 'com.kirekov:java-useful-utils' 32 | ``` 33 | 34 | ### Status 35 | [![Maven Central](https://img.shields.io/maven-central/v/com.kirekov/java-useful-utils)](https://mvnrepository.com/artifact/com.kirekov/java-useful-utils) 36 | [![Javadoc](https://javadoc.io/badge2/com.kirekov/java-useful-utils/javadoc.svg)](https://javadoc.io/doc/com.kirekov/java-useful-utils) 37 | [![Build Status](https://travis-ci.com/SimonHarmonicMinor/Java-Useful-Utils.svg?branch=master)](https://travis-ci.com/SimonHarmonicMinor/Java-Useful-Utils) 38 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=SimonHarmonicMinor_Java-Useful-Utils&metric=alert_status)](https://sonarcloud.io/dashboard?id=SimonHarmonicMinor_Java-Useful-Utils) 39 | [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=SimonHarmonicMinor_Java-Useful-Utils&metric=coverage)](https://sonarcloud.io/dashboard?id=SimonHarmonicMinor_Java-Useful-Utils) 40 | [![Hits-of-Code](https://hitsofcode.com/github/SimonHarmonicMinor/Java-Useful-Utils)](https://hitsofcode.com/github/SimonHarmonicMinor/Java-Useful-Utils/view) 41 | [![checkstyle](https://img.shields.io/badge/checkstyle-intergrated-informational)](https://checkstyle.sourceforge.io/) 42 | [![PMD](https://img.shields.io/badge/PMD-intergrated-informational)](https://pmd.github.io/pmd-6.35.0/pmd_rules_java.html) 43 | [![MIT License](https://img.shields.io/apm/l/atomic-design-ui.svg?)](https://github.com/SimonHarmonicMinor/Java-Useful-Utils/blob/master/LICENSE) 44 | 45 | ### Usage 46 | The library consists of three big parts. 47 | * [Measurement](#measurement) 48 | * [Monads](#monads) (functional style programming) 49 | * [Collections](#collections) 50 | 51 | ##### Measurement 52 | Sometimes we all face with the problem when we need to measure time of execution of one function or method. 53 | Usually it suppose to be done like this: 54 | 55 | ```java 56 | long before = System.currentTimeMillis(); 57 | int result = doSomething(); 58 | long after = System.currentTimeMillis(); 59 | long measurementResult = after - before; 60 | ``` 61 | 62 | It might be OK, if we need to do this once. 63 | But if there are several functions that have to be measured, 64 | writing the same code snippets every time is gonna be exhausting. 65 | 66 | So here is the solution: 67 | ```java 68 | ExecutionResult exec = 69 | Measure.executionTime(this::doSomething) 70 | .inMillis(); 71 | int res = exec.getResult(); 72 | long time = exec.getTime(); 73 | assert exec.getMeasureUnit() == MeasureUnit.MILLIS; 74 | ``` 75 | 76 | Simple, right? If you need to measure something in different 77 | units, just call the appropriate method 78 | (`inMillis()`, `inNanos()` or `inSeconds()`). 79 | 80 | And what if you need to measure code block from one point to another? 81 | Well, `Profiler` is what you need. 82 | ```java 83 | Profiler profiler = Profiler.startMeasuringInMillis(); 84 | // do some useful stuff 85 | ... 86 | long time = profiler.stopMeasuring(); 87 | ``` 88 | 89 | Such code style is much clearer than putting `System.currentTimeInMillis()` everywhere. 90 | 91 | ##### Monads 92 | I think every java developer used at least one monad - `java.util.Optional`. 93 | This class allows to work with nullable values much more efficiently. 94 | ```java 95 | return Optional.ofNullable(getNullableString()) 96 | .map(str -> str + "I've just edited it") 97 | .orElse("Oh my god. It is empty"); 98 | ``` 99 | 100 | This library has one monads: [`Try`](#try). 101 | 102 | ###### Try 103 | 104 | `Try` allows you to work with methods that may throw an exception 105 | in the same way as `Optional` in a **lazy** way. For instance, suppose we have such code: 106 | 107 | ```java 108 | int num; 109 | try { 110 | String value = getStringValue(); 111 | num = Integer.parseInt(value); 112 | } 113 | catch (NumberFormatException e) { 114 | num = getDefaultIntValue(); 115 | } 116 | ... 117 | try { 118 | return executeRpc(num); 119 | } 120 | catch (RPCException e) { 121 | return SOME_DEFAULT_VALUE; 122 | } 123 | ``` 124 | 125 | In this case we don't care about the exception type but the error fact itself. 126 | JUU allows to rewrite this snippet as two equations: 127 | 128 | ```java 129 | int num = Try.of(() -> Integer.parseInt(getStringValue())) 130 | .orElseGet(() -> getDefaultIntValue()); 131 | return Try.of(() -> executeRpc(num)) 132 | .orElse(SOME_DEFAULT_VALUE); 133 | ``` 134 | 135 | Also we can use `map`, `flatMap` and `filter` 136 | functions. 137 | 138 | ```java 139 | int value = Try.of(this::getStringValue) 140 | .map(this::reverseString) 141 | .flatMap(str -> Try.of(() -> Integer.parseInt(str))) 142 | .filter(num -> num > 0) 143 | .orElseThrow(IllegalArgumentException::new); 144 | ``` 145 | 146 | Let's deconstruct this monad step by step: 147 | 1) Receives some string value from `getStringValue()` method. 148 | 2) Reverses string. 149 | 3) Converts string to integer. 150 | 4) Checks if number is positive. 151 | 5) If any of previous steps fails, throws `IllegalArgumentException`. 152 | 153 | And here is the same code written in pure java: 154 | 155 | ```java 156 | int value; 157 | try { 158 | String val = getStringValue(); 159 | String reversed = reverseString(val); 160 | value = Integer.parseInt(reversed); 161 | if (value <= 0) { 162 | throw new RuntimeException("no no no"); 163 | } 164 | } 165 | catch (Exception e) { 166 | throw new IllegalArgumentException(); 167 | } 168 | ``` 169 | 170 | If you need the exception that led to the error, you can use 171 | `orElseGet` variation. 172 | 173 | ```java 174 | Try.of(() -> Integer.parseInt(getStringValue)) 175 | .map(x -> 2 % x) 176 | .orElseGet((Exception reason) -> { 177 | log.error("Something went wrong", reason); 178 | return 0; 179 | }) 180 | ``` 181 | 182 | `reason` has the type of `Exception` and it's the instance of that very 183 | exception that broke the chain. 184 | For instance, if `Integer.parseInt` threw an exception, 185 | the `reason` would be the type of `NumberFormatException`. 186 | On the other hand, if `x == 0` in the `map` callback, 187 | the reason would be the type of `ArithmeticException`. 188 | 189 | The class only catches `Exception` type. 190 | It means that all `Throwable` instances are skipped. 191 | The motivation is that `Error` extends from `Throwable` but these exceptions should not be caught manually. 192 | 193 | The fact that `Try` monad acts *lazily* means 194 | that you build a pipeline of execution that triggers on any *terminal* operation. 195 | 196 | ```java 197 | Try t = Try.of(() -> { 198 | println("First step"); 199 | return 1; 200 | }).map(val -> { 201 | println("Second step"); 202 | return val + 1; 203 | }).filter(val -> { 204 | println("Third step"); 205 | return val > 0; 206 | }); 207 | // nothing prints here 208 | 209 | assert 2 == t.orElseThrow(); 210 | // First step 211 | // Second step 212 | // Third step 213 | ``` 214 | 215 | All terminal operations are listed in the [javadoc](./src/main/java/com/kirekov/juu/monad/Try.java). 216 | 217 | ##### Collections 218 | 219 | The "Collections" part consists of two subparts: 220 | ["Immutable collections"](#immutable-collections) and 221 | ["Mutable containers"](#mutable-containers). 222 | 223 | ###### Immutable collections 224 | 225 | One of the most irritating thing for me in Java 226 | is total absence of immutable collections. 227 | For instance, suppose we have such code: 228 | 229 | ```java 230 | List numbers = getNumbers(); 231 | List result = doSomething(numbers); 232 | ``` 233 | 234 | Have this list been changed? What have been returned by 235 | `doSomething()`? The same list or the new one? 236 | If we delete an element from `numbers`, 237 | will it have an effect on `result`? 238 | 239 | Well, we can implement our own "immutable" list 240 | that inherits `java.util.List` and make mutating 241 | methods (`add`, `clear`, `set`...) throw an exception. 242 | But how do we know what implementation has been 243 | passed as a parameter? It is not convenient to write 244 | `try {} catch (Exception e) {}` or use [`Try`](#try) 245 | on every `add` call. 246 | 247 | JUU library provides new collections, which interfaces 248 | do not inherits from java native Collections. 249 | Here is the scheme ![scheme](./juu%202.0.0.svg) 250 | 251 | The recommended way to instantiate immutable collections is to use `Immutable` class. 252 | ```java 253 | // lists 254 | 255 | ImmutableList list1 = 256 | Immutable.listOf(1, 2, 3) // accepts T... 257 | ImmutableList list2 = 258 | Immutable.listOf(Arrays.asList("1", "2", "3")) // accepts Iterable 259 | 260 | // sets 261 | 262 | ImmutableSet set1 = 263 | Immutable.setOf(1, 2, 3); 264 | ImmutableSet set2 = 265 | Immutable.setOf(Arrays.asList("1", "2", "3")); 266 | 267 | // maps 268 | 269 | ImmutableMap map1 = 270 | Immutable.mapOf("1", 1, "2", 2, "3", 3); 271 | ImmutableMap map2 = 272 | Immutable.mapOf(Arrays.asList( 273 | Pair.of("1", 2), 274 | Pair.of("2", 2), 275 | Pair.of("3", 3) 276 | )) 277 | ``` 278 | 279 | You can also use collectors from `ImmutableCollectors` to create immutable collections 280 | from `Stream`. 281 | 282 | ```java 283 | ImmutableList list = 284 | getNumbersList().stream() 285 | .map(String::valueOf) 286 | .collect(ImmutableCollectors.toList()); 287 | 288 | ImmutableSet set = 289 | getNumbersList().stream() 290 | .map(String::valueOf) 291 | .collect(ImmutableCollectors.toSet()); 292 | 293 | ImmutableMap map = 294 | getNumbersList().stream() 295 | .collect(ImmutableCollectors.toMap( 296 | String::valueOf, 297 | value -> value 298 | )); 299 | ``` 300 | 301 | You can user Stream API with immutable collections as well, 302 | but `ImmutableList` and `ImmutableSet` provides kotlin-like methods: 303 | `map`, `flatMap`, `filter`, `min`, `max` and `zip` (for lists). 304 | `ImmutableList` also has `sorted`, `zipWith`, `zipWithNext` and indexed methods 305 | `mapIndexed`, `flatMapIndexed`, `filterIndexed`. 306 | 307 | ```java 308 | ImmutableList mappedList = list.map(Integer::parseInt); 309 | ImmutableSet mappedSet = set.flatMap(str -> Arrays.asList(str, str + "1")); 310 | 311 | ImmutableList filtered1 = mappedList.filter(x -> x > 0); 312 | ImmutableList filtered2 = 313 | mappedList.filterIndexed((index, val) -> index % 2 == 0); 314 | ``` 315 | 316 | `ImmutableList` provides Python-like Slice API. 317 | Which means, that you can use negative indices, steps and negative steps. 318 | 319 | ```java 320 | ImmutableList list = getList(); // [1, 2, 3, 4, 5, 6] 321 | assert list.get(list.size() - 1) == list.get(-1) // 6 322 | 323 | // startIndex (inclusively), endIndex (exclusively), stepSize 324 | list.slice(0, 3, 1); // [1, 2, 3] 325 | list.slice(-1, 2, -1); // [6, 5, 4] 326 | list.slice(0, 6, 2); // [1, 3, 5] 327 | list.step(3) // [1, 4] 328 | // if stepSize is negative, startIndex is -1 329 | list.step(-2) // [6, 4, 2] 330 | ``` 331 | 332 | ###### Mutable containers 333 | 334 | Sometimes we need to return several values from method. 335 | Problem can be solved by creating values wrapper. 336 | But it may produce tons of "infrastructural code", especially if types vary. 337 | JUU has special mutable containers that can be passed as a parameter. 338 | For instance 339 | 340 | ```java 341 | List someList = ...; 342 | MutableValue biggest = new MutableValue<>(null); 343 | // suppose to call `biggest.setValue(row)` 344 | int affectedRows = fillWithMagic(someList, biggest); 345 | ... 346 | return biggest.getValue(); 347 | ``` 348 | Also lib has implementations for each primitive type. 349 | `MutableInt`, `MutableDouble`, `MutableShort`, 350 | `MutableLong`, `MutableFloat`, `MutableChar`, 351 | `MutableByte`, `MutableBoolean`. 352 | 353 | ### Authors 354 | - [@SimonHarmonicMinor](https://github.com/SimonHarmonicMinor) -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'maven-publish' 4 | id 'signing' 5 | id 'jacoco' 6 | id "org.sonarqube" version "2.7.1" 7 | id 'checkstyle' 8 | id "io.github.gradle-nexus.publish-plugin" version "1.1.0" 9 | id 'pmd' 10 | } 11 | 12 | tasks.withType(JavaCompile) { 13 | options.compilerArgs << "-Xlint:all" << "-Xlint:-serial" << "-Werror" 14 | } 15 | 16 | java { 17 | withJavadocJar() 18 | withSourcesJar() 19 | sourceCompatibility = JavaVersion.VERSION_1_8 20 | targetCompatibility = JavaVersion.VERSION_1_8 21 | } 22 | 23 | group 'com.kirekov' 24 | version 'DEV-SNAPSHOT' 25 | description 'Just some useful utils for everyday coding' 26 | 27 | pmd { 28 | consoleOutput = true 29 | toolVersion = "6.31.0" 30 | ignoreFailures = false 31 | ruleSets = [] 32 | } 33 | 34 | pmdMain { 35 | ruleSetFiles = files("codestyle/pmd_main.xml") 36 | } 37 | 38 | pmdTest { 39 | ruleSetFiles = files("codestyle/pmd_test.xml") 40 | } 41 | 42 | task runStaticAnalysis(dependsOn: [checkstyleMain, checkstyleTest, pmdMain, pmdTest]) 43 | 44 | checkstyle { 45 | configFile = file('codestyle/checkstyle.xml') 46 | ignoreFailures = false 47 | maxWarnings = 0 48 | showViolations = true 49 | toolVersion = '8.42' 50 | } 51 | 52 | repositories { 53 | mavenCentral() 54 | } 55 | 56 | dependencies { 57 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.1' 58 | testImplementation 'org.awaitility:awaitility:3.1.6' 59 | testImplementation 'org.mockito:mockito-core:2.28.2' 60 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.1' 61 | } 62 | 63 | test { 64 | useJUnitPlatform() 65 | } 66 | 67 | task testsJar(type: Jar) { 68 | archiveClassifier = 'tests' 69 | from sourceSets.test.output 70 | } 71 | 72 | publishing { 73 | publications { 74 | javaUsefulUtils(MavenPublication) { 75 | artifact jar { 76 | extension 'jar' 77 | } 78 | artifact javadocJar { 79 | classifier 'javadoc' 80 | extension 'jar' 81 | } 82 | artifact sourcesJar { 83 | classifier 'sources' 84 | extension 'jar' 85 | } 86 | artifact testsJar { 87 | classifier 'tests' 88 | extension 'jar' 89 | } 90 | pom { 91 | name = 'Java Useful Utils' 92 | description = 'Just some useful utils for everyday coding' 93 | url = 'https://github.com/SimonHarmonicMinor/Java-Useful-Utils' 94 | licenses { 95 | license { 96 | name = 'MIT License' 97 | url = 'https://www.mit.edu/~amini/LICENSE.md' 98 | } 99 | } 100 | developers { 101 | developer { 102 | id = 'kirekov' 103 | name = 'Semyon Kirekov' 104 | email = 'semyon@kirekov.com' 105 | } 106 | } 107 | scm { 108 | connection = 'scm:https://github.com/SimonHarmonicMinor/Java-Useful-Utils.git' 109 | developerConnection = 'scm:git://github.com/SimonHarmonicMinor/Java-Useful-Utils.git' 110 | url = 'https://github.com/SimonHarmonicMinor/Java-Useful-Utils' 111 | } 112 | } 113 | } 114 | } 115 | repositories { 116 | maven { 117 | def releasesRepoUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/' 118 | def snapshotsRepoUrl = 'https://oss.sonatype.org/content/repositories/snapshots' 119 | url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl 120 | credentials { 121 | username findProperty('ossSonatypeUsername') 122 | password findProperty('ossSonatypePassword') 123 | } 124 | } 125 | } 126 | } 127 | 128 | signing { 129 | sign publishing.publications.javaUsefulUtils 130 | } 131 | 132 | nexusPublishing { 133 | repositories { 134 | sonatype { 135 | username = findProperty('ossSonatypeUsername') 136 | password = findProperty('ossSonatypePassword') 137 | } 138 | } 139 | } 140 | 141 | javadoc { 142 | if (JavaVersion.current().isJava9Compatible()) { 143 | options.addBooleanOption('html5', true) 144 | } 145 | } 146 | 147 | jacocoTestReport { 148 | reports { 149 | xml.enabled true 150 | } 151 | } 152 | 153 | sonarqube { 154 | properties { 155 | property "sonar.projectKey", "SimonHarmonicMinor_Java-Useful-Utils" 156 | property "sonar.coverage.jacoco.xmlReportPaths", "build/reports/jacoco/test/jacocoTestReport.xml" 157 | } 158 | } -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./gradlew publishToSonatype -------------------------------------------------------------------------------- /gradle.properties.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimonHarmonicMinor/Java-Useful-Utils/b1ac46736dacd4341c250e16a05fa6377f3a3d94/gradle.properties.enc -------------------------------------------------------------------------------- /gradle.template.properties: -------------------------------------------------------------------------------- 1 | # This is a template file for 'gradle.properties`. 2 | # Its presence is required to release a new version to Maven Central. 3 | 4 | 5 | # User that has the permission to deploy under specified group-id 6 | ossSonatypeUsername= 7 | # User's password 8 | ossSonatypePassword= 9 | 10 | # ID for the private GPG key. It should be 8 characters long. If the string is longer, take the last 8 characters. 11 | signing.keyId= 12 | # The passphrase that was used for private key generation. 13 | signing.password= 14 | # Path to private key. 15 | signing.secretKeyRingFile=private_key.gpg 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimonHarmonicMinor/Java-Useful-Utils/b1ac46736dacd4341c250e16a05fa6377f3a3d94/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /private_key.gpg.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimonHarmonicMinor/Java-Useful-Utils/b1ac46736dacd4341c250e16a05fa6377f3a3d94/private_key.gpg.enc -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'java-useful-utils' -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/ParallelStreaming.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection; 2 | 3 | import java.util.stream.Stream; 4 | 5 | /** 6 | * Allows to convert object to parallel stream. 7 | * 8 | * @param the type of the stream content 9 | * @since 1.0 10 | */ 11 | public interface ParallelStreaming extends Streaming { 12 | 13 | /** 14 | * Get {@linkplain Stream#parallel()} from the object. 15 | * 16 | * @return parallel stream of object content 17 | */ 18 | Stream parallelStream(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/Streaming.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection; 2 | 3 | import java.util.stream.Stream; 4 | 5 | /** 6 | * Allows to convert an object to {@link Stream}. 7 | * 8 | * @param the type of the stream content 9 | * @since 1.0 10 | */ 11 | @FunctionalInterface 12 | public interface Streaming { 13 | 14 | /** 15 | * Get {@linkplain Stream} from the object. 16 | * 17 | * @return stream of object content 18 | */ 19 | Stream stream(); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/Immutable.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.Objects; 8 | import java.util.stream.Collectors; 9 | 10 | /** 11 | * Static class for retrieving empty sets and lists or instantiating immutable collections. The 12 | * class caches empty lists and sets in order not to create new objects unless it is necessary. So, 13 | * it is recommended to use methods from this class, but not to create immutable collections via 14 | * their constructors. 15 | * 16 | * @see Collections 17 | * @see ImmutableCollection 18 | * @see ImmutableList 19 | * @see ImmutableSet 20 | * @see ImmutableMap 21 | * @see Pair 22 | * @since 1.0 23 | */ 24 | public final class Immutable { 25 | 26 | private static final ImmutableArrayList EMPTY_ARRAY_LIST = 27 | new ImmutableArrayList<>(Collections.emptyList()); 28 | private static final ImmutableHashSet EMPTY_HASH_SET = 29 | new ImmutableHashSet<>(Collections.emptyList()); 30 | private static final ImmutableHashMap EMPTY_HASH_MAP = 31 | new ImmutableHashMap<>(Collections.emptyMap()); 32 | 33 | /** 34 | * Suppresses default constructor, ensuring non-instantiability. 35 | */ 36 | private Immutable() { 37 | } 38 | 39 | /** 40 | * Returns empty immutable list. Does not create the new one, returns the same instance every 41 | * time. 42 | * 43 | * @param the type of the list content 44 | * @return empty list 45 | */ 46 | @SuppressWarnings("unchecked") 47 | public static ImmutableList emptyList() { 48 | return (ImmutableList) EMPTY_ARRAY_LIST; 49 | } 50 | 51 | /** 52 | * Returns empty immutable set. Does not create the new one, returns the same instance every 53 | * time. 54 | * 55 | * @param the type of the set content 56 | * @return empty set 57 | */ 58 | @SuppressWarnings("unchecked") 59 | public static ImmutableSet emptySet() { 60 | return (ImmutableSet) EMPTY_HASH_SET; 61 | } 62 | 63 | /** 64 | * Returns empty map. Does not create the new one, returns the same instance every time 65 | * 66 | * @param the type of the key 67 | * @param the type of the value 68 | * @return empty map 69 | */ 70 | @SuppressWarnings("unchecked") 71 | public static ImmutableMap emptyMap() { 72 | return (ImmutableMap) EMPTY_HASH_MAP; 73 | } 74 | 75 | /** 76 | * Creates new immutable list from given elements. If array is empty, returns {@link 77 | * Immutable#emptyList()}. This is the preferred way of creating immutable lists, unless you need 78 | * particular implementation. 79 | * 80 | * @param elements array of elements 81 | * @param the type of the element 82 | * @return immutable list 83 | * @throws NullPointerException if {@code elements} is null 84 | */ 85 | @SafeVarargs 86 | @SuppressWarnings("varargs") 87 | public static ImmutableList listOf(T... elements) { 88 | return listOf(Arrays.stream(elements).collect(Collectors.toList())); 89 | } 90 | 91 | /** 92 | * Creates new immutable list from given elements. If iterable has no elements, returns {@link 93 | * Immutable#emptyList()}. This is the preferred way of creating immutable lists, unless you need 94 | * particular implementation. 95 | * 96 | * @param elements iterable elements 97 | * @param the type of the element 98 | * @return immutable list 99 | * @throws NullPointerException if {@code elements} is null 100 | */ 101 | public static ImmutableList listOf(Iterable elements) { 102 | Objects.requireNonNull(elements); 103 | if (!elements.iterator().hasNext()) { 104 | return emptyList(); 105 | } 106 | return new ImmutableArrayList<>(elements); 107 | } 108 | 109 | /** 110 | * Creates new immutable set from given elements. If array is empty, returns {@link 111 | * Immutable#emptySet()}. This is the preferred way of creating immutable sets, unless you need 112 | * particular implementation. 113 | * 114 | * @param elements array of elements 115 | * @param the type of the element 116 | * @return immutable set 117 | * @throws NullPointerException if {@code elements} is null 118 | */ 119 | @SafeVarargs 120 | @SuppressWarnings({"varargs", "PMD.LinguisticNaming"}) 121 | public static ImmutableSet setOf(T... elements) { 122 | Objects.requireNonNull(elements); 123 | return setOf(Arrays.stream(elements).collect(Collectors.toSet())); 124 | } 125 | 126 | /** 127 | * Creates new immutable set from given elements. If iterable has no elements, returns {@link 128 | * Immutable#emptySet()}. This is the preferred way of creating immutable sets, unless you need 129 | * particular implementation. 130 | * 131 | * @param elements iterable elements 132 | * @param the type of the element 133 | * @return immutable set 134 | * @throws NullPointerException if {@code elements} is null 135 | */ 136 | @SuppressWarnings("PMD.LinguisticNaming") 137 | public static ImmutableSet setOf(Iterable elements) { 138 | Objects.requireNonNull(elements); 139 | if (!elements.iterator().hasNext()) { 140 | return emptySet(); 141 | } 142 | return new ImmutableHashSet<>(elements); 143 | } 144 | 145 | /** 146 | * Creates {@linkplain ImmutableMap} from regular java {@linkplain Map}. The values are copied 147 | * from the source. So, {@code map} modifying does not affect the resulted immutable one. 148 | * 149 | * @param map source to build {@linkplain ImmutableMap} 150 | * @param type of key 151 | * @param type of value 152 | * @return immutable map 153 | * @throws NullPointerException if {@code map} is null 154 | */ 155 | public static ImmutableMap mapOf(Map map) { 156 | Objects.requireNonNull(map); 157 | return new ImmutableHashMap<>(map); 158 | } 159 | 160 | /** 161 | * Creates {@linkplain ImmutableMap} from {@linkplain Iterable} of {@linkplain Pair}. 162 | * 163 | * @param pairs {@linkplain Iterable} of {@linkplain Pair} that are used as map entries 164 | * @param the type of key 165 | * @param the type of value 166 | * @return immutable map 167 | * @throws NullPointerException if {@code map} is null 168 | */ 169 | public static ImmutableMap mapOf(Iterable> pairs) { 170 | Objects.requireNonNull(pairs); 171 | if (!pairs.iterator().hasNext()) { 172 | return emptyMap(); 173 | } 174 | final HashMap hashMap = new HashMap<>(); 175 | for (final Pair p : pairs) { 176 | hashMap.put(p.getKey(), p.getValue()); 177 | } 178 | return new ImmutableHashMap<>(hashMap); 179 | } 180 | 181 | /** 182 | * Creates {@linkplain ImmutableMap} from one key and value. 183 | * 184 | * @param k the key 185 | * @param v the value 186 | * @param the type of key 187 | * @param the type of value 188 | * @return immutable map 189 | * @see Immutable#mapOf(Iterable) 190 | */ 191 | public static ImmutableMap mapOf(K k, V v) { 192 | return mapOf(Collections.singletonList(Pair.of(k, v))); 193 | } 194 | 195 | /** 196 | * Creates {@linkplain ImmutableMap} from two keys and values. 197 | * 198 | * @param k1 the first key 199 | * @param v1 the first value 200 | * @param k2 the second key 201 | * @param v2 the second value 202 | * @param the type of the key 203 | * @param the type of the value 204 | * @return immutable map 205 | * @see Immutable#mapOf(Iterable) 206 | */ 207 | public static ImmutableMap mapOf(K k1, V v1, K k2, V v2) { 208 | return mapOf(Arrays.asList(Pair.of(k1, v1), Pair.of(k2, v2))); 209 | } 210 | 211 | /** 212 | * Creates {@linkplain ImmutableMap} from three keys and values. 213 | * 214 | * @param k1 the first key 215 | * @param v1 the first value 216 | * @param k2 the second key 217 | * @param v2 the second value 218 | * @param k3 the third key 219 | * @param v3 the third value 220 | * @param the type of the key 221 | * @param the type of the value 222 | * @return immutable map 223 | * @see Immutable#mapOf(Iterable) 224 | */ 225 | public static ImmutableMap mapOf(K k1, V v1, K k2, V v2, K k3, V v3) { 226 | return mapOf(Arrays.asList(Pair.of(k1, v1), Pair.of(k2, v2), Pair.of(k3, v3))); 227 | } 228 | 229 | /** 230 | * Creates {@linkplain ImmutableMap} from four keys and values. 231 | * 232 | * @param k1 the first key 233 | * @param v1 the first value 234 | * @param k2 the second key 235 | * @param v2 the second value 236 | * @param k3 the third key 237 | * @param v3 the third value 238 | * @param k4 the fourth key 239 | * @param v4 the fourth value 240 | * @param the type of the key 241 | * @param the type of the value 242 | * @return immutable map 243 | * @see Immutable#mapOf(Iterable) 244 | */ 245 | public static ImmutableMap mapOf(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { 246 | return mapOf(Arrays.asList(Pair.of(k1, v1), Pair.of(k2, v2), Pair.of(k3, v3), Pair.of(k4, v4))); 247 | } 248 | 249 | /** 250 | * Creates {@linkplain ImmutableMap} from five keys and values. 251 | * 252 | * @param k1 the first key 253 | * @param v1 the first value 254 | * @param k2 the second key 255 | * @param v2 the second value 256 | * @param k3 the third key 257 | * @param v3 the third value 258 | * @param k4 the fourth key 259 | * @param v4 the fourth value 260 | * @param k5 the fifth key 261 | * @param v5 the fifth value 262 | * @param the type of the key 263 | * @param the type of the value 264 | * @return immutable map 265 | * @see Immutable#mapOf(Iterable) 266 | */ 267 | public static ImmutableMap mapOf( 268 | K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5 269 | ) { 270 | return mapOf( 271 | Arrays.asList( 272 | Pair.of(k1, v1), 273 | Pair.of(k2, v2), 274 | Pair.of(k3, v3), 275 | Pair.of(k4, v4), 276 | Pair.of(k5, v5))); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableArrayList.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import com.kirekov.juu.collection.immutable.abstraction.AbstractImmutableList; 4 | import java.util.ArrayList; 5 | import java.util.Comparator; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | import java.util.Objects; 9 | import java.util.OptionalInt; 10 | import java.util.function.BiConsumer; 11 | import java.util.function.BiFunction; 12 | import java.util.function.BiPredicate; 13 | import java.util.function.Function; 14 | import java.util.function.Predicate; 15 | import java.util.stream.Stream; 16 | 17 | /** 18 | * An immutable implementation of java native {@link ArrayList}. 19 | * 20 | * @param the type of the content 21 | * @see ImmutableList 22 | * @see List 23 | * @see ArrayList 24 | * @since 1.0 25 | */ 26 | public final class ImmutableArrayList extends AbstractImmutableList { 27 | 28 | private final List arrayList; 29 | 30 | /** 31 | * Constructor. 32 | * 33 | * @param iterable the source of elements 34 | */ 35 | public ImmutableArrayList(Iterable iterable) { 36 | super(); 37 | arrayList = new ArrayList<>(); 38 | for (final T element : iterable) { 39 | arrayList.add(element); 40 | } 41 | } 42 | 43 | @Override 44 | public T get(int index) { 45 | final int normalized = normalizeIndex(index); 46 | checkIndex(normalized); 47 | return arrayList.get(normalized); 48 | } 49 | 50 | @Override 51 | public OptionalInt indexOf(T element) { 52 | final int index = arrayList.indexOf(element); 53 | if (index == -1) { 54 | return OptionalInt.empty(); 55 | } 56 | return OptionalInt.of(index); 57 | } 58 | 59 | @Override 60 | public OptionalInt lastIndexOf(T element) { 61 | final int index = arrayList.lastIndexOf(element); 62 | if (index == -1) { 63 | return OptionalInt.empty(); 64 | } 65 | return OptionalInt.of(index); 66 | } 67 | 68 | @Override 69 | public ImmutableList slice(int fromIndex) { 70 | return slice(fromIndex, size(), 1); 71 | } 72 | 73 | @Override 74 | public ImmutableList slice(int fromIndex, int toIndex) { 75 | final int fromNorm = normalizeIndex(fromIndex); 76 | final int toNorm = normalizeIndex(toIndex); 77 | return slice(fromNorm, toNorm, fromNorm < toNorm ? 1 : -1); 78 | } 79 | 80 | @Override 81 | public ImmutableList slice(int fromIndex, int toIndex, int stepSize) { 82 | int fromNorm = normalizeIndex(fromIndex); 83 | final int toNorm = normalizeIndex(toIndex); 84 | checkIndex(fromNorm); 85 | checkStepSize(stepSize); 86 | final BiFunction condition = 87 | fromNorm <= toNorm 88 | ? (from, to) -> from < to && from < size() 89 | : (from, to) -> from > to && from >= 0; 90 | final Function nextValueFunc = index -> index + stepSize; 91 | final ArrayList newArrayList = new ArrayList<>(); 92 | while (condition.apply(fromNorm, toNorm)) { 93 | newArrayList.add(get(fromNorm)); 94 | fromNorm = nextValueFunc.apply(fromNorm); 95 | } 96 | return new ImmutableArrayList<>(newArrayList); 97 | } 98 | 99 | @Override 100 | public ImmutableList step(int fromIndex, int stepSize) { 101 | checkStepSize(stepSize); 102 | checkIndex(normalizeIndex(fromIndex)); 103 | if (stepSize > 0) { 104 | return slice(fromIndex, size(), stepSize); 105 | } else { 106 | return slice(fromIndex, -size() - 1, stepSize); 107 | } 108 | } 109 | 110 | @Override 111 | public ImmutableList concatWith(Iterable iterable) { 112 | Objects.requireNonNull(iterable, "iterable to concat with cannot be null"); 113 | final ArrayList copy = new ArrayList<>(this.arrayList); 114 | for (final T t : iterable) { 115 | copy.add(t); 116 | } 117 | return new ImmutableArrayList<>(copy); 118 | } 119 | 120 | @Override 121 | public ImmutableList> zipWith(ImmutableList list) { 122 | Objects.requireNonNull(list, "list to zip with cannot be null"); 123 | final int maxSize = Math.max(size(), list.size()); 124 | final ArrayList> newArrayList = new ArrayList<>(maxSize); 125 | for (int i = 0; i < maxSize; i++) { 126 | final T left = getValByIndex(this, i); 127 | final R right = getValByIndex(list, i); 128 | newArrayList.add(Pair.of(left, right)); 129 | } 130 | return new ImmutableArrayList<>(newArrayList); 131 | } 132 | 133 | @Override 134 | public ImmutableList> zipWithNext() { 135 | final ArrayList> newArrayList = new ArrayList<>(size()); 136 | for (int i = 0; i < size() - 1; i++) { 137 | newArrayList.add(Pair.of(get(i), get(i + 1))); 138 | } 139 | return new ImmutableArrayList<>(newArrayList); 140 | } 141 | 142 | @Override 143 | public ImmutableList map(Function mapper) { 144 | Objects.requireNonNull(mapper, "mapper function cannot be null"); 145 | final ArrayList newList = new ArrayList<>(arrayList.size()); 146 | for (final T t : arrayList) { 147 | newList.add(mapper.apply(t)); 148 | } 149 | return new ImmutableArrayList<>(newList); 150 | } 151 | 152 | @Override 153 | public ImmutableList mapIndexed(BiFunction mapper) { 154 | Objects.requireNonNull(mapper, "indexed mapper function cannot be null"); 155 | final ArrayList newList = new ArrayList<>(arrayList.size()); 156 | for (int i = 0; i < arrayList.size(); i++) { 157 | newList.add(mapper.apply(i, arrayList.get(i))); 158 | } 159 | return new ImmutableArrayList<>(newList); 160 | } 161 | 162 | @Override 163 | public ImmutableList flatMap(Function> mapper) { 164 | Objects.requireNonNull(mapper, "flat mapper function cannot be null"); 165 | final ArrayList newList = new ArrayList<>(arrayList.size()); 166 | for (final T t : arrayList) { 167 | final ImmutableArrayList listElement = new ImmutableArrayList<>(mapper.apply(t)); 168 | newList.addAll(listElement.arrayList); 169 | } 170 | return new ImmutableArrayList<>(newList); 171 | } 172 | 173 | @Override 174 | public ImmutableList flatMapIndexed( 175 | BiFunction> mapper 176 | ) { 177 | Objects.requireNonNull(mapper, "indexed flat mapper function cannot be null"); 178 | final ArrayList newList = new ArrayList<>(arrayList.size()); 179 | for (int i = 0; i < arrayList.size(); i++) { 180 | final ImmutableArrayList listElement = 181 | new ImmutableArrayList<>(mapper.apply(i, arrayList.get(i))); 182 | newList.addAll(listElement.arrayList); 183 | } 184 | return new ImmutableArrayList<>(newList); 185 | } 186 | 187 | @Override 188 | public ImmutableList filter(Predicate predicate) { 189 | Objects.requireNonNull(predicate, "filtering predicate cannot be null"); 190 | final ArrayList newList = new ArrayList<>(arrayList.size()); 191 | for (final T t : arrayList) { 192 | if (predicate.test(t)) { 193 | newList.add(t); 194 | } 195 | } 196 | return new ImmutableArrayList<>(newList); 197 | } 198 | 199 | @Override 200 | public ImmutableList filterIndexed(BiPredicate predicate) { 201 | Objects.requireNonNull(predicate, "indexed filtering predicate cannot be null"); 202 | final ArrayList newList = new ArrayList<>(arrayList.size()); 203 | for (int i = 0; i < arrayList.size(); i++) { 204 | if (predicate.test(i, arrayList.get(i))) { 205 | newList.add(arrayList.get(i)); 206 | } 207 | } 208 | return new ImmutableArrayList<>(newList); 209 | } 210 | 211 | @Override 212 | public void forEachIndexed(BiConsumer action) { 213 | Objects.requireNonNull(action, "indexed for-each consumer cannot be null"); 214 | for (int i = 0; i < arrayList.size(); i++) { 215 | action.accept(i, arrayList.get(i)); 216 | } 217 | } 218 | 219 | @Override 220 | public ImmutableList sorted(Comparator comparator) { 221 | Objects.requireNonNull(comparator, "sorting comparator cannot be null"); 222 | final ArrayList copy = new ArrayList<>(arrayList); 223 | copy.sort(comparator); 224 | return new ImmutableArrayList<>(copy); 225 | } 226 | 227 | @Override 228 | public ImmutableList limit(int size) { 229 | if (size < 0) { 230 | throw new IllegalArgumentException(String.format("Limit size is less than zero: %s", size)); 231 | } 232 | final ArrayList newList = new ArrayList<>(arrayList.size()); 233 | for (int i = 0; i < Math.min(size(), size); i++) { 234 | newList.add(arrayList.get(i)); 235 | } 236 | return new ImmutableArrayList<>(newList); 237 | } 238 | 239 | @Override 240 | public ImmutableList skip(int size) { 241 | if (size < 0) { 242 | throw new IllegalArgumentException(String.format("Skip size is less than zero: %s", size)); 243 | } 244 | final ArrayList newList = new ArrayList<>(arrayList.size()); 245 | for (int i = Math.min(size, size()); i < arrayList.size(); i++) { 246 | newList.add(arrayList.get(i)); 247 | } 248 | return new ImmutableArrayList<>(newList); 249 | } 250 | 251 | @Override 252 | public int size() { 253 | return arrayList.size(); 254 | } 255 | 256 | @Override 257 | public boolean contains(Object element) { 258 | return arrayList.contains(element); 259 | } 260 | 261 | @Override 262 | public ImmutableList toList() { 263 | return this; 264 | } 265 | 266 | @Override 267 | public ImmutableSet toSet() { 268 | return Immutable.setOf(arrayList); 269 | } 270 | 271 | @Override 272 | public Stream parallelStream() { 273 | return arrayList.parallelStream(); 274 | } 275 | 276 | @Override 277 | public Stream stream() { 278 | return arrayList.stream(); 279 | } 280 | 281 | @Override 282 | public Iterator iterator() { 283 | return new UnmodifiableIterator<>(arrayList.iterator()); 284 | } 285 | 286 | @Override 287 | public boolean equals(Object o) { 288 | if (this == o) { 289 | return true; 290 | } 291 | if (o == null || getClass() != o.getClass()) { 292 | return false; 293 | } 294 | final ImmutableArrayList that = (ImmutableArrayList) o; 295 | return arrayList.equals(that.arrayList); 296 | } 297 | 298 | @Override 299 | public int hashCode() { 300 | return arrayList.hashCode(); 301 | } 302 | 303 | private int normalizeIndex(int index) { 304 | return index >= 0 ? index : size() + index; 305 | } 306 | 307 | private void checkStepSize(int stepSize) { 308 | if (stepSize == 0) { 309 | throw new IllegalArgumentException("Step size cannot be zero"); 310 | } 311 | } 312 | 313 | private void checkIndex(int index) { 314 | if (index < 0 || index >= size()) { 315 | throw new IndexOutOfBoundsException(String.format("Index %d is out of bounds", index)); 316 | } 317 | } 318 | 319 | private static R getValByIndex(ImmutableList immutableList, int index) { 320 | if (index < immutableList.size()) { 321 | return immutableList.get(index); 322 | } 323 | return null; 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableCollectionUtils.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import com.kirekov.juu.monad.Try; 4 | import java.util.Objects; 5 | import java.util.Optional; 6 | import java.util.function.Supplier; 7 | 8 | class ImmutableCollectionUtils { 9 | 10 | private ImmutableCollectionUtils() { 11 | } 12 | 13 | static Optional tryGetElement(Supplier supplier) { 14 | Objects.requireNonNull(supplier); 15 | return Try.of(supplier::get) 16 | .map(Optional::ofNullable) 17 | .orElse(Optional.empty()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableCollectors.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Objects; 8 | import java.util.function.BiConsumer; 9 | import java.util.function.BinaryOperator; 10 | import java.util.function.Function; 11 | import java.util.stream.Collector; 12 | 13 | /** 14 | * Provides useful reduction operations for java Stream API. 15 | * 16 | * @see java.util.stream.Collectors 17 | * @since 1.0 18 | */ 19 | public final class ImmutableCollectors { 20 | 21 | /** 22 | * Suppresses default constructor, ensuring non-instantiability. 23 | */ 24 | private ImmutableCollectors() { 25 | } 26 | 27 | /** 28 | * Provides collector to {@link ImmutableCollection}. 29 | * 30 | * @param collectionFactory function that accepts {@link Iterable} and returns {@link 31 | * ImmutableCollection} 32 | * @param the type of the content 33 | * @param the type of the return collection 34 | * @return collector 35 | * @throws NullPointerException if {@code collectionFactory} is null 36 | */ 37 | public static > Collector, C> toCollection( 38 | Function, C> collectionFactory) { 39 | Objects.requireNonNull(collectionFactory); 40 | return Collector.of( 41 | ArrayList::new, 42 | List::add, 43 | (res1, res2) -> { 44 | res1.addAll(res2); 45 | return res1; 46 | }, 47 | collectionFactory 48 | ); 49 | } 50 | 51 | /** 52 | * Provides collector to {@link ImmutableList}. 53 | * 54 | * @param the type of the content 55 | * @return collector 56 | * @see ImmutableCollectors#toCollection(Function) 57 | */ 58 | public static Collector, ImmutableList> toList() { 59 | return toCollection(ImmutableArrayList::new); 60 | } 61 | 62 | /** 63 | * Provides collector to {@link ImmutableSet}. 64 | * 65 | * @param the type of the content 66 | * @return collector 67 | * @see ImmutableCollectors#toCollection(Function) 68 | */ 69 | public static Collector> toSet() { 70 | return toCollection(ImmutableHashSet::new); 71 | } 72 | 73 | /** 74 | * Provides collector to {@link ImmutableMap}. 75 | * 76 | * @param keyMapper function that accepts value and produces map key 77 | * @param valueMapper function that accepts value and produces map value 78 | * @param the type of the content 79 | * @param the type of the map key 80 | * @param the type of the map value 81 | * @return collector 82 | * @throws IllegalStateException if keys were duplicated 83 | * @throws NullPointerException if {@code keyMapper} of {@code valueMapper} is null 84 | */ 85 | public static Collector, ImmutableMap> toMap( 86 | Function keyMapper, Function valueMapper) { 87 | Objects.requireNonNull(keyMapper); 88 | Objects.requireNonNull(valueMapper); 89 | return Collector.of( 90 | HashMap::new, 91 | uniqKeysMapAccumulator(keyMapper, valueMapper), 92 | uniqKeysMapMerger(), 93 | ImmutableHashMap::new 94 | ); 95 | } 96 | 97 | private static BiConsumer, T> uniqKeysMapAccumulator( 98 | Function keyMapper, Function valueMapper) { 99 | return (map, element) -> { 100 | final K k = keyMapper.apply(element); 101 | final V v = Objects.requireNonNull(valueMapper.apply(element)); 102 | final V u = map.putIfAbsent(k, v); 103 | if (u != null) { 104 | throw duplicateKeyException(k, u, v); 105 | } 106 | }; 107 | } 108 | 109 | private static > BinaryOperator uniqKeysMapMerger() { 110 | return (m1, m2) -> { 111 | for (final Map.Entry e : m2.entrySet()) { 112 | final K k = e.getKey(); 113 | final V v = Objects.requireNonNull(e.getValue()); 114 | final V u = m1.putIfAbsent(k, v); 115 | if (u != null) { 116 | throw duplicateKeyException(k, u, v); 117 | } 118 | } 119 | return m1; 120 | }; 121 | } 122 | 123 | private static IllegalStateException duplicateKeyException(Object k, Object u, Object v) { 124 | return new IllegalStateException( 125 | String.format("Duplicate key %s (attempted merging values %s and %s)", k, u, v) 126 | ); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableHashMap.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * An immutable implementation of java native {@link HashMap}. 8 | * 9 | * @param the type of the key 10 | * @param the type of the value 11 | * @see ImmutableMap 12 | * @see HashMap 13 | * @since 1.0 14 | */ 15 | public final class ImmutableHashMap implements ImmutableMap { 16 | 17 | private final Map hashMap; 18 | private final ImmutableSet keys; 19 | private final ImmutableList values; 20 | private final ImmutableSet> pairs; 21 | 22 | /** 23 | * Creates new {@linkplain ImmutableHashMap} instance from regular java {@linkplain Map}. The 24 | * entries are being copied from the source. 25 | * 26 | * @param map source map 27 | */ 28 | public ImmutableHashMap(Map map) { 29 | this.hashMap = new HashMap<>(map); 30 | this.keys = Immutable.setOf(hashMap.keySet()); 31 | this.values = Immutable.listOf(hashMap.values()); 32 | this.pairs = ImmutableMapUtils.toPairSet(hashMap.entrySet()); 33 | } 34 | 35 | @Override 36 | public int size() { 37 | return hashMap.size(); 38 | } 39 | 40 | @Override 41 | public boolean containsKey(Object key) { 42 | return hashMap.containsKey(key); 43 | } 44 | 45 | @Override 46 | public boolean containsValue(Object value) { 47 | return hashMap.containsValue(value); 48 | } 49 | 50 | @Override 51 | public V get(Object key) { 52 | return hashMap.get(key); 53 | } 54 | 55 | @Override 56 | public ImmutableSet keySet() { 57 | return keys; 58 | } 59 | 60 | @Override 61 | public ImmutableList values() { 62 | return values; 63 | } 64 | 65 | @Override 66 | public ImmutableSet> pairSet() { 67 | return pairs; 68 | } 69 | 70 | @Override 71 | public Map toMutableMap() { 72 | return new HashMap<>(hashMap); 73 | } 74 | 75 | @Override 76 | public boolean equals(Object o) { 77 | if (this == o) { 78 | return true; 79 | } 80 | if (o == null || getClass() != o.getClass()) { 81 | return false; 82 | } 83 | final ImmutableHashMap that = (ImmutableHashMap) o; 84 | return hashMap.equals(that.hashMap); 85 | } 86 | 87 | @Override 88 | public int hashCode() { 89 | return hashMap.hashCode(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableHashSet.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import com.kirekov.juu.collection.immutable.abstraction.AbstractImmutableSet; 4 | import java.util.HashSet; 5 | import java.util.Iterator; 6 | import java.util.Objects; 7 | import java.util.Set; 8 | import java.util.function.Function; 9 | import java.util.function.Predicate; 10 | import java.util.stream.Stream; 11 | 12 | /** 13 | * An immutable implementation of java native {@link HashSet}. 14 | * 15 | * @param the type of the content 16 | * @see ImmutableSet 17 | * @see Set 18 | * @see HashSet 19 | * @since 1.0 20 | */ 21 | public final class ImmutableHashSet extends AbstractImmutableSet { 22 | 23 | private final Set hashSet; 24 | 25 | /** 26 | * Constructor. 27 | * 28 | * @param iterable the source of elements 29 | */ 30 | public ImmutableHashSet(Iterable iterable) { 31 | super(); 32 | hashSet = new HashSet<>(); 33 | for (final T element : iterable) { 34 | hashSet.add(element); 35 | } 36 | } 37 | 38 | @Override 39 | public ImmutableSet concatWith(Iterable iterable) { 40 | Objects.requireNonNull(iterable, "iterable to concat with cannot be null"); 41 | final HashSet newHashSet = new HashSet<>(this.hashSet); 42 | for (final T t : iterable) { 43 | newHashSet.add(t); 44 | } 45 | return new ImmutableHashSet<>(newHashSet); 46 | } 47 | 48 | @Override 49 | public ImmutableSet map(Function mapper) { 50 | Objects.requireNonNull(mapper, "mapper function cannot be null"); 51 | final HashSet newHashSet = new HashSet<>(size()); 52 | for (final T t : this) { 53 | newHashSet.add(mapper.apply(t)); 54 | } 55 | return new ImmutableHashSet<>(newHashSet); 56 | } 57 | 58 | @Override 59 | public ImmutableSet flatMap(Function> mapper) { 60 | Objects.requireNonNull(mapper, "flat mapper function cannot be null"); 61 | final HashSet newHashSet = new HashSet<>(size()); 62 | for (final T t : this) { 63 | final ImmutableHashSet immutableHashSet = new ImmutableHashSet<>(mapper.apply(t)); 64 | newHashSet.addAll(immutableHashSet.hashSet); 65 | } 66 | return new ImmutableHashSet<>(newHashSet); 67 | } 68 | 69 | @Override 70 | public ImmutableSet filter(Predicate predicate) { 71 | Objects.requireNonNull(predicate, "filtering predicate cannot be null"); 72 | final HashSet newHashSet = new HashSet<>(size()); 73 | for (final T t : this) { 74 | if (predicate.test(t)) { 75 | newHashSet.add(t); 76 | } 77 | } 78 | return new ImmutableHashSet<>(newHashSet); 79 | } 80 | 81 | @Override 82 | public int size() { 83 | return hashSet.size(); 84 | } 85 | 86 | @Override 87 | public boolean contains(Object element) { 88 | return hashSet.contains(element); 89 | } 90 | 91 | @Override 92 | public ImmutableList toList() { 93 | return Immutable.listOf(this.hashSet); 94 | } 95 | 96 | @Override 97 | public ImmutableSet toSet() { 98 | return this; 99 | } 100 | 101 | @Override 102 | public Stream parallelStream() { 103 | return this.hashSet.parallelStream(); 104 | } 105 | 106 | @Override 107 | public Stream stream() { 108 | return this.hashSet.stream(); 109 | } 110 | 111 | @Override 112 | public Iterator iterator() { 113 | return new UnmodifiableIterator<>(hashSet.iterator()); 114 | } 115 | 116 | @Override 117 | public boolean equals(Object o) { 118 | if (this == o) { 119 | return true; 120 | } 121 | if (o == null || getClass() != o.getClass()) { 122 | return false; 123 | } 124 | final ImmutableHashSet that = (ImmutableHashSet) o; 125 | return hashSet.equals(that.hashSet); 126 | } 127 | 128 | @Override 129 | public int hashCode() { 130 | return hashSet.hashCode(); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableList.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import java.util.Comparator; 4 | import java.util.List; 5 | import java.util.OptionalInt; 6 | import java.util.function.BiConsumer; 7 | import java.util.function.BiFunction; 8 | import java.util.function.BiPredicate; 9 | import java.util.function.Consumer; 10 | import java.util.function.Function; 11 | import java.util.function.Predicate; 12 | 13 | /** 14 | * Defines an immutable list. Unlike native {@link List} this interface does not have any methods 15 | * that can mutate its content. So it can be safely injected to any methods or objects. 16 | * 17 | * @param the type of the object, that list contains 18 | * @see ImmutableCollection 19 | * @see List 20 | * @since 1.0 21 | */ 22 | public interface ImmutableList extends ImmutableCollection { 23 | 24 | /** 25 | * Returns the element by its index. Supports negative indices in "python-way". 26 | * 27 | *
{@code
 28 |    * ImmutableList list = getList(); // [1, 2, 3, 4, 5]
 29 |    * list.get(1);     // 2
 30 |    * list.get(3);     // 4
 31 |    * list.get(-1);    // 5
 32 |    * list.get(-3);    // 3
 33 |    * list.get(-5);    // 1;
 34 |    * list.get(0);     // 1;
 35 |    * }
36 | * 37 | *

If {@code index} is less than zero, then {@code list.get(index)} equals to {@code 38 | * list.get(list.size() - Math.abs(index))}

39 | * 40 | * @param index index of the element to return 41 | * @return the element at the specified position 42 | * @throws IndexOutOfBoundsException if the index is bigger than or equal to {@code list.size()} 43 | * or {@code list.size() - Math.abs(index)} is less than zero if 44 | * index is negative 45 | */ 46 | T get(int index); 47 | 48 | /** 49 | * Returns the first index of the specified element or {@link OptionalInt#empty()}, if element is 50 | * not found. 51 | * 52 | * @param element the searching element 53 | * @return the first index of the {@link OptionalInt#empty()} 54 | */ 55 | OptionalInt indexOf(T element); 56 | 57 | /** 58 | * Returns the last index of the specified element or {@link OptionalInt#empty()}, if element is 59 | * not found. 60 | * 61 | * @param element the searching element 62 | * @return the first index of the element or {@link OptionalInt#empty()} 63 | */ 64 | OptionalInt lastIndexOf(T element); 65 | 66 | /** 67 | * Returns sublist starting from {@code fromIndex}. Supports negative indices. 68 | * 69 | *
{@code
 70 |    * ImmutableList list = getList(); // [1, 2, 3, 4, 5, 6]
 71 |    * list.slice(1);                           // [2, 3, 4, 5, 6]
 72 |    * list.slice(-2);                          // [5, 6]
 73 |    * list.slice(-4);                          // [3, 4, 5, 6]
 74 |    * }
75 | * 76 | * @param fromIndex start index (inclusively) 77 | * @return sublist 78 | * @throws IndexOutOfBoundsException if {@code fromIndex} is out of bounds 79 | * @see ImmutableList#slice(int, int, int) 80 | */ 81 | ImmutableList slice(int fromIndex); 82 | 83 | /** 84 | * Returns sublist from {@code fromIndex} to {@code toIndex} exclusively. Supports negative 85 | * indices. If {@code fromIndex} is placed before {@code toIndex}, the result list order is 86 | * descending. 87 | * 88 | *
{@code
 89 |    * ImmutableList list = getList(); // [1, 2, 3, 4, 5, 6]
 90 |    * list.slice(1, 3);                        // [2, 3]
 91 |    * list.slice(-3, -1);                      // [4, 5]
 92 |    * list.slice(5, 1);                        // [6, 5, 4, 3]
 93 |    * list.slice(-2, -6);                      // [5, 4, 3, 2]
 94 |    * }
95 | * 96 | * @param fromIndex start index (inclusively) 97 | * @param toIndex end index (exclusively) 98 | * @return sublist 99 | * @throws IndexOutOfBoundsException if {@code fromIndex} is out of bounds 100 | * @see ImmutableList#slice(int, int, int) 101 | */ 102 | ImmutableList slice(int fromIndex, int toIndex); 103 | 104 | /** 105 | * Returns sublist from {@code fromIndex} inclusively to {@code toIndex} exclusively with the 106 | * given step size. Supports negative indices. If {@code stepSize} is negative, then the list is 107 | * to be traversed backwards. 108 | * 109 | *
{@code
110 |    * ImmutableList list = getList(); // [1, 2, 3, 4, 5, 6]
111 |    * list.slice(0, 3, 1);                     // [1, 2, 3]
112 |    * list.slice(-1, 2, -1);                   // [6, 5, 4]
113 |    * list.slice(0, 6, 2);                     // [0, 3, 5]
114 |    * }
115 | * 116 | * @param fromIndex start index (inclusively) 117 | * @param toIndex end index (exclusively) 118 | * @param stepSize the size of the step traversing 119 | * @return result sublist 120 | * @throws IndexOutOfBoundsException if {@code fromIndex} is out of bounds 121 | * @throws IllegalArgumentException if {@code stepSize} is zero 122 | * @see ImmutableList#get(int) 123 | */ 124 | ImmutableList slice(int fromIndex, int toIndex, int stepSize); 125 | 126 | /** 127 | * Returns new list with elements traversed by step step size. If {@code stepSize} is negative, 128 | * the list ought to be traversed in reversed order. 129 | * 130 | *
{@code
131 |    * ImmutableList list = getList(); // [1, 2, 3, 4, 5, 6]
132 |    * list.step(2);                            // [1, 3, 5]
133 |    * list.step(-3);                           // [6, 3]
134 |    * }
135 | * 136 | * @param stepSize the size of step traversing 137 | * @return stepped list 138 | * @throws IllegalArgumentException if {@code stepSize} is zero 139 | * @see ImmutableList#step(int, int) 140 | */ 141 | default ImmutableList step(int stepSize) { 142 | if (stepSize == 0) { 143 | throw new IllegalArgumentException("Step size cannot be 0"); 144 | } 145 | if (stepSize > 0) { 146 | return step(0, stepSize); 147 | } else { 148 | return step(-1, stepSize); 149 | } 150 | } 151 | 152 | /** 153 | * Returns sublist traversed with given step starting from the given index. 154 | * 155 | *
{@code
156 |    * ImmutableList list = createList(); // [1, 2, 3, 4, 5, 6, 7]
157 |    * ImmutableList newOne = list.step(0, 2) // [1, 3, 5, 7]
158 |    * ImmutableList newTwo = list.step(1, 3) // [2, 5]
159 |    * }
160 | * 161 | *

Step size might be negative as well. It means backwards traversing.

162 | * 163 | *
{@code
164 |    * ImmutableList list = createList(); [1, 2, 3, 4, 5, 6, 7]
165 |    * ImmutableList newOne = list.step(-1, -2) // [7, 5, 3, 1]
166 |    * }
167 | * 168 | * @param fromIndex start index (might be negative) 169 | * @param stepSize the size of the step traversing 170 | * @return stepped list 171 | * @throws IndexOutOfBoundsException if fromIndex is out of bounds 172 | * @throws IllegalArgumentException if stepSize is zero 173 | * @see ImmutableList#get(int) 174 | */ 175 | ImmutableList step(int fromIndex, int stepSize); 176 | 177 | /** 178 | * Zips current list with provided list and returns pairs, where key contains the element from the 179 | * current list and value contains the element from the provided list. 180 | * 181 | *
{@code
182 |    * ImmutableList people = getPeople();
183 |    * ImmutableList jobs = getJobs();
184 |    *
185 |    * ImmutableList> zipped =
186 |    *      people.zipWith(jobs);
187 |    * }
188 | *

If lists have different length, then at some position the key or the value will become 189 | * null.

190 | * 191 | * @param list provided list 192 | * @param the type of the content of the provided list 193 | * @return new list 194 | * @throws NullPointerException if {@code list} is null 195 | */ 196 | ImmutableList> zipWith(ImmutableList list); 197 | 198 | /** 199 | * Returns two adjacent elements of the list as pairs. If list has length less then two, returns 200 | * empty list. 201 | * 202 | * @return new list 203 | */ 204 | ImmutableList> zipWithNext(); 205 | 206 | /** 207 | * {@inheritDoc} 208 | */ 209 | @Override 210 | ImmutableList concatWith(Iterable iterable); 211 | 212 | /** 213 | * {@inheritDoc} 214 | */ 215 | @Override 216 | ImmutableList map(Function mapper); 217 | 218 | /** 219 | * {@inheritDoc} 220 | */ 221 | @Override 222 | ImmutableList flatMap(Function> mapper); 223 | 224 | /** 225 | * {@inheritDoc} 226 | */ 227 | @Override 228 | ImmutableList filter(Predicate predicate); 229 | 230 | /** 231 | * Maps the content of the list from one type to another. Mapper accepts current index and current 232 | * value. 233 | * 234 | * @param mapper mapping function 235 | * @param the result type 236 | * @return new list 237 | * @throws NullPointerException if {@code mapper} is null 238 | */ 239 | ImmutableList mapIndexed(BiFunction mapper); 240 | 241 | /** 242 | * This method has exactly the same behaviour as {@link ImmutableList#flatMap(Function)}, but 243 | * mapper accepts two arguments. The first is the current index and the second is the current 244 | * value. 245 | * 246 | * @param mapper mapping function, that returns {@link Iterable} 247 | * @param the type of the return list 248 | * @return new list 249 | * @throws NullPointerException if {@code mapper} is null 250 | * @see ImmutableList#flatMap(Function) 251 | */ 252 | ImmutableList flatMapIndexed(BiFunction> mapper); 253 | 254 | /** 255 | * Returns new list which values match provided predicate. 256 | * 257 | * @param predicate predicate to apply to each element to determine if it should be included. The 258 | * first argument is the current index and the second one is the current value 259 | * @return new list 260 | * @throws NullPointerException if {@code predicate} is null 261 | */ 262 | ImmutableList filterIndexed(BiPredicate predicate); 263 | 264 | /** 265 | * Traverses each element like {@link Iterable#forEach(Consumer)}, but consumer accepts two 266 | * arguments. The first arg is the current index and the second one is the current value. 267 | * 268 | * @param action consumer 269 | * @throws NullPointerException if {@code action} is null 270 | */ 271 | void forEachIndexed(BiConsumer action); 272 | 273 | /** 274 | * Sorts the list and returns the new one. 275 | * 276 | * @param comparator comparator, which defines the sort order 277 | * @return new sorted list 278 | * @throws NullPointerException if {@code comparator} is null 279 | */ 280 | ImmutableList sorted(Comparator comparator); 281 | 282 | /** 283 | * Returns the first {@code size} elements.
If {@code size} is bigger than the size of the 284 | * list, then returns the list itself.
If {@code size} is zero, then returns an empty list. 285 | * 286 | * @param size the max size of new list, starting from the beginning 287 | * @return new list 288 | * @throws IllegalArgumentException if {@code size} is less than zero 289 | */ 290 | ImmutableList limit(int size); 291 | 292 | /** 293 | * Skips the first {@code size} elements and returns remaining as a new list.
if {@code size} 294 | * is bigger than the size of the list, then returns an empty list.
if {@code size} if zero, 295 | * then return the list itself. 296 | * 297 | * @param size the count of the elements that must be skipped from the beginning 298 | * @return new list 299 | * @throws IllegalArgumentException if {@code size} is less than zero 300 | */ 301 | ImmutableList skip(int size); 302 | 303 | /** 304 | * Reverses the list and returns its copy. 305 | * 306 | * @return reversed list 307 | */ 308 | default ImmutableList reversed() { 309 | return step(-1); 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableMap.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import com.kirekov.juu.lambda.TriFunction; 4 | import java.util.Map; 5 | import java.util.Objects; 6 | import java.util.function.BiConsumer; 7 | 8 | /** 9 | * Defines an immutable map. Unlike native {@link Map} this interface does not have any methods that 10 | * can mutate its content. So it can be safely injected to any methods or objects. 11 | * 12 | *

It is strongly recommended to instantiate this map with only immutable keys and values in 13 | * order not to face with some unexpected mutations.

14 | * 15 | * @param the type of the key 16 | * @param the type of the value 17 | * @see Map 18 | * @since 1.0 19 | */ 20 | public interface ImmutableMap { 21 | 22 | /** 23 | * Gets size of the map. 24 | * 25 | * @return size of the map (keys count) 26 | */ 27 | int size(); 28 | 29 | /** 30 | * Whether the map is empty. 31 | * 32 | * @return true if size is zero, otherwise false 33 | */ 34 | default boolean isEmpty() { 35 | return size() == 0; 36 | } 37 | 38 | /** 39 | * Whether the map is NOT empty. 40 | * 41 | * @return true if size is not zero, otherwise false 42 | */ 43 | default boolean isNotEmpty() { 44 | return !isEmpty(); 45 | } 46 | 47 | /** 48 | * Whether the map contains the given {@code key}. 49 | * 50 | * @param key the key whose presence is to be tested 51 | * @return true if map contains key, otherwise false 52 | */ 53 | boolean containsKey(Object key); 54 | 55 | /** 56 | * Whether the map NOT contains the given {@code key}. 57 | * 58 | * @param key the key whose presence is to be tested 59 | * @return true if map does not contain key, otherwise false 60 | */ 61 | default boolean notContainsKey(Object key) { 62 | return !containsKey(key); 63 | } 64 | 65 | /** 66 | * Whether the map contains the given {@code value}. 67 | * 68 | * @param value the value whose presence is to be tested 69 | * @return true if map contains value, otherwise false 70 | */ 71 | boolean containsValue(Object value); 72 | 73 | /** 74 | * Whether the map NOT contains the given {@code value}. 75 | * 76 | * @param value the value whose presence is to be tested 77 | * @return true if map does not contain value, otherwise false 78 | */ 79 | default boolean notContainsValue(Object value) { 80 | return !containsValue(value); 81 | } 82 | 83 | /** 84 | * Whether the map contains the given {@code pair}. 85 | * 86 | * @param pair the pair whose presence is to be tested 87 | * @return true if map contains pair, otherwise false 88 | * @throws NullPointerException if {@code pair} is null 89 | */ 90 | default boolean containsPair(Pair pair) { 91 | Objects.requireNonNull(pair); 92 | return pairSet().contains(pair); 93 | } 94 | 95 | /** 96 | * Whether the map NOT contains the given {@code pair}. 97 | * 98 | * @param pair the value whose presence is to be tested 99 | * @return true if map does not contain pair, otherwise false 100 | * @throws NullPointerException if {@code pair} is null 101 | */ 102 | default boolean notContainsPair(Pair pair) { 103 | Objects.requireNonNull(pair); 104 | return !containsPair(pair); 105 | } 106 | 107 | /** 108 | * Concatenates the current map with the given one. In case of occurring the same keys in two maps 109 | * the value from the given map will be added to the final result. 110 | * 111 | * @param map the map whose pairs will be added to the current one 112 | * @return new map with merged pairs 113 | * @throws NullPointerException if {@code map} is null 114 | * @see ImmutableMap#concatWith(ImmutableMap, TriFunction) 115 | */ 116 | default ImmutableMap concatWithOverride(ImmutableMap map) { 117 | return concatWith(map, (key, oldVal, newVal) -> newVal); 118 | } 119 | 120 | /** 121 | * Concatenates the current map with the given one. In case of occurring the same keys in two maps 122 | * the value from the current map will be added to final result. 123 | * 124 | * @param map the map whose pairs will be added to the current one 125 | * @return new map with merged pairs 126 | * @throws NullPointerException if {@code map} is null 127 | * @see ImmutableMap#concatWith(ImmutableMap, TriFunction) 128 | */ 129 | default ImmutableMap concatWithoutOverride(ImmutableMap map) { 130 | return concatWith(map, (key, oldVal, newVal) -> oldVal); 131 | } 132 | 133 | /** 134 | * Adds all pairs of keys and values from current map and the given map to the new one and returns 135 | * new instance. 136 | * 137 | *

If these two maps have any equal keys, {@code overrideBehaviour} function will be called to 138 | * resolve the conflict.

139 | * 140 | * @param mapToConcatWith the map whose pairs will be added to the current one 141 | * @param overrideBehaviour function is called every time when two same keys in two maps are 142 | * found. The first param is the key. The second param is the value from 143 | * the current map. The third param is the value from the given map. 144 | * Function returns the value that will be added to the final result. 145 | * @return new map with merged pairs 146 | * @throws NullPointerException if {@code map} is null or {@code overrideBehaviour} is null 147 | */ 148 | default ImmutableMap concatWith(ImmutableMap mapToConcatWith, 149 | TriFunction overrideBehaviour) { 150 | Map resultMutableMap = toMutableMap(); 151 | mapToConcatWith.forEach( 152 | (k, v) -> { 153 | if (this.containsKey(k)) { 154 | resultMutableMap.put(k, overrideBehaviour.apply(k, this.get(k), v)); 155 | } else { 156 | resultMutableMap.put(k, v); 157 | } 158 | }); 159 | return new ImmutableHashMap<>(resultMutableMap); 160 | } 161 | 162 | /** 163 | * Gets the value from the map by {@code key}. 164 | * 165 | * @param key the key whose associated value is to be found 166 | * @return the value that associates with the key if it is present, otherwise null 167 | */ 168 | V get(Object key); 169 | 170 | /** 171 | * Gets keys from the map as {@linkplain ImmutableSet}. 172 | * 173 | * @return immutable set of keys, that map contains 174 | */ 175 | ImmutableSet keySet(); 176 | 177 | /** 178 | * Gets values from the map as {@linkplain ImmutableList}. 179 | * 180 | * @return immutable list of values, that map contains 181 | */ 182 | ImmutableList values(); 183 | 184 | /** 185 | * Gets pairs from the map as {@linkplain ImmutableSet}. 186 | * 187 | * @return immutable set of (key -> value) associations 188 | */ 189 | ImmutableSet> pairSet(); 190 | 191 | /** 192 | * Converts this immutable map to mutable one. Creates new object, so its mutations does not 193 | * affect the mutable one. 194 | * 195 | * @return new mutable map 196 | */ 197 | Map toMutableMap(); 198 | 199 | /** 200 | * Overrides method from {@link Object#equals(Object)}. Must be implemented. 201 | * 202 | * @param o the reference object with which to compare 203 | * @return true if two objects are equal and false otherwise 204 | */ 205 | @Override 206 | boolean equals(Object o); 207 | 208 | /** 209 | * Overrides method from {@link Object#hashCode()}. Must be implemented. 210 | * 211 | * @return a hash code value for this object 212 | */ 213 | @Override 214 | int hashCode(); 215 | 216 | /** 217 | * If value contains the key, returns associated value, otherwise returns {@code defaultValue}. 218 | * 219 | * @param key the key whose associated value is to be found 220 | * @param defaultValue the value that returns in case of key absence 221 | * @return the value or the default one 222 | */ 223 | default V getOrDefault(Object key, V defaultValue) { 224 | return containsKey(key) ? get(key) : defaultValue; 225 | } 226 | 227 | /** 228 | * Traverses all pairs (key -> value) that map contains. 229 | * 230 | * @param action the procedure that invokes on each iteration 231 | */ 232 | default void forEach(BiConsumer action) { 233 | Objects.requireNonNull(action); 234 | for (Pair pair : pairSet()) { 235 | K k = pair.getKey(); 236 | V v = pair.getValue(); 237 | action.accept(k, v); 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableMapUtils.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import java.util.Map; 4 | import java.util.Objects; 5 | import java.util.Set; 6 | import java.util.stream.Collectors; 7 | 8 | class ImmutableMapUtils { 9 | 10 | private ImmutableMapUtils() { 11 | } 12 | 13 | static ImmutableSet> toPairSet(Set> entrySet) { 14 | Objects.requireNonNull(entrySet); 15 | return Immutable.setOf(entrySet.stream() 16 | .map(e -> Pair.of(e.getKey(), e.getValue())) 17 | .collect(Collectors.toList())); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableNavigableMap.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import java.util.Collections; 4 | import java.util.Comparator; 5 | import java.util.NavigableMap; 6 | import java.util.NavigableSet; 7 | import java.util.Optional; 8 | 9 | /** 10 | * Defines an immutable navigable map. Unlike native {@linkplain NavigableMap} this interface does 11 | * not provide any methods that can mutate its state. So, it safe to use it in concurrent 12 | * environments. 13 | * 14 | *

It is strongly recommended to instantiate this map with only immutable keys and values in 15 | * order not to face with some unexpected mutations.

16 | * 17 | * @param the type of the key 18 | * @param the type of value 19 | * @see NavigableMap 20 | */ 21 | public interface ImmutableNavigableMap extends ImmutableSortedMap { 22 | 23 | /** 24 | * Returns a key-value mapping associated with the greatest key strictly less than the given key, 25 | * or {@link Optional#empty()} if there is no such key. 26 | * 27 | * @param key the key 28 | * @return an entry with the greatest key less than {@code key}, or {@link Optional#empty()} if 29 | * there is no such key 30 | */ 31 | Optional> lowerPair(K key); 32 | 33 | /** 34 | * Returns the greatest key strictly less than the given key, or {@link Optional#empty()} if there 35 | * is no such key. 36 | * 37 | * @param key the key 38 | * @return the greatest key less than {@code key}, or {@link Optional#empty()} if there is no such 39 | * key 40 | */ 41 | Optional lowerKey(K key); 42 | 43 | /** 44 | * Returns a key-value mapping associated with the greatest key less than or equal to the given 45 | * key, or {@link Optional#empty()} if there is no such key. 46 | * 47 | * @param key the key 48 | * @return an entry with the greatest key less than or equal to {@code key}, or {@link 49 | * Optional#empty()} if there is no such key 50 | */ 51 | Optional> floorPair(K key); 52 | 53 | /** 54 | * Returns the greatest key less than or equal to the given key, or {@link Optional#empty()} if 55 | * there is no such key. 56 | * 57 | * @param key the key 58 | * @return the greatest key less than or equal to {@code key}, or {@link Optional#empty()} if 59 | * there is no such key 60 | */ 61 | Optional floorKey(K key); 62 | 63 | /** 64 | * Returns a key-value mapping associated with the least key greater than or equal to the given 65 | * key, or {@link Optional#empty()} if there is no such key. 66 | * 67 | * @param key the key 68 | * @return an entry with the least key greater than or equal to {@code key}, or {@link 69 | * Optional#empty()} if there is no such key 70 | */ 71 | Optional> ceilingPair(K key); 72 | 73 | /** 74 | * Returns the least key greater than or equal to the given key, or {@link Optional#empty()} if 75 | * there is no such key. 76 | * 77 | * @param key the key 78 | * @return the least key greater than or equal to {@code key}, or {@link Optional#empty()} if 79 | * there is no such key 80 | */ 81 | Optional ceilingKey(K key); 82 | 83 | /** 84 | * Returns a key-value mapping associated with the least key strictly greater than the given key, 85 | * or {@link Optional#empty()} if there is no such key. 86 | * 87 | * @param key the key 88 | * @return an entry with the least key greater than {@code key}, or {@link Optional#empty()} if 89 | * there is no such key 90 | */ 91 | Optional> higherPair(K key); 92 | 93 | /** 94 | * Returns the least key strictly greater than the given key, or {@link Optional#empty()} if there 95 | * is no such key. 96 | * 97 | * @param key the key 98 | * @return the least key greater than {@code key}, or {@link Optional#empty()} if there is no such 99 | * key 100 | */ 101 | Optional higherKey(K key); 102 | 103 | /** 104 | * Returns a key-value mapping associated with the least key in this map, or {@link 105 | * Optional#empty()} if the map is empty. 106 | * 107 | * @return an entry with the least key, or {@link Optional#empty()} if this map is empty 108 | */ 109 | Optional> firstPair(); 110 | 111 | /** 112 | * Returns a key-value mapping associated with the greatest key in this map, or {@link 113 | * Optional#empty()} if the map is empty. 114 | * 115 | * @return an entry with the greatest key, or {@link Optional#empty()} if this map is empty 116 | */ 117 | Optional> lastPair(); 118 | 119 | /** 120 | * Returns a reverse order view of the mappings contained in this map. 121 | * 122 | *

The returned map has an ordering equivalent to 123 | * {@link Collections#reverseOrder(Comparator) Collections.reverseOrder}{@code (comparator())}. 124 | * The expression {@code m.reversedOrderMap().reversedOrderMap()} returns a view of {@code m} 125 | * essentially equivalent to {@code m}. 126 | * 127 | * @return a reverse order view of this map 128 | */ 129 | ImmutableNavigableMap reversedOrderMap(); 130 | 131 | /** 132 | * Returns a {@link NavigableSet} view of the keys contained in this map. The set's iterator 133 | * returns the keys in ascending order. 134 | * 135 | * @return a navigable set view of the keys in this map 136 | */ 137 | ImmutableNavigableSet navigableKeySet(); 138 | 139 | /** 140 | * Returns a reverse order {@link NavigableSet} view of the keys contained in this map. The set's 141 | * iterator returns the keys in descending order. 142 | * 143 | * @return a reverse order navigable set view of the keys in this map 144 | */ 145 | ImmutableNavigableSet reversedOrderKeySet(); 146 | 147 | /** 148 | * Returns a view of the portion of this map whose keys range from {@code fromKey} to {@code 149 | * toKey}. If {@code fromKey} and {@code toKey} are equal, the returned map is empty unless 150 | * {@code fromInclusive} and {@code toInclusive} are both true. 151 | * 152 | * @param fromKey low endpoint of the keys in the returned map 153 | * @param fromInclusive {@code true} if the low endpoint is to be included in the returned view 154 | * @param toKey high endpoint of the keys in the returned map 155 | * @param toInclusive {@code true} if the high endpoint is to be included in the returned view 156 | * @return a view of the portion of this map whose keys range from {@code fromKey} to {@code 157 | * toKey} 158 | */ 159 | ImmutableNavigableMap subMap(K fromKey, boolean fromInclusive, 160 | K toKey, boolean toInclusive); 161 | 162 | /** 163 | * Returns a view of the portion of this map whose keys are less than (or equal to, if {@code 164 | * inclusive} is true) {@code toKey}. 165 | * 166 | * @param toKey high endpoint of the keys in the returned map 167 | * @param inclusive {@code true} if the high endpoint is to be included in the returned view 168 | * @return a view of the portion of this map whose keys are less than (or equal to, if {@code 169 | * inclusive} is true) {@code toKey} 170 | */ 171 | ImmutableNavigableMap headMap(K toKey, boolean inclusive); 172 | 173 | /** 174 | * Returns a view of the portion of this map whose keys are greater than (or equal to, if {@code 175 | * inclusive} is true) {@code fromKey}. 176 | * 177 | * @param fromKey low endpoint of the keys in the returned map 178 | * @param inclusive {@code true} if the low endpoint is to be included in the returned view 179 | * @return a view of the portion of this map whose keys are greater than (or equal to, if {@code 180 | * inclusive} is true) {@code fromKey} 181 | */ 182 | ImmutableNavigableMap tailMap(K fromKey, boolean inclusive); 183 | 184 | /** 185 | * Converts immutable navigable map to mutable map. 186 | * 187 | * @return new mutable navigable map 188 | */ 189 | NavigableMap toMutableNavigableMap(); 190 | } 191 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableNavigableSet.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import java.util.Collections; 4 | import java.util.Comparator; 5 | import java.util.Iterator; 6 | import java.util.NavigableSet; 7 | import java.util.Optional; 8 | 9 | /** 10 | * Defines an immutable navigable set. Unlike native {@linkplain NavigableSet} this interface does 11 | * not provide any methods that can mutate its state. So, it is safe to use it in concurrent 12 | * environments. 13 | * 14 | *

It is strongly recommended to instantiate this map with only immutable keys and values in 15 | * order not to face with some unexpected mutations.

16 | * 17 | * @param the type of the value 18 | * @see NavigableSet 19 | */ 20 | public interface ImmutableNavigableSet extends ImmutableSortedSet { 21 | 22 | /** 23 | * Returns the greatest element in this set strictly less than the given element, or {@code null} 24 | * if there is no such element. 25 | * 26 | * @param t the value to match 27 | * @return the greatest element less than {@code t}, or {@code null} if there is no such element 28 | */ 29 | Optional lower(T t); 30 | 31 | /** 32 | * Returns the greatest element in this set less than or equal to the given element, or {@code 33 | * null} if there is no such element. 34 | * 35 | * @param t the value to match 36 | * @return the greatest element less than or equal to {@code t}, or {@code null} if there is no 37 | * such element 38 | */ 39 | Optional floor(T t); 40 | 41 | /** 42 | * Returns the least element in this set greater than or equal to the given element, or {@code 43 | * null} if there is no such element. 44 | * 45 | * @param t the value to match 46 | * @return the least element greater than or equal to {@code t}, or {@code null} if there is no 47 | * such element 48 | */ 49 | Optional ceiling(T t); 50 | 51 | /** 52 | * Returns the least element in this set strictly greater than the given element, or {@code null} 53 | * if there is no such element. 54 | * 55 | * @param t the value to match 56 | * @return the least element greater than {@code t}, or {@code null} if there is no such element 57 | */ 58 | Optional higher(T t); 59 | 60 | /** 61 | * Returns a reverse order view of the elements contained in this set. 62 | * 63 | *

The returned set has an ordering equivalent to {@link Collections#reverseOrder(Comparator) 64 | * Collections.reverseOrder}{@code (comparator())}. The expression {@code 65 | * s.reversedOrderSet().reversedOrderSet()} returns a view of {@code s} essentially equivalent to 66 | * {@code s}. 67 | * 68 | * @return a reverse order view of this set 69 | */ 70 | ImmutableNavigableSet reversedOrderSet(); 71 | 72 | /** 73 | * Returns an iterator over the elements in this set, in descending order. Equivalent in effect to 74 | * {@code reversedOrderSet().iterator()}. 75 | * 76 | * @return an iterator over the elements in this set, in descending order 77 | */ 78 | Iterator reversedOrderIterator(); 79 | 80 | /** 81 | * Returns a view of the portion of this set whose elements range from {@code fromElement} to 82 | * {@code toElement}. If {@code fromElement} and {@code toElement} are equal, the returned set is 83 | * empty unless {@code fromInclusive} and {@code toInclusive} are both true. 84 | * 85 | * @param fromElement low endpoint of the returned set 86 | * @param fromInclusive {@code true} if the low endpoint is to be included in the returned view 87 | * @param toElement high endpoint of the returned set 88 | * @param toInclusive {@code true} if the high endpoint is to be included in the returned view 89 | * @return a view of the portion of this set whose elements range from {@code fromElement}, 90 | * inclusive, to {@code toElement}, exclusive 91 | */ 92 | ImmutableNavigableSet subSet( 93 | T fromElement, boolean fromInclusive, T toElement, boolean toInclusive); 94 | 95 | /** 96 | * Returns a view of the portion of this set whose elements are less than (or equal to, if {@code 97 | * inclusive} is true) {@code toElement}. 98 | * 99 | * @param toElement high endpoint of the returned set 100 | * @param inclusive {@code true} if the high endpoint is to be included in the returned view 101 | * @return a view of the portion of this set whose elements are less than (or equal to, if {@code 102 | * inclusive} is true) {@code toElement} 103 | */ 104 | ImmutableNavigableSet headSet(T toElement, boolean inclusive); 105 | 106 | /** 107 | * Returns a view of the portion of this set whose elements are greater than (or equal to, if 108 | * {@code inclusive} is true) {@code fromElement}. 109 | * 110 | * @param fromElement low endpoint of the returned set 111 | * @param inclusive {@code true} if the low endpoint is to be included in the returned view 112 | * @return a view of the portion of this set whose elements are greater than or equal to {@code 113 | * fromElement} 114 | */ 115 | ImmutableNavigableSet tailSet(T fromElement, boolean inclusive); 116 | 117 | /** 118 | * Converts this immutable navigable set to mutable one. Creates new object, so its mutations does 119 | * not affect the immutable one. 120 | * 121 | * @return new mutable navigable set 122 | */ 123 | NavigableSet toMutableNavigableSet(); 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableSet.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import java.util.function.Function; 4 | import java.util.function.Predicate; 5 | 6 | /** 7 | * Defines an immutable set. Unlike native {@link java.util.Set} this interface does not have any 8 | * methods that can mutate its content. So it can be safely injected to any methods or objects. 9 | * 10 | * @param the type of the object, that set contains 11 | * @see ImmutableCollection 12 | * @see java.util.Set 13 | * @since 1.0 14 | */ 15 | public interface ImmutableSet extends ImmutableCollection { 16 | 17 | /** 18 | * {@inheritDoc} 19 | */ 20 | @Override 21 | ImmutableSet concatWith(Iterable iterable); 22 | 23 | /** 24 | * {@inheritDoc} 25 | */ 26 | @Override 27 | ImmutableSet map(Function mapper); 28 | 29 | /** 30 | * {@inheritDoc} 31 | */ 32 | @Override 33 | ImmutableSet flatMap(Function> mapper); 34 | 35 | /** 36 | * {@inheritDoc} 37 | */ 38 | @Override 39 | ImmutableSet filter(Predicate predicate); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableSortedMap.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import java.util.Comparator; 4 | import java.util.Optional; 5 | import java.util.SortedMap; 6 | 7 | /** 8 | * Defines immutable sorted map. Unlike native {@linkplain SortedMap} this interface does not 9 | * provide any methods that can mutate its state. So, it is safe to use it in concurrent 10 | * environments. 11 | * 12 | * @param the type of the key 13 | * @param the type of the value 14 | * @see SortedMap 15 | * @since 1.1 16 | */ 17 | public interface ImmutableSortedMap extends ImmutableMap { 18 | 19 | /** 20 | * Returns the comparator used to order the keys in this map, or {@code null} if this map uses the 21 | * {@linkplain Comparable natural ordering} of its keys. 22 | * 23 | * @return the comparator used to order the keys in this map, or {@code null} if this map uses the 24 | * natural ordering of its keys 25 | */ 26 | Comparator comparator(); 27 | 28 | /** 29 | * Returns a view of the portion of this map whose keys range from {@code fromKey}, inclusive, to 30 | * {@code toKey}, exclusive. If {@code fromKey} and {@code toKey} are equal, the returned map is 31 | * empty. The returned map is backed by this map, so changes in the returned map are reflected in 32 | * this map, and vice-versa. The returned map supports all optional map operations that this map 33 | * supports. 34 | * 35 | * @param fromKey low endpoint (inclusive) of the keys in the returned map 36 | * @param toKey high endpoint (exclusive) of the keys in the returned map 37 | * @return a view of the portion of this map whose keys range from {@code fromKey}, inclusive, to 38 | * {@code toKey}, exclusive 39 | */ 40 | ImmutableSortedMap subMap(K fromKey, K toKey); 41 | 42 | /** 43 | * Returns a view of the portion of this map whose keys are strictly less than {@code toKey}. The 44 | * returned map is backed by this map, so changes in the returned map are reflected in this map, 45 | * and vice-versa. The returned map supports all optional map operations that this map supports. 46 | * 47 | * @param toKey high endpoint (exclusive) of the keys in the returned map 48 | * @return a view of the portion of this map whose keys are strictly less than {@code toKey} 49 | */ 50 | ImmutableSortedMap headMap(K toKey); 51 | 52 | /** 53 | * Returns a view of the portion of this map whose keys are greater than or equal to {@code 54 | * fromKey}. The returned map is backed by this map, so changes in the returned map are reflected 55 | * in this map, and vice-versa. The returned map supports all optional map operations that this 56 | * map supports. 57 | * 58 | * @param fromKey low endpoint (inclusive) of the keys in the returned map 59 | * @return a view of the portion of this map whose keys are greater than or equal to {@code 60 | * fromKey} 61 | */ 62 | ImmutableSortedMap tailMap(K fromKey); 63 | 64 | /** 65 | * Returns the first (lowest) key currently in this map. 66 | * 67 | * @return the first (lowest) key currently in this map 68 | */ 69 | Optional firstKey(); 70 | 71 | /** 72 | * Returns the last (highest) key currently in this map. 73 | * 74 | * @return the last (highest) key currently in this map 75 | */ 76 | Optional lastKey(); 77 | 78 | /** 79 | * Converts immutable sorted map to mutable one. 80 | * 81 | * @return new mutable sorted map 82 | */ 83 | SortedMap toMutableSortedMap(); 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableSortedSet.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import java.util.Comparator; 4 | import java.util.Optional; 5 | import java.util.SortedSet; 6 | 7 | /** 8 | * Defines immutable sorted set. Unlike native {@linkplain SortedSet} this interface does not 9 | * provide any methods that can mutate its state. So, it safe to use it in concurrent environments. 10 | * 11 | * @param the type of the objects that set contains 12 | * @see SortedSet 13 | */ 14 | public interface ImmutableSortedSet extends ImmutableSet { 15 | 16 | /** 17 | * Gets {@linkplain Comparator} that is used to sort values in the set. 18 | * 19 | * @return the comparator used to order the keys in this map, or {@code null} if this map uses the 20 | * {@linkplain Comparable natural ordering} of its keys. 21 | */ 22 | Comparator comparator(); 23 | 24 | /** 25 | * Returns a view of the portion of this set whose elements range from {@code fromElement}, 26 | * inclusive, to {@code toElement}, exclusive. 27 | * 28 | * @param fromElement low endpoint (inclusive) of the returned set 29 | * @param toElement high endpoint (exclusive) of the returned set 30 | * @return a view of the portion of this set whose elements range from {@code fromElement}, 31 | * inclusive, to {@code toElement}, exclusive 32 | */ 33 | ImmutableSortedSet subSet(T fromElement, T toElement); 34 | 35 | /** 36 | * Returns a view of the portion of this set whose elements are strictly less than {@code 37 | * toElement}. 38 | * 39 | * @param toElement high endpoint (exclusive) of the returned set 40 | * @return a view of the portion of this set whose elements are strictly less than {@code 41 | * toElement} 42 | */ 43 | ImmutableSortedSet headSet(T toElement); 44 | 45 | /** 46 | * Returns a view of the portion of this set whose elements are greater than or equal to {@code 47 | * fromElement}. 48 | * 49 | * @param fromElement low endpoint (inclusive) of the returned set 50 | * @return a view of the portion of this set whose elements are greater than or equal to {@code 51 | * fromElement} 52 | */ 53 | ImmutableSortedSet tailSet(T fromElement); 54 | 55 | /** 56 | * Returns the first (lowest) element currently in this set. 57 | * 58 | * @return the first (lowest) element currently in this set 59 | */ 60 | Optional first(); 61 | 62 | /** 63 | * Returns the last (highest) element currently in this set. 64 | * 65 | * @return the last (highest) element currently in this set 66 | */ 67 | Optional last(); 68 | 69 | /** 70 | * Converts this immutable sorted set to mutable one. Creates new object, so its mutations does 71 | * not affect the immutable one. 72 | * 73 | * @return new mutable sorted set 74 | */ 75 | SortedSet toMutableSortedSet(); 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableTreeMap.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import com.kirekov.juu.monad.Try; 4 | import java.util.Collections; 5 | import java.util.Comparator; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.NavigableMap; 9 | import java.util.Objects; 10 | import java.util.Optional; 11 | import java.util.SortedMap; 12 | import java.util.TreeMap; 13 | import java.util.function.Supplier; 14 | 15 | /** 16 | * An immutable implementation of java native {@link TreeMap}. 17 | * 18 | * @param the type of the key 19 | * @param the type of the value 20 | * @see ImmutableNavigableMap 21 | * @see ImmutableMap 22 | * @see TreeMap 23 | * @since 1.1 24 | */ 25 | public final class ImmutableTreeMap implements ImmutableNavigableMap { 26 | 27 | private final NavigableMap navigableMap; 28 | private final ImmutableSet keys; 29 | private final ImmutableList values; 30 | private final ImmutableSet> pairs; 31 | 32 | /** 33 | * Creates new {@linkplain ImmutableTreeMap} from the given {@linkplain Map} and {@linkplain 34 | * Comparator}. 35 | * 36 | * @param map the source map 37 | * @param comparator the comparator 38 | * @param the type of the key 39 | * @param the type of the value 40 | * @return immutable tree map 41 | * @throws NullPointerException if {@code map} or {@code comparator} is null 42 | */ 43 | public static ImmutableTreeMap of(Map map, Comparator comparator) { 44 | Objects.requireNonNull(map); 45 | Objects.requireNonNull(comparator); 46 | return new ImmutableTreeMap<>(map, comparator); 47 | } 48 | 49 | public static , V> ImmutableTreeMap of(Map map) { 50 | Objects.requireNonNull(map); 51 | return new ImmutableTreeMap<>(map, null); 52 | } 53 | 54 | public static ImmutableTreeMap ofSortedMap(SortedMap sortedMap) { 55 | Objects.requireNonNull(sortedMap); 56 | return new ImmutableTreeMap<>(sortedMap); 57 | } 58 | 59 | ImmutableTreeMap(Map map, Comparator comparator) { 60 | Objects.requireNonNull(map); 61 | navigableMap = new TreeMap<>(comparator); 62 | map.forEach(navigableMap::put); 63 | keys = Immutable.setOf(navigableMap.keySet()); 64 | values = Immutable.listOf(navigableMap.values()); 65 | pairs = ImmutableMapUtils.toPairSet(navigableMap.entrySet()); 66 | } 67 | 68 | ImmutableTreeMap(SortedMap sortedMap) { 69 | navigableMap = new TreeMap<>(sortedMap); 70 | keys = Immutable.setOf(navigableMap.keySet()); 71 | values = Immutable.listOf(navigableMap.values()); 72 | pairs = ImmutableMapUtils.toPairSet(navigableMap.entrySet()); 73 | } 74 | 75 | @Override 76 | public Optional> lowerPair(K key) { 77 | return ImmutableCollectionUtils.tryGetElement(() -> navigableMap.lowerEntry(key)) 78 | .map(Pair::of); 79 | } 80 | 81 | @Override 82 | public Optional lowerKey(K key) { 83 | return ImmutableCollectionUtils.tryGetElement(() -> navigableMap.lowerKey(key)); 84 | } 85 | 86 | @Override 87 | public Optional> floorPair(K key) { 88 | return ImmutableCollectionUtils.tryGetElement(() -> navigableMap.floorEntry(key)) 89 | .map(Pair::of); 90 | } 91 | 92 | @Override 93 | public Optional floorKey(K key) { 94 | return ImmutableCollectionUtils.tryGetElement(() -> navigableMap.floorKey(key)); 95 | } 96 | 97 | @Override 98 | public Optional> ceilingPair(K key) { 99 | return ImmutableCollectionUtils.tryGetElement(() -> navigableMap.ceilingEntry(key)) 100 | .map(Pair::of); 101 | } 102 | 103 | @Override 104 | public Optional ceilingKey(K key) { 105 | return ImmutableCollectionUtils.tryGetElement(() -> navigableMap.ceilingKey(key)); 106 | } 107 | 108 | @Override 109 | public Optional> higherPair(K key) { 110 | return ImmutableCollectionUtils.tryGetElement(() -> navigableMap.higherEntry(key)) 111 | .map(Pair::of); 112 | } 113 | 114 | @Override 115 | public Optional higherKey(K key) { 116 | return ImmutableCollectionUtils.tryGetElement(() -> navigableMap.higherKey(key)); 117 | } 118 | 119 | @Override 120 | public Optional> firstPair() { 121 | return ImmutableCollectionUtils.tryGetElement(navigableMap::firstEntry) 122 | .map(Pair::of); 123 | } 124 | 125 | @Override 126 | public Optional> lastPair() { 127 | return ImmutableCollectionUtils.tryGetElement(navigableMap::lastEntry) 128 | .map(Pair::of); 129 | } 130 | 131 | @Override 132 | public ImmutableNavigableMap reversedOrderMap() { 133 | return new ImmutableTreeMap<>(navigableMap.descendingMap()); 134 | } 135 | 136 | @Override 137 | public ImmutableNavigableSet navigableKeySet() { 138 | return new ImmutableTreeSet<>(navigableMap.navigableKeySet()); 139 | } 140 | 141 | @Override 142 | public ImmutableNavigableSet reversedOrderKeySet() { 143 | return new ImmutableTreeSet<>(navigableMap.descendingKeySet()); 144 | } 145 | 146 | private ImmutableNavigableMap tryGetSubMap(Supplier> supplier) { 147 | return Try.of(supplier::get) 148 | .orElse(new ImmutableTreeMap<>(Collections.emptyMap(), navigableMap.comparator())); 149 | } 150 | 151 | @Override 152 | public ImmutableSortedMap subMap(K fromKey, K toKey) { 153 | return tryGetSubMap(() -> 154 | new ImmutableTreeMap<>( 155 | navigableMap.subMap(fromKey, toKey) 156 | )); 157 | } 158 | 159 | @Override 160 | public ImmutableNavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, 161 | boolean toInclusive) { 162 | return tryGetSubMap(() -> 163 | new ImmutableTreeMap<>( 164 | navigableMap.subMap(fromKey, fromInclusive, toKey, toInclusive) 165 | )); 166 | } 167 | 168 | @Override 169 | public ImmutableSortedMap headMap(K toKey) { 170 | return tryGetSubMap(() -> 171 | new ImmutableTreeMap<>( 172 | navigableMap.headMap(toKey) 173 | )); 174 | } 175 | 176 | @Override 177 | public ImmutableNavigableMap headMap(K toKey, boolean inclusive) { 178 | return tryGetSubMap(() -> 179 | new ImmutableTreeMap<>( 180 | navigableMap.headMap(toKey, inclusive) 181 | )); 182 | } 183 | 184 | @Override 185 | public ImmutableSortedMap tailMap(K fromKey) { 186 | return tryGetSubMap(() -> 187 | new ImmutableTreeMap<>( 188 | navigableMap.tailMap(fromKey) 189 | )); 190 | } 191 | 192 | @Override 193 | public ImmutableNavigableMap tailMap(K fromKey, boolean inclusive) { 194 | return tryGetSubMap(() -> 195 | new ImmutableTreeMap<>( 196 | navigableMap.tailMap(fromKey, inclusive) 197 | )); 198 | } 199 | 200 | @Override 201 | public NavigableMap toMutableNavigableMap() { 202 | return new TreeMap<>(navigableMap); 203 | } 204 | 205 | @Override 206 | public Comparator comparator() { 207 | return navigableMap.comparator(); 208 | } 209 | 210 | @Override 211 | public Optional firstKey() { 212 | return ImmutableCollectionUtils.tryGetElement(navigableMap::firstKey); 213 | } 214 | 215 | @Override 216 | public Optional lastKey() { 217 | return ImmutableCollectionUtils.tryGetElement(navigableMap::lastKey); 218 | } 219 | 220 | @Override 221 | public SortedMap toMutableSortedMap() { 222 | return toMutableNavigableMap(); 223 | } 224 | 225 | @Override 226 | public int size() { 227 | return navigableMap.size(); 228 | } 229 | 230 | @Override 231 | public boolean containsKey(Object key) { 232 | return Try.of(() -> navigableMap.containsKey(key)) 233 | .orElse(false); 234 | } 235 | 236 | @Override 237 | public boolean containsValue(Object value) { 238 | return Try.of(() -> navigableMap.containsValue(value)) 239 | .orElse(false); 240 | } 241 | 242 | @Override 243 | public V get(Object key) { 244 | return Try.of(() -> navigableMap.get(key)) 245 | .orElse(null); 246 | } 247 | 248 | @Override 249 | public ImmutableSet keySet() { 250 | return keys; 251 | } 252 | 253 | @Override 254 | public ImmutableList values() { 255 | return values; 256 | } 257 | 258 | @Override 259 | public ImmutableSet> pairSet() { 260 | return pairs; 261 | } 262 | 263 | @Override 264 | public Map toMutableMap() { 265 | return new HashMap<>(this.navigableMap); 266 | } 267 | 268 | @Override 269 | public boolean equals(Object o) { 270 | if (this == o) { 271 | return true; 272 | } 273 | if (o == null || getClass() != o.getClass()) { 274 | return false; 275 | } 276 | final ImmutableTreeMap that = (ImmutableTreeMap) o; 277 | return navigableMap.equals(that.navigableMap); 278 | } 279 | 280 | @Override 281 | public int hashCode() { 282 | return navigableMap.hashCode(); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/ImmutableTreeSet.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import com.kirekov.juu.collection.immutable.abstraction.AbstractImmutableSet; 4 | import com.kirekov.juu.monad.Try; 5 | import java.util.Comparator; 6 | import java.util.HashSet; 7 | import java.util.Iterator; 8 | import java.util.NavigableSet; 9 | import java.util.Objects; 10 | import java.util.Optional; 11 | import java.util.Set; 12 | import java.util.SortedSet; 13 | import java.util.TreeSet; 14 | import java.util.function.Function; 15 | import java.util.function.Predicate; 16 | import java.util.function.Supplier; 17 | import java.util.stream.Stream; 18 | 19 | /** 20 | * An immutable implementation of java native {@linkplain TreeSet}. 21 | * 22 | * @param the type of the value 23 | * @see ImmutableNavigableSet 24 | * @see Set 25 | * @see TreeSet 26 | */ 27 | public final class ImmutableTreeSet 28 | extends AbstractImmutableSet implements ImmutableNavigableSet { 29 | 30 | private final NavigableSet navigableSet; 31 | 32 | /** 33 | * Creates new {@linkplain ImmutableTreeSet}. 34 | * 35 | * @param iterable source of elements 36 | * @param the type of the element 37 | * @return new {@linkplain ImmutableTreeSet} 38 | * @throws NullPointerException if {@code iterable} is null 39 | */ 40 | public static > ImmutableTreeSet of(Iterable iterable) { 41 | Objects.requireNonNull( 42 | iterable, 43 | "iterable to create ImmutableTreeSet cannot be null" 44 | ); 45 | return new ImmutableTreeSet<>(iterable, null); 46 | } 47 | 48 | /** 49 | * Creates new {@linkplain ImmutableTreeSet}. 50 | * 51 | * @param iterable source of elements 52 | * @param comparator element's comparator. Might be null 53 | * @param the type of the element 54 | * @return new {@linkplain ImmutableTreeSet} 55 | * @throws NullPointerException if {@code iterable} is null 56 | */ 57 | public static ImmutableTreeSet of(Iterable iterable, Comparator comparator) { 58 | Objects.requireNonNull( 59 | iterable, 60 | "iterable to create ImmutableTreeSet cannot be null" 61 | ); 62 | return new ImmutableTreeSet<>(iterable, comparator); 63 | } 64 | 65 | /** 66 | * Creates new {@linkplain ImmutableTreeSet}. 67 | * 68 | * @param sortedSet source of elements 69 | * @param the type of the element 70 | * @return new {@linkplain ImmutableTreeSet} 71 | * @throws NullPointerException if {@code sortedSet} is null 72 | */ 73 | public static ImmutableTreeSet ofSortedSet(SortedSet sortedSet) { 74 | Objects.requireNonNull( 75 | sortedSet, 76 | "sortedSet to create ImmutableTreeSet cannot be null" 77 | ); 78 | return new ImmutableTreeSet<>(sortedSet); 79 | } 80 | 81 | /** 82 | * Constructor. 83 | * 84 | * @param iterable source of elements 85 | * @param comparator element's comparator. Might be null. 86 | * @throws NullPointerException if {@code iterable} is null 87 | */ 88 | ImmutableTreeSet(Iterable iterable, Comparator comparator) { 89 | super(); 90 | Objects.requireNonNull( 91 | iterable, 92 | "iterable to create ImmutableTreeSet cannot be null" 93 | ); 94 | navigableSet = new TreeSet<>(comparator); 95 | for (final T t : iterable) { 96 | navigableSet.add(t); 97 | } 98 | } 99 | 100 | /** 101 | * Constructor. 102 | * 103 | * @param sortedSet source of elements 104 | * @throws NullPointerException if {@code sortedSet} is null 105 | */ 106 | ImmutableTreeSet(SortedSet sortedSet) { 107 | super(); 108 | Objects.requireNonNull( 109 | sortedSet, 110 | "sortedSet to create ImmutableTreeSet cannot be null" 111 | ); 112 | this.navigableSet = new TreeSet<>(sortedSet); 113 | } 114 | 115 | @Override 116 | public Optional lower(T t) { 117 | return ImmutableCollectionUtils.tryGetElement(() -> navigableSet.lower(t)); 118 | } 119 | 120 | @Override 121 | public Optional floor(T t) { 122 | return ImmutableCollectionUtils.tryGetElement(() -> navigableSet.floor(t)); 123 | } 124 | 125 | @Override 126 | public Optional ceiling(T t) { 127 | return ImmutableCollectionUtils.tryGetElement(() -> navigableSet.ceiling(t)); 128 | } 129 | 130 | @Override 131 | public Optional higher(T t) { 132 | return ImmutableCollectionUtils.tryGetElement(() -> navigableSet.higher(t)); 133 | } 134 | 135 | @Override 136 | public ImmutableNavigableSet reversedOrderSet() { 137 | return new ImmutableTreeSet<>(navigableSet.descendingSet()); 138 | } 139 | 140 | @Override 141 | public Iterator reversedOrderIterator() { 142 | return new UnmodifiableIterator<>(navigableSet.descendingIterator()); 143 | } 144 | 145 | @Override 146 | public ImmutableSortedSet subSet(T fromElement, T toElement) { 147 | return tryGetSubSet(() -> 148 | new ImmutableTreeSet<>( 149 | navigableSet.subSet(fromElement, toElement) 150 | )); 151 | } 152 | 153 | @Override 154 | public ImmutableNavigableSet subSet( 155 | T fromElement, 156 | boolean fromInclusive, 157 | T toElement, 158 | boolean toInclusive 159 | ) { 160 | return tryGetSubSet(() -> 161 | new ImmutableTreeSet<>( 162 | navigableSet.subSet(fromElement, fromInclusive, toElement, toInclusive) 163 | )); 164 | } 165 | 166 | @Override 167 | public ImmutableSortedSet headSet(T toElement) { 168 | return tryGetSubSet(() -> 169 | new ImmutableTreeSet<>( 170 | navigableSet.headSet(toElement) 171 | )); 172 | } 173 | 174 | @Override 175 | public ImmutableNavigableSet headSet(T toElement, boolean inclusive) { 176 | return tryGetSubSet(() -> 177 | new ImmutableTreeSet<>( 178 | navigableSet.headSet(toElement, inclusive) 179 | )); 180 | } 181 | 182 | @Override 183 | public ImmutableSortedSet tailSet(T fromElement) { 184 | return tryGetSubSet(() -> 185 | new ImmutableTreeSet<>( 186 | navigableSet.tailSet(fromElement) 187 | )); 188 | } 189 | 190 | @Override 191 | public ImmutableNavigableSet tailSet(T fromElement, boolean inclusive) { 192 | return tryGetSubSet(() -> 193 | new ImmutableTreeSet<>( 194 | navigableSet.tailSet(fromElement, inclusive) 195 | )); 196 | } 197 | 198 | @Override 199 | public NavigableSet toMutableNavigableSet() { 200 | return new TreeSet<>(navigableSet); 201 | } 202 | 203 | @Override 204 | public Comparator comparator() { 205 | return navigableSet.comparator(); 206 | } 207 | 208 | @Override 209 | public Optional first() { 210 | return ImmutableCollectionUtils.tryGetElement(navigableSet::first); 211 | } 212 | 213 | @Override 214 | public Optional last() { 215 | return ImmutableCollectionUtils.tryGetElement(navigableSet::last); 216 | } 217 | 218 | @Override 219 | public SortedSet toMutableSortedSet() { 220 | return toMutableNavigableSet(); 221 | } 222 | 223 | @Override 224 | public ImmutableSet concatWith(Iterable iterable) { 225 | Objects.requireNonNull(iterable, "iterable to concat with cannot be null"); 226 | final TreeSet newTreeSet = new TreeSet<>(navigableSet); 227 | for (final T t : iterable) { 228 | newTreeSet.add(t); 229 | } 230 | return new ImmutableTreeSet<>(newTreeSet); 231 | } 232 | 233 | @Override 234 | public ImmutableSet map(Function mapper) { 235 | Objects.requireNonNull(mapper, "mapper function cannot be null"); 236 | final HashSet hashSet = new HashSet<>(size()); 237 | for (final T t : this) { 238 | hashSet.add(mapper.apply(t)); 239 | } 240 | return new ImmutableHashSet<>(hashSet); 241 | } 242 | 243 | @Override 244 | public ImmutableSet flatMap(Function> mapper) { 245 | Objects.requireNonNull(mapper, "flat mapper function cannot be null"); 246 | final HashSet hashSet = new HashSet<>(); 247 | for (final T t : this) { 248 | for (final R r : mapper.apply(t)) { 249 | hashSet.add(r); 250 | } 251 | } 252 | return new ImmutableHashSet<>(hashSet); 253 | } 254 | 255 | @Override 256 | public ImmutableSet filter(Predicate predicate) { 257 | Objects.requireNonNull(predicate, "filtering predicate cannot be null"); 258 | final TreeSet newTreeSet = new TreeSet<>(comparator()); 259 | for (final T t : this) { 260 | if (predicate.test(t)) { 261 | newTreeSet.add(t); 262 | } 263 | } 264 | return new ImmutableTreeSet<>(newTreeSet); 265 | } 266 | 267 | @Override 268 | public int size() { 269 | return navigableSet.size(); 270 | } 271 | 272 | @Override 273 | public boolean contains(Object element) { 274 | return Try.of(() -> navigableSet.contains(element)) 275 | .orElse(false); 276 | } 277 | 278 | @Override 279 | public ImmutableList toList() { 280 | return Immutable.listOf(navigableSet); 281 | } 282 | 283 | @Override 284 | public ImmutableSet toSet() { 285 | return this; 286 | } 287 | 288 | @Override 289 | public Stream parallelStream() { 290 | return navigableSet.parallelStream(); 291 | } 292 | 293 | @Override 294 | public Stream stream() { 295 | return navigableSet.stream(); 296 | } 297 | 298 | @Override 299 | public Iterator iterator() { 300 | return new UnmodifiableIterator<>(navigableSet.iterator()); 301 | } 302 | 303 | @Override 304 | public boolean equals(Object o) { 305 | if (this == o) { 306 | return true; 307 | } 308 | if (o == null || getClass() != o.getClass()) { 309 | return false; 310 | } 311 | final ImmutableTreeSet that = (ImmutableTreeSet) o; 312 | return navigableSet.equals(that.navigableSet); 313 | } 314 | 315 | @Override 316 | public int hashCode() { 317 | return navigableSet.hashCode(); 318 | } 319 | 320 | private ImmutableNavigableSet tryGetSubSet(Supplier> supplier) { 321 | return Try.of(supplier::get) 322 | .orElse(new ImmutableTreeSet<>(Immutable.emptyList(), navigableSet.comparator())); 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/Pair.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import java.util.Map; 4 | import java.util.Objects; 5 | 6 | /** 7 | * Immutable pair that keeps key and value. Used by {@link ImmutableMap}. 8 | * 9 | * @param the type of the key 10 | * @param the type of the value 11 | * @since 1.0 12 | */ 13 | public interface Pair { 14 | 15 | K getKey(); 16 | 17 | V getValue(); 18 | 19 | /** 20 | * Overrides method from {@link Object#equals(Object)}. Must be implemented. 21 | */ 22 | @Override 23 | boolean equals(Object o); 24 | 25 | /** 26 | * Overrides method from {@link Object#hashCode()}. Must be implemented. 27 | */ 28 | @Override 29 | int hashCode(); 30 | 31 | /** 32 | * Instantiates new pair. 33 | * 34 | * @param key the key 35 | * @param value the value 36 | * @param the type of the key 37 | * @param the type of the value 38 | * @return new pair 39 | */ 40 | static Pair of(K key, V value) { 41 | return new PairImpl<>(key, value); 42 | } 43 | 44 | /** 45 | * Instantiates new pair. 46 | * 47 | * @param entry entry which key and value will be used 48 | * @param the type of the key 49 | * @param the type of the value 50 | * @return new pair 51 | * @throws NullPointerException if {@code entry} is null 52 | */ 53 | static Pair of(Map.Entry entry) { 54 | Objects.requireNonNull(entry); 55 | return new PairImpl<>(entry.getKey(), entry.getValue()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/PairImpl.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * Simple implementation of {@link Pair}. 7 | * 8 | * @param the type of the key 9 | * @param the type of the value 10 | * @since 1.0 11 | */ 12 | final class PairImpl implements Pair { 13 | 14 | private final K key; 15 | private final V value; 16 | 17 | PairImpl(K key, V value) { 18 | this.key = key; 19 | this.value = value; 20 | } 21 | 22 | @Override 23 | public K getKey() { 24 | return key; 25 | } 26 | 27 | @Override 28 | public V getValue() { 29 | return value; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) { 35 | return true; 36 | } 37 | if (o == null || getClass() != o.getClass()) { 38 | return false; 39 | } 40 | final PairImpl pair = (PairImpl) o; 41 | return Objects.equals(key, pair.key) && Objects.equals(value, pair.value); 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | return Objects.hash(key, value); 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return String.format("(key=%s; value=%s)", key, value); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/UnmodifiableIterator.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import java.util.Iterator; 4 | import java.util.Objects; 5 | 6 | /** 7 | * An implementation of {@link Iterator} that forbids removing elements. Behaves as a wrapper that 8 | * accepts actual iterator and allows to call concrete methods. 9 | * 10 | * @param the type of the element 11 | */ 12 | public final class UnmodifiableIterator implements Iterator { 13 | 14 | private final Iterator iterator; 15 | 16 | /** 17 | * Instantiates new instance of {@linkplain UnmodifiableIterator}. 18 | * 19 | * @param iterator the actual iterator that is going to be traversed 20 | * @throws NullPointerException if {@code iterator} is null 21 | */ 22 | public UnmodifiableIterator(Iterator iterator) { 23 | Objects.requireNonNull(iterator); 24 | this.iterator = iterator; 25 | } 26 | 27 | 28 | @Override 29 | public boolean hasNext() { 30 | return iterator.hasNext(); 31 | } 32 | 33 | @Override 34 | public T next() { 35 | return iterator.next(); 36 | } 37 | 38 | /** 39 | * Always throws {@link UnsupportedOperationException}. Declared as final in order not be 40 | * overridden. 41 | * 42 | * @throws UnsupportedOperationException always 43 | */ 44 | @Override 45 | public void remove() { 46 | throw new UnsupportedOperationException("Element's removing is not allowed"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/abstraction/AbstractImmutableList.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable.abstraction; 2 | 3 | import com.kirekov.juu.collection.immutable.ImmutableList; 4 | 5 | /** 6 | * Abstract immutable list. 7 | * 8 | * @param the type of the content 9 | */ 10 | public abstract class AbstractImmutableList implements ImmutableList { 11 | 12 | @Override 13 | public String toString() { 14 | if (isEmpty()) { 15 | return "[]"; 16 | } 17 | final StringBuilder builder = new StringBuilder().append("["); 18 | for (int i = 0; i < size(); i++) { 19 | builder.append(get(i)); 20 | if (i == size() - 1) { 21 | builder.append("]"); 22 | } else { 23 | builder.append(", "); 24 | } 25 | } 26 | return builder.toString(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/immutable/abstraction/AbstractImmutableSet.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable.abstraction; 2 | 3 | import com.kirekov.juu.collection.immutable.ImmutableSet; 4 | import java.util.Iterator; 5 | 6 | /** 7 | * Abstract immutable set. 8 | * 9 | * @param the type of the content 10 | */ 11 | public abstract class AbstractImmutableSet implements ImmutableSet { 12 | 13 | @Override 14 | public String toString() { 15 | if (isEmpty()) { 16 | return "{}"; 17 | } 18 | final StringBuilder builder = new StringBuilder().append("{"); 19 | final Iterator it = iterator(); 20 | while (it.hasNext()) { 21 | final T element = it.next(); 22 | builder.append(element); 23 | if (it.hasNext()) { 24 | builder.append(", "); 25 | } else { 26 | builder.append("}"); 27 | } 28 | } 29 | return builder.toString(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/mutable/MutableBoolean.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | /** 4 | * Container which allows to hold boolean value and change it if necessary. 5 | * 6 | * @see MutableValue 7 | */ 8 | public final class MutableBoolean { 9 | 10 | private boolean value; 11 | 12 | public MutableBoolean(boolean value) { 13 | this.value = value; 14 | } 15 | 16 | public boolean isValue() { 17 | return value; 18 | } 19 | 20 | public void setValue(boolean value) { 21 | this.value = value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/mutable/MutableByte.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | /** 4 | * Container which allows to hold byte value and change it if necessary. 5 | * 6 | * @see MutableValue 7 | */ 8 | public final class MutableByte { 9 | 10 | private byte value; 11 | 12 | public MutableByte(byte value) { 13 | this.value = value; 14 | } 15 | 16 | public byte getValue() { 17 | return value; 18 | } 19 | 20 | public void setValue(byte value) { 21 | this.value = value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/mutable/MutableChar.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | /** 4 | * Container which allows to hold char value and change it if necessary. 5 | * 6 | * @see MutableValue 7 | */ 8 | public final class MutableChar { 9 | 10 | private char value; 11 | 12 | public MutableChar(char value) { 13 | this.value = value; 14 | } 15 | 16 | public char getValue() { 17 | return value; 18 | } 19 | 20 | public void setValue(char value) { 21 | this.value = value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/mutable/MutableDouble.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | /** 4 | * Container which allows to hold double value and change it if necessary. 5 | * 6 | * @see MutableValue 7 | */ 8 | public final class MutableDouble { 9 | 10 | private double value; 11 | 12 | public MutableDouble(double value) { 13 | this.value = value; 14 | } 15 | 16 | public double getValue() { 17 | return value; 18 | } 19 | 20 | public void setValue(double value) { 21 | this.value = value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/mutable/MutableFloat.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | /** 4 | * Container which allows to hold float value and change it if necessary. 5 | * 6 | * @see MutableValue 7 | */ 8 | public final class MutableFloat { 9 | 10 | private float value; 11 | 12 | public MutableFloat(float value) { 13 | this.value = value; 14 | } 15 | 16 | public float getValue() { 17 | return value; 18 | } 19 | 20 | public void setValue(float value) { 21 | this.value = value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/mutable/MutableInt.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | /** 4 | * Container which allows to hold int value and change it if necessary. 5 | * 6 | * @see MutableValue 7 | */ 8 | public final class MutableInt { 9 | 10 | private int value; 11 | 12 | public MutableInt(int value) { 13 | this.value = value; 14 | } 15 | 16 | public int getValue() { 17 | return value; 18 | } 19 | 20 | public void setValue(int value) { 21 | this.value = value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/mutable/MutableLong.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | /** 4 | * Container which allows to hold long value and change it if necessary. 5 | * 6 | * @see MutableValue 7 | */ 8 | public final class MutableLong { 9 | 10 | private long value; 11 | 12 | public MutableLong(long value) { 13 | this.value = value; 14 | } 15 | 16 | public long getValue() { 17 | return value; 18 | } 19 | 20 | public void setValue(long value) { 21 | this.value = value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/mutable/MutableShort.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | /** 4 | * Container which allows to hold short value and change it if necessary. 5 | */ 6 | public final class MutableShort { 7 | 8 | private short value; 9 | 10 | public MutableShort(short value) { 11 | this.value = value; 12 | } 13 | 14 | public short getValue() { 15 | return value; 16 | } 17 | 18 | public void setValue(short value) { 19 | this.value = value; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/collection/mutable/MutableValue.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | /** 4 | * Container which allows to hold a value and change it if necessary. 5 | * 6 | * @param the type of the value 7 | * @see MutableBoolean 8 | * @see MutableByte 9 | * @see MutableChar 10 | * @see MutableDouble 11 | * @see MutableFloat 12 | * @see MutableInt 13 | * @see MutableLong 14 | * @see MutableShort 15 | */ 16 | public final class MutableValue { 17 | 18 | private T value; 19 | 20 | public MutableValue(T value) { 21 | this.value = value; 22 | } 23 | 24 | public T getValue() { 25 | return value; 26 | } 27 | 28 | public void setValue(T value) { 29 | this.value = value; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/exception/EmptyContainerException.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.exception; 2 | 3 | import com.kirekov.juu.monad.Try; 4 | 5 | /** 6 | * Suppose to be thrown in case of accessing empty container. 7 | * 8 | * @see Try 9 | * @since 1.1 10 | */ 11 | public class EmptyContainerException extends RuntimeException { 12 | 13 | public EmptyContainerException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | 17 | public EmptyContainerException(String message) { 18 | super(message); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/lambda/Action.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.lambda; 2 | 3 | /** 4 | * Represents a procedure that do some stuff and returns nothing. 5 | * 6 | * @since 1.0 7 | */ 8 | @FunctionalInterface 9 | public interface Action { 10 | 11 | void execute(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/lambda/CachedResultCheckedSupplier.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.lambda; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Implementation for {@linkplain CheckedSupplier} which calculates value only for the first time 8 | * and then returns cached result. This implementation is not thread-safe. 9 | * 10 | * @param the type of the return value 11 | * @param the type of the exception 12 | */ 13 | public class CachedResultCheckedSupplier implements CheckedSupplier { 14 | 15 | private final List result = new ArrayList<>(1); 16 | 17 | private final CheckedSupplier checkedSupplier; 18 | 19 | public CachedResultCheckedSupplier(CheckedSupplier checkedSupplier) { 20 | this.checkedSupplier = checkedSupplier; 21 | } 22 | 23 | @Override 24 | public T get() throws E { 25 | if (result.isEmpty()) { 26 | result.add(checkedSupplier.get()); 27 | } 28 | return result.get(0); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/lambda/CachedResultSupplier.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.lambda; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.function.Supplier; 6 | 7 | /** 8 | * Implementation for {@linkplain Supplier} which calculates value only once for the first time and 9 | * then returns cached result. This implementation is not thread safe. 10 | * 11 | * @param the type of the return value 12 | */ 13 | public class CachedResultSupplier implements Supplier { 14 | 15 | private final List result = new ArrayList<>(1); 16 | 17 | private final Supplier supplier; 18 | 19 | public CachedResultSupplier(Supplier supplier) { 20 | this.supplier = supplier; 21 | } 22 | 23 | @Override 24 | public T get() { 25 | if (result.isEmpty()) { 26 | result.add(supplier.get()); 27 | } 28 | return result.get(0); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/lambda/CheckedFunction.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.lambda; 2 | 3 | /** 4 | * Represents a function that accepts one argument and produces a result. May throw an exception. 5 | * 6 | * @param the type of the input to the function 7 | * @param the type of the result of the function 8 | * @param the type of the exception 9 | * @since 1.0 10 | */ 11 | @FunctionalInterface 12 | public interface CheckedFunction { 13 | 14 | R apply(T t) throws E; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/lambda/CheckedSupplier.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.lambda; 2 | 3 | /** 4 | * Represents a supplier of results. May throw an exception 5 | * 6 | * @param the type of the result 7 | * @param the type of the exception 8 | * @since 1.0 9 | */ 10 | @FunctionalInterface 11 | public interface CheckedSupplier { 12 | 13 | T get() throws E; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/lambda/TriFunction.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.lambda; 2 | 3 | import java.util.function.BiFunction; 4 | import java.util.function.Function; 5 | 6 | /** 7 | * Represents the function that accepts three arguments and produces a result. 8 | * 9 | * @param the type of the first argument 10 | * @param the type of the second argument 11 | * @param the type of the third argument 12 | * @param the type of the result 13 | * @see Function 14 | * @see BiFunction 15 | */ 16 | @FunctionalInterface 17 | public interface TriFunction { 18 | 19 | R apply(T1 t1, T2 t2, T3 t3); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/measure/ExecutionResult.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.measure; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * A class which contains the result of function execution and time spent for it. 7 | * 8 | * @param function result type 9 | * @see Measure 10 | * @see MeasureUnit 11 | * @since 0.1 12 | */ 13 | public final class ExecutionResult { 14 | 15 | private final T result; 16 | private final long time; 17 | private final MeasureUnit measureUnit; 18 | 19 | /** 20 | * Instantiates object. 21 | * 22 | * @param result result of calculation, can be null 23 | * @param time time spent 24 | * @param measureUnit measure units, cannot be null 25 | * @throws NullPointerException if measureUnit is null 26 | */ 27 | public ExecutionResult(T result, long time, MeasureUnit measureUnit) { 28 | this.result = result; 29 | this.time = time; 30 | this.measureUnit = Objects.requireNonNull(measureUnit); 31 | } 32 | 33 | /** 34 | * Gets result of the execution. 35 | * 36 | * @return function result 37 | */ 38 | public T getResult() { 39 | return result; 40 | } 41 | 42 | /** 43 | * Gets time spent for the calculation. 44 | * 45 | * @return time spent 46 | */ 47 | public long getTime() { 48 | return time; 49 | } 50 | 51 | /** 52 | * Gets {@linkplain MeasureUnit} that was used to measure calculation time. 53 | * 54 | * @return measure unit 55 | */ 56 | public MeasureUnit getMeasureUnit() { 57 | return measureUnit; 58 | } 59 | 60 | @Override 61 | public boolean equals(Object o) { 62 | if (this == o) { 63 | return true; 64 | } 65 | if (o == null || getClass() != o.getClass()) { 66 | return false; 67 | } 68 | final ExecutionResult that = (ExecutionResult) o; 69 | return time == that.time 70 | && Objects.equals(result, that.result) 71 | && measureUnit == that.measureUnit; 72 | } 73 | 74 | @Override 75 | public int hashCode() { 76 | return Objects.hash(result, time, measureUnit); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/measure/Measure.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.measure; 2 | 3 | import static com.kirekov.juu.measure.MeasureConverter.millisToSeconds; 4 | 5 | import com.kirekov.juu.lambda.Action; 6 | import java.util.Objects; 7 | import java.util.function.Supplier; 8 | 9 | /** 10 | * A class for measuring time of function execution with retrieving result. 11 | * 12 | * @param type of function return result 13 | * @see ExecutionResult 14 | * @since 0.1 15 | */ 16 | public final class Measure { 17 | 18 | private final Supplier supplier; 19 | 20 | private Measure(Supplier supplier) { 21 | this.supplier = supplier; 22 | } 23 | 24 | /** 25 | * Creates an instance of {@link Measure} class with function that will be executed Function will 26 | * not be executed until {@link Measure#inMillis()} or {@link Measure#inNanos()} will be called. 27 | * 28 | * @param supplier lambda function which needs to be executed. Cannot be null 29 | * @param function return type 30 | * @return an instance of class with given function 31 | * @throws NullPointerException if supplier is null 32 | */ 33 | public static Measure executionTime(Supplier supplier) { 34 | Objects.requireNonNull(supplier); 35 | return new Measure<>(supplier); 36 | } 37 | 38 | /** 39 | * Creates an instance of {@link Measure} class with procedure that will be executed. Procedure 40 | * will not be executed until {@link Measure#inMillis()} or {@link Measure#inNanos()} will be 41 | * called. 42 | * 43 | * @param action lambda procedure which needs to be executed. Cannot be {@code null} 44 | * @return an instance of class with given procedure 45 | * @throws NullPointerException if action is null 46 | */ 47 | public static Measure executionTime(Action action) { 48 | Objects.requireNonNull(action); 49 | return new Measure<>( 50 | () -> { 51 | action.execute(); 52 | return null; 53 | }); 54 | } 55 | 56 | /** 57 | * Gets execution result measured in {@linkplain MeasureUnit#MILLIS}. 58 | * 59 | * @return execution result measured in millis. 60 | */ 61 | public ExecutionResult inMillis() { 62 | final long time = System.currentTimeMillis(); 63 | final T result = supplier.get(); 64 | return new ExecutionResult<>(result, System.currentTimeMillis() - time, MeasureUnit.MILLIS); 65 | } 66 | 67 | /** 68 | * Gets execution result measured in {@linkplain MeasureUnit#NANOS}. 69 | * 70 | * @return execution result measured in nanos. 71 | */ 72 | public ExecutionResult inNanos() { 73 | final long time = System.nanoTime(); 74 | final T result = supplier.get(); 75 | return new ExecutionResult<>(result, System.nanoTime() - time, MeasureUnit.NANOS); 76 | } 77 | 78 | /** 79 | * Gets execution result measure in {@linkplain MeasureUnit#SECONDS}. 80 | * 81 | * @return execution result measured in seconds 82 | * @since 1.1 83 | */ 84 | public ExecutionResult inSeconds() { 85 | final long start = System.currentTimeMillis(); 86 | final T result = supplier.get(); 87 | final long finish = System.currentTimeMillis(); 88 | return new ExecutionResult<>(result, millisToSeconds(finish - start), MeasureUnit.SECONDS); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/measure/MeasureConverter.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.measure; 2 | 3 | /** 4 | * A class which converts one measure unit to another. 5 | * 6 | * @since 0.1 7 | */ 8 | public final class MeasureConverter { 9 | 10 | private static final long MEGA_COEFFICIENT = 1000L * 1000L; 11 | private static final long KILO_COEFFICIENT = 1000L; 12 | 13 | /** 14 | * Suppresses default constructor, ensuring non-instantiability. 15 | */ 16 | private MeasureConverter() { 17 | } 18 | 19 | /** 20 | * Convert millis to nanos. 21 | * 22 | * @param millis time in millis 23 | * @return time in nanos 24 | */ 25 | public static long millisToNanos(long millis) { 26 | return millis * MEGA_COEFFICIENT; 27 | } 28 | 29 | /** 30 | * Convert nanos to millis. 31 | * 32 | * @param nanos time in nanos 33 | * @return time in millis 34 | */ 35 | public static long nanosToMillis(long nanos) { 36 | return nanos / MEGA_COEFFICIENT; 37 | } 38 | 39 | /** 40 | * Converts millis to seconds. 41 | * 42 | * @param millis time in millis 43 | * @return time in seconds 44 | */ 45 | public static long millisToSeconds(long millis) { 46 | return millis / KILO_COEFFICIENT; 47 | } 48 | 49 | /** 50 | * Converts nanos to seconds. 51 | * 52 | * @param nanos time in nanos 53 | * @return time in seconds 54 | */ 55 | public static long nanosToSeconds(long nanos) { 56 | return nanos / KILO_COEFFICIENT / MEGA_COEFFICIENT; 57 | } 58 | 59 | /** 60 | * Converts seconds to millis. 61 | * 62 | * @param seconds time in seconds 63 | * @return time in millis 64 | */ 65 | public static long secondsToMillis(long seconds) { 66 | return seconds * KILO_COEFFICIENT; 67 | } 68 | 69 | /** 70 | * Converts seconds to nanos. 71 | * 72 | * @param seconds time in seconds 73 | * @return time in nanos 74 | */ 75 | public static long secondsToNanos(long seconds) { 76 | return seconds * KILO_COEFFICIENT * MEGA_COEFFICIENT; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/measure/MeasureUnit.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.measure; 2 | 3 | /** 4 | * Represents unit of measuring. 5 | * 6 | * @since 0.1 7 | */ 8 | public enum MeasureUnit { 9 | MILLIS, 10 | NANOS, 11 | SECONDS 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/measure/Profiler.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.measure; 2 | 3 | import static com.kirekov.juu.measure.MeasureConverter.millisToSeconds; 4 | 5 | import java.util.function.Supplier; 6 | 7 | /** 8 | * Measures time between object instantiating and stopping measuring. The class is not thread-safe. 9 | * 10 | * @since 0.1 11 | */ 12 | public final class Profiler { 13 | 14 | private static final long STILL_MEASURING = -1L; 15 | 16 | private final Supplier stopMeasuringSupplier; 17 | private final MeasureUnit measureUnit; 18 | 19 | private long measuringResult = STILL_MEASURING; 20 | 21 | /** 22 | * Instantiates new {@link Profiler} object and starts measuring in millis. 23 | * 24 | * @return new object with millis measuring 25 | */ 26 | public static Profiler startMeasuringInMillis() { 27 | final long startPoint = System.currentTimeMillis(); 28 | return new Profiler( 29 | () -> System.currentTimeMillis() - startPoint, 30 | MeasureUnit.MILLIS 31 | ); 32 | } 33 | 34 | /** 35 | * Instantiates new {@link Profiler} object and starts measuring in nanos. 36 | * 37 | * @return new object with nanos measuring 38 | */ 39 | public static Profiler startMeasuringInNanos() { 40 | final long startPoint = System.nanoTime(); 41 | return new Profiler( 42 | () -> System.nanoTime() - startPoint, 43 | MeasureUnit.NANOS 44 | ); 45 | } 46 | 47 | /** 48 | * Instantiates new {@link Profiler} object and starts measuring in seconds. 49 | * 50 | * @return new object with seconds measuring 51 | * @since 1.1 52 | */ 53 | public static Profiler startMeasuringInSeconds() { 54 | final long startPoint = System.currentTimeMillis(); 55 | return new Profiler( 56 | () -> millisToSeconds(System.currentTimeMillis() - startPoint), 57 | MeasureUnit.SECONDS 58 | ); 59 | } 60 | 61 | private Profiler(Supplier stopMeasuringSupplier, MeasureUnit measureUnit) { 62 | this.stopMeasuringSupplier = stopMeasuringSupplier; 63 | this.measureUnit = measureUnit; 64 | } 65 | 66 | /** 67 | * Gets {@linkplain MeasureUnit}. 68 | * 69 | * @return units of measuring 70 | */ 71 | public MeasureUnit getMeasureUnit() { 72 | return measureUnit; 73 | } 74 | 75 | /** 76 | * Stops measuring and returns time. Multiple calls don't affect the result. 77 | * 78 | * @return measured time 79 | */ 80 | public long stopMeasuring() { 81 | if (measuringResult == STILL_MEASURING) { 82 | measuringResult = stopMeasuringSupplier.get(); 83 | } 84 | return measuringResult; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/juu/monad/Try.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.monad; 2 | 3 | import com.kirekov.juu.collection.Streaming; 4 | import com.kirekov.juu.lambda.CheckedFunction; 5 | import com.kirekov.juu.lambda.CheckedSupplier; 6 | import java.util.NoSuchElementException; 7 | import java.util.Objects; 8 | import java.util.Optional; 9 | import java.util.function.Function; 10 | import java.util.function.Predicate; 11 | import java.util.function.Supplier; 12 | import java.util.stream.Stream; 13 | 14 | /** 15 | * Monad for retrieving values just like {@link Optional}, but instead a container considered as an 16 | * empty one if an exception has been thrown during calculations. 17 | *
18 | * The monad acts lazily. So, all methods build a pipeline which execution is triggered on 19 | * any terminal operation. 20 | *
21 | * List of terminal operations. 22 | *

    23 | *
  • {@linkplain Try#orElse(Object)}
  • 24 | *
  • {@linkplain Try#orElseGet(Supplier)}
  • 25 | *
  • {@linkplain Try#orElseGet(Function)}
  • 26 | *
  • {@linkplain Try#orElseThrow()}
  • 27 | *
  • {@linkplain Try#orElseThrow(Supplier)}
  • 28 | *
  • {@linkplain Try#stream()}
  • 29 | *
30 | *

The class only catches exceptions of type {@link Exception}. It means that all {@linkplain 31 | * Throwable} instances shall be skipped. The motivation is that {@link Error} extends from 32 | * {@linkplain Throwable} but this exceptions should not be caught manually.

33 | *
34 | * The class is thread-safe if the pipeline is thread-safe too. 35 | * 36 | * @param the type of the return value 37 | * @since 1.0 38 | */ 39 | public final class Try implements Streaming { 40 | 41 | private final CheckedSupplier valueSupplier; 42 | 43 | private Try(CheckedSupplier value) { 44 | this.valueSupplier = value; 45 | } 46 | 47 | /** 48 | * Create a monad with successful result. 49 | * 50 | * @param value the value to retrieve 51 | * @param the type of the value 52 | * @return monad with successful execution 53 | */ 54 | public static Try success(T value) { 55 | return new Try<>(() -> value); 56 | } 57 | 58 | /** 59 | * Create a monad with error result. The execution throws {@linkplain NoSuchElementException}. 60 | * 61 | * @param the type of the return value 62 | * @return monad with error result 63 | */ 64 | public static Try error() { 65 | return new Try<>(() -> { 66 | throw new NoSuchElementException("'Try' container is empty"); 67 | }); 68 | } 69 | 70 | /** 71 | * Create a monad with error result. The execution throws {@code exceptionToThrow}. 72 | * 73 | * @param the type of the return value 74 | * @param exceptionToThrow the exception that is ought to be thrown 75 | * @return monad with error result 76 | * @throws NullPointerException if {@code exceptionToThrow} is null 77 | */ 78 | public static Try error(Exception exceptionToThrow) { 79 | Objects.requireNonNull(exceptionToThrow, "exceptionToThrow cannot be null"); 80 | return new Try<>(() -> { 81 | throw exceptionToThrow; 82 | }); 83 | } 84 | 85 | 86 | /** 87 | * Create a monad of the given supplier. This is an intermediate operation. 88 | * 89 | * @param supplier supplier that returns value 90 | * @param the type of the return value 91 | * @return monad that holds that given supplier 92 | * @throws NullPointerException if suppliers parameter is null 93 | */ 94 | public static Try of(CheckedSupplier supplier) { 95 | Objects.requireNonNull(supplier, "supplier cannot be null"); 96 | return new Try<>(supplier); 97 | } 98 | 99 | /** 100 | * Map the value from one to another and return new monad. This is an intermediate operation. 101 | * 102 | * @param mapper mapping function 103 | * @param the type of the new value 104 | * @return a monad with mapped function 105 | * @throws NullPointerException if {@code mapper} is null 106 | */ 107 | public Try map(CheckedFunction mapper) { 108 | Objects.requireNonNull(mapper, "mapper cannot be null"); 109 | return Try.of(() -> mapper.apply(valueSupplier.get())); 110 | } 111 | 112 | /** 113 | * Map the value from one to another and returns new monad. The mapping function must return 114 | * another {@link Try} container. This is an intermediate operation. 115 | *
116 | * For instance, 117 | *
{@code
118 |    * Try.of(() -> 1)
119 |    *    .flatMap(v -> Try.of(() -> v + 1))
120 |    *    .orElse(-1)
121 |    * }
122 | * returns 2, while 123 | *
{@code
124 |    * Try.of(() -> 1)
125 |    *    .flatMap(v -> Try.of(() -> v / 0))
126 |    *    .orElse(-1)
127 |    * }
128 | * returns -1 129 | * 130 | * @param mapper mapping function 131 | * @param the type of the new value 132 | * @return monad with new value or an empty one 133 | * @throws NullPointerException if {@code mapper} is null 134 | * @see Try#map(CheckedFunction) 135 | */ 136 | public Try flatMap( 137 | CheckedFunction, ? extends Exception> mapper) { 138 | Objects.requireNonNull(mapper, "mapper cannot be null"); 139 | return Try.of(() -> mapper.apply(valueSupplier.get()).orElseThrow()); 140 | } 141 | 142 | /** 143 | * Filter the value with the given {@code predicate}. If {@code predicate} returns {@code false}, 144 | * the {@linkplain NoSuchElementException} is thrown. This is an intermediate operation. 145 | * 146 | * @param predicate predicate function 147 | * @return the container itself or an empty one 148 | * @throws NullPointerException if predicate is null 149 | */ 150 | public Try filter(Predicate predicate) { 151 | Objects.requireNonNull(predicate, "predicate cannot be null"); 152 | return Try.of(() -> { 153 | final T value = valueSupplier.get(); 154 | if (predicate.test(value)) { 155 | return value; 156 | } 157 | throw new NoSuchElementException("The filter does not pass"); 158 | }); 159 | } 160 | 161 | /** 162 | * Calculate the value and return the new monad that holds it. Otherwise, return the monad of the 163 | * default value. This is an intermediate operation. 164 | *
165 | *
{@code
166 |    * Try.of(() -> "str")
167 |    *    .orElseTry(() -> "newStr")
168 |    *    .orElse("null")
169 |    * }
170 | * returns {@code "str"}. 171 | *
{@code
172 |    * Try.of(() -> {throw new Exception();})
173 |    *    .orElseTry(() -> "newStr")
174 |    *    .orElse("null")
175 |    * }
176 | * returns {@code "newStr"} 177 | *
{@code
178 |    * Try.of(() -> {throw new Exception();})
179 |    *    .orElseTry(() -> {throw new Exception();})
180 |    *    .orElse("null")
181 |    * }
182 | * returns {@code "null"}. 183 | * 184 | * @param defaultValueSupplier supplier that provides the default value 185 | * @return the monad of the calculated value or the default one 186 | * @throws NullPointerException if {@code defaultValueSupplier} is null 187 | */ 188 | public Try orElseTry(CheckedSupplier defaultValueSupplier) { 189 | Objects.requireNonNull(defaultValueSupplier, "defaultValueSupplier cannot be null"); 190 | return new Try<>(() -> { 191 | try { 192 | return valueSupplier.get(); 193 | } catch (Exception e) { 194 | return defaultValueSupplier.get(); 195 | } 196 | }); 197 | } 198 | 199 | /** 200 | * Calculate the value and return it, if the calculation does not fail. Otherwise, return the 201 | * default value. This is a terminal operation. 202 | * 203 | * @param other the default value 204 | * @return the calculated value or the default one 205 | */ 206 | public T orElse(T other) { 207 | try { 208 | return valueSupplier.get(); 209 | } catch (Exception e) { 210 | return other; 211 | } 212 | } 213 | 214 | /** 215 | * Calculate the value and return it, if the calculation does not fail. Otherwise, return the 216 | * defaultValue. This is a terminal operation. 217 | * 218 | * @param defaultValueSupplier supplier the provides the default value 219 | * @return the calculated value or the default one 220 | * @throws NullPointerException if {@code defaultValueSupplier} is null 221 | */ 222 | public T orElseGet(Supplier defaultValueSupplier) { 223 | Objects.requireNonNull(defaultValueSupplier, "defaultValueSupplier cannot be null"); 224 | try { 225 | return valueSupplier.get(); 226 | } catch (Exception e) { 227 | return defaultValueSupplier.get(); 228 | } 229 | } 230 | 231 | /** 232 | * Calculate the value and return it, if the calculation does not fail. Otherwise, return the 233 | * default value. This is a terminal operation. 234 | * 235 | * @param defaultValueFunction function that accepts the exception that occurred and returns the 236 | * default value 237 | * @return the calculated value or the default one 238 | * @throws NullPointerException if {@code defaultValueFunction} is null 239 | */ 240 | public T orElseGet(Function defaultValueFunction) { 241 | Objects.requireNonNull(defaultValueFunction, "defaultValueFunction cannot be null"); 242 | try { 243 | return valueSupplier.get(); 244 | } catch (Exception e) { 245 | return defaultValueFunction.apply(e); 246 | } 247 | } 248 | 249 | /** 250 | * Calculate the value and return it, if the calculation does not fail. Otherwise, throws the 251 | * exception that led to error. 252 | * 253 | * @return the value of the container 254 | */ 255 | public T orElseThrow() { 256 | try { 257 | return valueSupplier.get(); 258 | } catch (Exception e) { 259 | return throwException(e); 260 | } 261 | } 262 | 263 | /** 264 | * Calculate the value and return it, if the calculation does not fail. Otherwise, throw the 265 | * supplied exception. This is a terminal operation. 266 | * 267 | * @param exceptionSupplier supplier, that returns exception 268 | * @param the type of the exception 269 | * @return the value of the container 270 | * @throws E if the value calculation fails 271 | * @throws NullPointerException if {@code exceptionSupplier} is null 272 | */ 273 | public T orElseThrow(Supplier exceptionSupplier) throws E { 274 | Objects.requireNonNull(exceptionSupplier); 275 | try { 276 | return valueSupplier.get(); 277 | } catch (Exception e) { 278 | throw exceptionSupplier.get(); 279 | } 280 | } 281 | 282 | /** 283 | * Transform a monad to {@linkplain Stream}. This is a terminal operation. 284 | * 285 | * @return stream of container's value, if the calculation passes. Otherwise, returns {@link 286 | * Stream#empty()} 287 | */ 288 | @Override 289 | public Stream stream() { 290 | try { 291 | return Stream.of(valueSupplier.get()); 292 | } catch (Exception e) { 293 | return Stream.empty(); 294 | } 295 | } 296 | 297 | @SuppressWarnings("unchecked") 298 | private static T throwException(Exception exception) throws E { 299 | throw (E) exception; 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/immutable/ImmutableCollectorsTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertThrows; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import java.util.stream.Stream; 8 | import org.junit.jupiter.api.Test; 9 | 10 | class ImmutableCollectorsTest { 11 | 12 | @Test 13 | void shouldCollectListFromTheStream() { 14 | ImmutableList list = 15 | Stream.of("1", "2", "3", "4") 16 | .collect(ImmutableCollectors.toList()); 17 | assertEquals(4, list.size()); 18 | assertEquals("1", list.get(0)); 19 | assertEquals("2", list.get(1)); 20 | assertEquals("3", list.get(2)); 21 | assertEquals("4", list.get(3)); 22 | } 23 | 24 | @Test 25 | void shouldCollectSetFromTheList() { 26 | ImmutableSet set = 27 | Stream.of("1", "2", "3", "4", "3", "2") 28 | .collect(ImmutableCollectors.toSet()); 29 | assertEquals(4, set.size()); 30 | assertTrue(set.contains("1")); 31 | assertTrue(set.contains("2")); 32 | assertTrue(set.contains("3")); 33 | assertTrue(set.contains("4")); 34 | } 35 | 36 | @Test 37 | void shouldCollectMapFromTheStream() { 38 | ImmutableMap map = 39 | Stream.of( 40 | Pair.of(1, "1"), 41 | Pair.of(2, "2"), 42 | Pair.of(3, "3"), 43 | Pair.of(4, "4") 44 | ).collect(ImmutableCollectors.toMap( 45 | Pair::getKey, 46 | Pair::getValue 47 | )); 48 | assertEquals(4, map.size()); 49 | assertEquals("1", map.get(1)); 50 | assertEquals("2", map.get(2)); 51 | assertEquals("3", map.get(3)); 52 | assertEquals("4", map.get(4)); 53 | 54 | assertThrows(IllegalStateException.class, () -> Stream.of( 55 | Pair.of(1, "1"), 56 | Pair.of(2, "2"), 57 | Pair.of(2, "3"), 58 | Pair.of(4, "4") 59 | ).collect(ImmutableCollectors.toMap( 60 | Pair::getKey, 61 | Pair::getValue 62 | ))); 63 | } 64 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/immutable/ImmutableHashMapTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 6 | import static org.junit.jupiter.api.Assertions.assertTrue; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import org.junit.jupiter.api.Test; 11 | 12 | class ImmutableHashMapTest { 13 | 14 | @Test 15 | void instantiateWithCloning() { 16 | Map mutable = new HashMap<>(); 17 | mutable.put("1", 1); 18 | mutable.put("2", 2); 19 | mutable.put("3", 3); 20 | 21 | ImmutableMap immutableMap = new ImmutableHashMap<>(mutable); 22 | 23 | assertEquals(3, immutableMap.size()); 24 | assertEquals(1, immutableMap.get("1")); 25 | assertEquals(2, immutableMap.get("2")); 26 | assertEquals(3, immutableMap.get("3")); 27 | 28 | mutable.clear(); 29 | 30 | assertEquals(3, immutableMap.size()); 31 | assertEquals(1, immutableMap.get("1")); 32 | assertEquals(2, immutableMap.get("2")); 33 | assertEquals(3, immutableMap.get("3")); 34 | } 35 | 36 | @Test 37 | void containsKeyAndValue() { 38 | Map mutable = new HashMap<>(); 39 | mutable.put("1", 1); 40 | mutable.put("2", 2); 41 | mutable.put("3", 3); 42 | 43 | ImmutableMap immutableMap = new ImmutableHashMap<>(mutable); 44 | 45 | assertTrue(immutableMap.containsKey("1")); 46 | assertTrue(immutableMap.containsKey("2")); 47 | assertTrue(immutableMap.containsKey("3")); 48 | 49 | assertTrue(immutableMap.containsValue(1)); 50 | assertTrue(immutableMap.containsValue(2)); 51 | assertTrue(immutableMap.containsValue(3)); 52 | 53 | assertFalse(immutableMap.containsKey("123")); 54 | assertFalse(immutableMap.containsKey("1123")); 55 | assertFalse(immutableMap.containsKey(12331)); 56 | 57 | assertFalse(immutableMap.containsValue(3123)); 58 | assertFalse(immutableMap.containsValue(315123)); 59 | assertFalse(immutableMap.containsValue("dasdas")); 60 | 61 | assertTrue(immutableMap.isNotEmpty()); 62 | assertFalse(immutableMap.isEmpty()); 63 | } 64 | 65 | @Test 66 | void notContainsKeyAndValue() { 67 | Map mutable = new HashMap<>(); 68 | mutable.put("1", 1); 69 | mutable.put("2", 2); 70 | mutable.put("3", 3); 71 | 72 | ImmutableMap immutableMap = new ImmutableHashMap<>(mutable); 73 | 74 | assertFalse(immutableMap.notContainsKey("1")); 75 | assertFalse(immutableMap.notContainsKey("2")); 76 | assertFalse(immutableMap.notContainsKey("3")); 77 | 78 | assertFalse(immutableMap.notContainsValue(1)); 79 | assertFalse(immutableMap.notContainsValue(2)); 80 | assertFalse(immutableMap.notContainsValue(3)); 81 | 82 | assertTrue(immutableMap.notContainsKey("123")); 83 | assertTrue(immutableMap.notContainsKey("1123")); 84 | assertTrue(immutableMap.notContainsKey(12331)); 85 | 86 | assertTrue(immutableMap.notContainsValue(3123)); 87 | assertTrue(immutableMap.notContainsValue(315123)); 88 | assertTrue(immutableMap.notContainsValue("dasdas")); 89 | } 90 | 91 | @Test 92 | void concatWithOverride() { 93 | Map mutable1 = new HashMap<>(); 94 | mutable1.put("1", 1); 95 | mutable1.put("2", 2); 96 | mutable1.put("3", 3); 97 | 98 | ImmutableMap map1 = new ImmutableHashMap<>(mutable1); 99 | 100 | Map mutable2 = new HashMap<>(); 101 | mutable2.put("4", 1); 102 | mutable2.put("5", 2); 103 | mutable2.put("1", 3); 104 | 105 | ImmutableMap map2 = new ImmutableHashMap<>(mutable2); 106 | 107 | ImmutableMap concat = map1.concatWithOverride(map2); 108 | assertEquals(5, concat.size()); 109 | assertEquals(3, concat.get("1")); 110 | assertEquals(2, concat.get("2")); 111 | assertEquals(3, concat.get("3")); 112 | assertEquals(1, concat.get("4")); 113 | assertEquals(2, concat.get("5")); 114 | 115 | concat = map2.concatWithOverride(map1); 116 | assertEquals(5, concat.size()); 117 | assertEquals(1, concat.get("1")); 118 | assertEquals(2, concat.get("2")); 119 | assertEquals(3, concat.get("3")); 120 | assertEquals(1, concat.get("4")); 121 | assertEquals(2, concat.get("5")); 122 | } 123 | 124 | @Test 125 | void concatWithoutOverride() { 126 | Map mutable1 = new HashMap<>(); 127 | mutable1.put("1", 1); 128 | mutable1.put("2", 2); 129 | mutable1.put("3", 3); 130 | 131 | ImmutableMap map1 = new ImmutableHashMap<>(mutable1); 132 | 133 | Map mutable2 = new HashMap<>(); 134 | mutable2.put("4", 1); 135 | mutable2.put("5", 2); 136 | mutable2.put("1", 3); 137 | 138 | ImmutableMap map2 = new ImmutableHashMap<>(mutable2); 139 | 140 | ImmutableMap concat = map2.concatWithoutOverride(map1); 141 | assertEquals(5, concat.size()); 142 | assertEquals(3, concat.get("1")); 143 | assertEquals(2, concat.get("2")); 144 | assertEquals(3, concat.get("3")); 145 | assertEquals(1, concat.get("4")); 146 | assertEquals(2, concat.get("5")); 147 | 148 | concat = map1.concatWithoutOverride(map2); 149 | assertEquals(5, concat.size()); 150 | assertEquals(1, concat.get("1")); 151 | assertEquals(2, concat.get("2")); 152 | assertEquals(3, concat.get("3")); 153 | assertEquals(1, concat.get("4")); 154 | assertEquals(2, concat.get("5")); 155 | } 156 | 157 | @Test 158 | void concatWith() { 159 | Map mutable1 = new HashMap<>(); 160 | mutable1.put("1", 1); 161 | mutable1.put("2", 2); 162 | mutable1.put("3", 3); 163 | mutable1.put("4", 4); 164 | 165 | ImmutableMap map1 = new ImmutableHashMap<>(mutable1); 166 | 167 | Map mutable2 = new HashMap<>(); 168 | mutable2.put("1", 5); 169 | mutable2.put("2", 6); 170 | mutable2.put("3", 7); 171 | mutable2.put("4", 8); 172 | 173 | ImmutableMap map2 = new ImmutableHashMap<>(mutable2); 174 | 175 | ImmutableMap concat = 176 | map1.concatWith(map2, (key, oldVal, newVal) -> { 177 | if (Integer.parseInt(key) % 2 == 0) { 178 | return oldVal; 179 | } else { 180 | return newVal; 181 | } 182 | }); 183 | 184 | assertEquals(4, concat.size()); 185 | assertEquals(5, concat.get("1")); 186 | assertEquals(2, concat.get("2")); 187 | assertEquals(7, concat.get("3")); 188 | assertEquals(4, concat.get("4")); 189 | } 190 | 191 | @Test 192 | void keySet() { 193 | Map mutable = new HashMap<>(); 194 | mutable.put("1", 1); 195 | mutable.put("2", 2); 196 | mutable.put("3", 3); 197 | mutable.put("4", 4); 198 | 199 | ImmutableMap map = new ImmutableHashMap<>(mutable); 200 | 201 | ImmutableSet keySet = map.keySet(); 202 | 203 | assertEquals(4, keySet.size()); 204 | assertTrue(keySet.contains("1")); 205 | assertTrue(keySet.contains("2")); 206 | assertTrue(keySet.contains("3")); 207 | assertTrue(keySet.contains("4")); 208 | } 209 | 210 | @Test 211 | void values() { 212 | Map mutable = new HashMap<>(); 213 | mutable.put("1", 1); 214 | mutable.put("2", 2); 215 | mutable.put("3", 3); 216 | mutable.put("4", 4); 217 | 218 | ImmutableMap map = new ImmutableHashMap<>(mutable); 219 | ImmutableList values = map.values(); 220 | 221 | assertEquals(4, values.size()); 222 | assertTrue(values.contains(1)); 223 | assertTrue(values.contains(2)); 224 | assertTrue(values.contains(3)); 225 | assertTrue(values.contains(4)); 226 | } 227 | 228 | @Test 229 | void shouldBuildNewImmutableMapFromMutableMap() { 230 | Map mutable = new HashMap<>(); 231 | mutable.put("1", 1); 232 | mutable.put("2", 2); 233 | mutable.put("3", 3); 234 | mutable.put("4", 4); 235 | 236 | ImmutableMap map = new ImmutableHashMap<>(mutable); 237 | 238 | Map newMutable = map.toMutableMap(); 239 | 240 | assertEquals(4, map.size()); 241 | assertEquals(4, newMutable.size()); 242 | 243 | newMutable.clear(); 244 | 245 | assertEquals(4, map.size()); 246 | } 247 | 248 | @Test 249 | void equalsAndHashCode() { 250 | Map mutable = new HashMap<>(); 251 | mutable.put("1", 1); 252 | mutable.put("2", 2); 253 | mutable.put("3", 3); 254 | mutable.put("4", 4); 255 | 256 | ImmutableMap map1 = new ImmutableHashMap<>(mutable); 257 | ImmutableMap map2 = new ImmutableHashMap<>(mutable); 258 | mutable.remove("1"); 259 | ImmutableMap map3 = new ImmutableHashMap<>(mutable); 260 | 261 | assertEquals(map1, map2); 262 | assertEquals(map1.hashCode(), map2.hashCode()); 263 | assertNotEquals(map1, map3); 264 | } 265 | 266 | @Test 267 | void shouldNotEqualIfObjectsOfDifferentType() { 268 | Map mutable = new HashMap<>(); 269 | mutable.put("1", 1); 270 | mutable.put("2", 2); 271 | mutable.put("3", 3); 272 | mutable.put("4", 4); 273 | 274 | ImmutableMap map = new ImmutableHashMap<>(mutable); 275 | assertNotEquals(map, mutable); 276 | } 277 | 278 | @Test 279 | void shouldEqualIfObjectIsTheSame() { 280 | Map mutable = new HashMap<>(); 281 | mutable.put("1", 1); 282 | mutable.put("2", 2); 283 | mutable.put("3", 3); 284 | mutable.put("4", 4); 285 | 286 | ImmutableMap map = new ImmutableHashMap<>(mutable); 287 | assertEquals(map, map); 288 | } 289 | 290 | @Test 291 | void shouldReturnDefaultValueIfItIsNotPresent() { 292 | Map mutable = new HashMap<>(); 293 | mutable.put("1", 1); 294 | mutable.put("2", 2); 295 | mutable.put("3", 3); 296 | mutable.put("4", 4); 297 | 298 | ImmutableMap map = new ImmutableHashMap<>(mutable); 299 | 300 | assertEquals(1, map.getOrDefault("1", Integer.MAX_VALUE)); 301 | assertEquals(Integer.MAX_VALUE, map.getOrDefault("11", Integer.MAX_VALUE)); 302 | } 303 | 304 | @Test 305 | void containsPair() { 306 | Map mutable = new HashMap<>(); 307 | mutable.put("1", 1); 308 | mutable.put("2", 2); 309 | mutable.put("3", 3); 310 | mutable.put("4", 4); 311 | 312 | ImmutableMap map = new ImmutableHashMap<>(mutable); 313 | assertTrue(map.containsPair(Pair.of("1", 1))); 314 | assertFalse(map.containsPair(Pair.of("2", 1))); 315 | } 316 | 317 | @Test 318 | void notContainsPair() { 319 | Map mutable = new HashMap<>(); 320 | mutable.put("1", 1); 321 | mutable.put("2", 2); 322 | mutable.put("3", 3); 323 | mutable.put("4", 4); 324 | 325 | ImmutableMap map = new ImmutableHashMap<>(mutable); 326 | assertFalse(map.notContainsPair(Pair.of("1", 1))); 327 | assertTrue(map.notContainsPair(Pair.of("2", 1))); 328 | } 329 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/immutable/ImmutableHashSetTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 6 | import static org.junit.jupiter.api.Assertions.assertNotNull; 7 | import static org.junit.jupiter.api.Assertions.assertTrue; 8 | 9 | import java.util.Arrays; 10 | import java.util.HashSet; 11 | import java.util.stream.Collectors; 12 | import java.util.stream.Stream; 13 | import org.junit.jupiter.api.Test; 14 | 15 | class ImmutableHashSetTest { 16 | 17 | @Test 18 | void instantiateWithCloning() { 19 | HashSet hashSet = new HashSet<>(); 20 | int item1 = 1; 21 | int item2 = 2; 22 | int item3 = 3; 23 | int item4 = 4; 24 | 25 | hashSet.add(item1); 26 | hashSet.add(item2); 27 | hashSet.add(item3); 28 | hashSet.add(item4); 29 | hashSet.add(item4); 30 | 31 | ImmutableSet immutableSet = new ImmutableHashSet<>(hashSet); 32 | 33 | assertEquals(4, immutableSet.size()); 34 | assertTrue(immutableSet.contains(item1)); 35 | assertTrue(immutableSet.contains(item2)); 36 | assertTrue(immutableSet.contains(item3)); 37 | assertTrue(immutableSet.contains(item4)); 38 | 39 | hashSet.clear(); 40 | 41 | assertEquals(4, immutableSet.size()); 42 | } 43 | 44 | @Test 45 | void concatWith() { 46 | String item1 = "dasgsdfggdgsf"; 47 | String item2 = "123afdasgsdfgherjer"; 48 | String item3 = "123afda14fsdffgdfsdfh"; 49 | String item4 = "123adfgdfgfda14fsdffgfgdfgfdgh"; 50 | 51 | ImmutableSet immutableSet = new ImmutableHashSet<>( 52 | Stream.of(item1, item2, item3, item4).collect(Collectors.toSet()) 53 | ); 54 | 55 | String newItem1 = "1"; 56 | String newItem2 = "2"; 57 | String newItem3 = "3"; 58 | 59 | ImmutableSet concat = immutableSet.concatWith( 60 | Arrays.asList(newItem1, newItem2, newItem3) 61 | ); 62 | 63 | assertEquals(7, concat.size()); 64 | assertTrue(concat.contains(item1)); 65 | assertTrue(concat.contains(item2)); 66 | assertTrue(concat.contains(item3)); 67 | assertTrue(concat.contains(item4)); 68 | assertTrue(concat.contains(newItem1)); 69 | assertTrue(concat.contains(newItem2)); 70 | assertTrue(concat.contains(newItem3)); 71 | } 72 | 73 | @Test 74 | void map() { 75 | String item1 = "dasgsdfg"; 76 | String item2 = "123afdasgsdfg"; 77 | String item3 = "223afda14fsdffg"; 78 | String item4 = "323adfgdfgfda14fsdffg"; 79 | 80 | ImmutableSet immutableSet = new ImmutableHashSet<>( 81 | Stream.of(item1, item2, item3, item4).collect(Collectors.toSet()) 82 | ); 83 | 84 | ImmutableSet mapped = immutableSet.map(x -> x.substring(0, 1)); 85 | 86 | assertEquals(4, mapped.size()); 87 | assertTrue(mapped.contains(item1.substring(0, 1))); 88 | assertTrue(mapped.contains(item2.substring(0, 1))); 89 | assertTrue(mapped.contains(item3.substring(0, 1))); 90 | assertTrue(mapped.contains(item4.substring(0, 1))); 91 | } 92 | 93 | private String reverseString(String str) { 94 | StringBuilder builder = new StringBuilder(str.length()); 95 | for (int i = str.length() - 1; i >= 0; i--) { 96 | builder.append(str.charAt(i)); 97 | } 98 | return builder.toString(); 99 | } 100 | 101 | @Test 102 | void flatMap() { 103 | String item1 = "dasgsdfg"; 104 | String item2 = "123afdasgsdfg"; 105 | String item3 = "223afda14fsdffg"; 106 | String item4 = "323adfgdfgfda14fsdffg"; 107 | 108 | ImmutableSet immutableSet = new ImmutableHashSet<>( 109 | Stream.of(item1, item2, item3, item4).collect(Collectors.toSet()) 110 | ); 111 | 112 | ImmutableSet flatMapped = 113 | immutableSet.flatMap(x -> Arrays.asList(x, reverseString(x))); 114 | 115 | assertEquals(8, flatMapped.size()); 116 | assertTrue(flatMapped.contains(item1)); 117 | assertTrue(flatMapped.contains(reverseString(item1))); 118 | assertTrue(flatMapped.contains(item2)); 119 | assertTrue(flatMapped.contains(reverseString(item2))); 120 | assertTrue(flatMapped.contains(item3)); 121 | assertTrue(flatMapped.contains(reverseString(item3))); 122 | assertTrue(flatMapped.contains(item4)); 123 | assertTrue(flatMapped.contains(reverseString(item4))); 124 | } 125 | 126 | @Test 127 | void filter() { 128 | String item1 = "dasgsdfg"; 129 | String item2 = "123afdasgsdfg"; 130 | String item3 = "223afda14fsdffg"; 131 | String item4 = "323adfgdfgfda14fsdffg"; 132 | 133 | ImmutableSet immutableSet = new ImmutableHashSet<>( 134 | Stream.of(item1, item2, item3, item4).collect(Collectors.toSet()) 135 | ); 136 | 137 | ImmutableSet filtered = immutableSet.filter( 138 | x -> x.startsWith("d") || x.startsWith("3") 139 | ); 140 | 141 | assertEquals(2, filtered.size()); 142 | assertTrue(filtered.contains(item1)); 143 | assertTrue(filtered.contains(item4)); 144 | } 145 | 146 | @Test 147 | void shouldCreateImmutableListFromTheSet() { 148 | String item1 = "dasgsdfg"; 149 | String item2 = "123afdasgsdfg"; 150 | String item3 = "223afda14fsdffg"; 151 | String item4 = "323adfgdfgfda14fsdffg"; 152 | 153 | ImmutableSet immutableSet = new ImmutableHashSet<>( 154 | Stream.of(item1, item2, item3, item4).collect(Collectors.toSet()) 155 | ); 156 | 157 | ImmutableList list = immutableSet.toList(); 158 | 159 | assertEquals(4, list.size()); 160 | assertTrue(list.contains(item1)); 161 | assertTrue(list.contains(item2)); 162 | assertTrue(list.contains(item3)); 163 | assertTrue(list.contains(item4)); 164 | } 165 | 166 | @Test 167 | void shouldReturnTheEqualImmutableSet() { 168 | String item1 = "dasgsdfg"; 169 | String item2 = "123afdasgsdfg"; 170 | String item3 = "223afda14fsdffg"; 171 | String item4 = "323adfgdfgfda14fsdffg"; 172 | 173 | ImmutableSet immutableSet = new ImmutableHashSet<>( 174 | Stream.of(item1, item2, item3, item4).collect(Collectors.toSet()) 175 | ); 176 | 177 | ImmutableSet set = immutableSet.toSet(); 178 | 179 | assertEquals(immutableSet, set); 180 | } 181 | 182 | @Test 183 | void parallelStream() { 184 | String item1 = "dasgsdfg"; 185 | String item2 = "123afdasgsdfg"; 186 | String item3 = "223afda14fsdffg"; 187 | String item4 = "323adfgdfgfda14fsdffg"; 188 | 189 | ImmutableSet immutableSet = new ImmutableHashSet<>( 190 | Stream.of(item1, item2, item3, item4).collect(Collectors.toSet()) 191 | ); 192 | 193 | Stream stream = immutableSet.parallelStream(); 194 | 195 | assertTrue(stream.isParallel()); 196 | } 197 | 198 | @Test 199 | void stream() { 200 | String item1 = "dasgsdfg"; 201 | String item2 = "123afdasgsdfg"; 202 | String item3 = "223afda14fsdffg"; 203 | String item4 = "323adfgdfgfda14fsdffg"; 204 | 205 | ImmutableSet immutableSet = new ImmutableHashSet<>( 206 | Stream.of(item1, item2, item3, item4).collect(Collectors.toSet()) 207 | ); 208 | 209 | Stream stream = immutableSet.stream(); 210 | 211 | assertFalse(stream.isParallel()); 212 | } 213 | 214 | @Test 215 | void hashCodeTest() { 216 | String item1 = "dasgsdfg"; 217 | String item2 = "123afdasgsdfg"; 218 | String item3 = "223afda14fsdffg"; 219 | String item4 = "323adfgdfgfda14fsdffg"; 220 | 221 | ImmutableSet immutableSet1 = new ImmutableHashSet<>( 222 | Stream.of(item1, item2, item3, item4).collect(Collectors.toSet()) 223 | ); 224 | ImmutableSet immutableSet2 = new ImmutableHashSet<>( 225 | Stream.of(item1, item2, item3, item4).collect(Collectors.toSet()) 226 | ); 227 | 228 | assertEquals(immutableSet1.hashCode(), immutableSet2.hashCode()); 229 | } 230 | 231 | @Test 232 | void shouldReturnNonNullString() { 233 | String item1 = "dasgsdfg"; 234 | String item2 = "123afdasgsdfg"; 235 | String item3 = "223afda14fsdffg"; 236 | String item4 = "323adfgdfgfda14fsdffg"; 237 | 238 | ImmutableSet immutableSet = new ImmutableHashSet<>( 239 | Stream.of(item1, item2, item3, item4).collect(Collectors.toSet()) 240 | ); 241 | assertNotNull(immutableSet.toString()); 242 | } 243 | 244 | @Test 245 | void equalsAndNotEquals() { 246 | String item1 = "dasgsdfg"; 247 | String item2 = "123afdasgsdfg"; 248 | String item3 = "223afda14fsdffg"; 249 | String item4 = "323adfgdfgfda14fsdffg"; 250 | 251 | ImmutableSet immutableSet1 = new ImmutableHashSet<>( 252 | Stream.of(item1, item2, item3, item4).collect(Collectors.toSet()) 253 | ); 254 | ImmutableSet immutableSet2 = new ImmutableHashSet<>( 255 | Stream.of(item1, item2, item3, item4).collect(Collectors.toSet()) 256 | ); 257 | ImmutableSet immutableSet3 = new ImmutableHashSet<>( 258 | Stream.of(item1, item2, item3).collect(Collectors.toSet()) 259 | ); 260 | 261 | assertEquals(immutableSet1, immutableSet2); 262 | assertNotEquals(immutableSet1, immutableSet3); 263 | } 264 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/immutable/ImmutableTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import org.junit.jupiter.api.Test; 12 | 13 | class ImmutableTest { 14 | 15 | @Test 16 | void emptyList() { 17 | ImmutableList list = Immutable.emptyList(); 18 | assertTrue(list.isEmpty()); 19 | assertFalse(list.isNotEmpty()); 20 | } 21 | 22 | @Test 23 | void emptySet() { 24 | ImmutableSet set = Immutable.emptySet(); 25 | assertTrue(set.isEmpty()); 26 | assertFalse(set.isNotEmpty()); 27 | } 28 | 29 | @Test 30 | void listOf() { 31 | int item1 = 1312; 32 | int item2 = 1241312; 33 | int item3 = -211312; 34 | int item4 = 233; 35 | 36 | ImmutableList list1 = Immutable.listOf(item1, item2, item3, item4); 37 | ImmutableList list2 = Immutable.listOf(Arrays.asList(item1, item2, item3, item4)); 38 | ImmutableList list3 = Immutable.listOf(Immutable.emptyList()); 39 | 40 | assertEquals(list1, list2); 41 | assertTrue(list3.isEmpty()); 42 | assertEquals(4, list1.size()); 43 | assertEquals(item1, list1.get(0)); 44 | assertEquals(item2, list1.get(1)); 45 | assertEquals(item3, list1.get(2)); 46 | assertEquals(item4, list1.get(3)); 47 | } 48 | 49 | @Test 50 | void setOf() { 51 | String item1 = "dasf"; 52 | String item2 = "12313"; 53 | String item3 = "12@@@@313"; 54 | String item4 = "1231!!!!!!3"; 55 | 56 | ImmutableSet set1 = Immutable.setOf(item1, item2, item3, item4); 57 | ImmutableSet set2 = Immutable.setOf(Arrays.asList(item1, item2, item3, item4)); 58 | ImmutableSet set3 = Immutable.setOf(Immutable.emptyList()); 59 | 60 | assertEquals(set1, set2); 61 | assertTrue(set3.isEmpty()); 62 | assertEquals(4, set1.size()); 63 | assertTrue(set1.contains(item1)); 64 | assertTrue(set1.contains(item2)); 65 | assertTrue(set1.contains(item3)); 66 | assertTrue(set1.contains(item4)); 67 | } 68 | 69 | @Test 70 | void mapOfWithJavaNativeMap() { 71 | Map mutable = new HashMap<>(); 72 | mutable.put("1", 1); 73 | mutable.put("2", 2); 74 | mutable.put("3", 3); 75 | mutable.put("4", 4); 76 | 77 | ImmutableMap map1 = Immutable.mapOf(mutable); 78 | ImmutableMap map2 = Immutable.mapOf(Collections.emptyMap()); 79 | 80 | assertTrue(map2.isEmpty()); 81 | 82 | assertEquals(4, map1.size()); 83 | assertEquals(1, map1.get("1")); 84 | assertEquals(2, map1.get("2")); 85 | assertEquals(3, map1.get("3")); 86 | assertEquals(4, map1.get("4")); 87 | 88 | mutable.clear(); 89 | 90 | assertEquals(4, map1.size()); 91 | assertEquals(1, map1.get("1")); 92 | assertEquals(2, map1.get("2")); 93 | assertEquals(3, map1.get("3")); 94 | assertEquals(4, map1.get("4")); 95 | } 96 | 97 | @Test 98 | void mapOfWithPairs() { 99 | ImmutableList> list = 100 | Immutable.listOf( 101 | Pair.of("1", 1), 102 | Pair.of("2", 2), 103 | Pair.of("3", 3), 104 | Pair.of("4", 4) 105 | ); 106 | 107 | ImmutableMap map1 = Immutable.mapOf(list); 108 | ImmutableMap map2 = Immutable.mapOf(Immutable.emptyList()); 109 | 110 | assertTrue(map2.isEmpty()); 111 | 112 | assertEquals(4, map1.size()); 113 | assertEquals(1, map1.get("1")); 114 | assertEquals(2, map1.get("2")); 115 | assertEquals(3, map1.get("3")); 116 | assertEquals(4, map1.get("4")); 117 | } 118 | 119 | @Test 120 | void mapOf2() { 121 | ImmutableMap map = Immutable.mapOf("1", 1); 122 | 123 | assertEquals(1, map.size()); 124 | assertEquals(1, map.get("1")); 125 | } 126 | 127 | 128 | @Test 129 | void mapOf4() { 130 | ImmutableMap map = Immutable.mapOf( 131 | "1", 1, 132 | "2", 2 133 | ); 134 | 135 | assertEquals(2, map.size()); 136 | assertEquals(1, map.get("1")); 137 | assertEquals(2, map.get("2")); 138 | } 139 | 140 | @Test 141 | void mapOf6() { 142 | ImmutableMap map = Immutable.mapOf( 143 | "1", 1, 144 | "2", 2, 145 | "3", 3 146 | ); 147 | 148 | assertEquals(3, map.size()); 149 | assertEquals(1, map.get("1")); 150 | assertEquals(2, map.get("2")); 151 | assertEquals(3, map.get("3")); 152 | } 153 | 154 | @Test 155 | void mapOf8() { 156 | ImmutableMap map = Immutable.mapOf( 157 | "1", 1, 158 | "2", 2, 159 | "3", 3, 160 | "4", 4 161 | ); 162 | 163 | assertEquals(4, map.size()); 164 | assertEquals(1, map.get("1")); 165 | assertEquals(2, map.get("2")); 166 | assertEquals(3, map.get("3")); 167 | assertEquals(4, map.get("4")); 168 | } 169 | 170 | @Test 171 | void mapOf10() { 172 | ImmutableMap map = Immutable.mapOf( 173 | "1", 1, 174 | "2", 2, 175 | "3", 3, 176 | "4", 4, 177 | "5", 5 178 | ); 179 | 180 | assertEquals(5, map.size()); 181 | assertEquals(1, map.get("1")); 182 | assertEquals(2, map.get("2")); 183 | assertEquals(3, map.get("3")); 184 | assertEquals(4, map.get("4")); 185 | assertEquals(5, map.get("5")); 186 | } 187 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/immutable/PairImplTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.immutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | class PairImplTest { 9 | 10 | @Test 11 | void shouldReturnThePassedArguments() { 12 | String key = "fsdf"; 13 | int value = 5235; 14 | Pair pair = new PairImpl<>(key, value); 15 | assertEquals(key, pair.getKey()); 16 | assertEquals(value, pair.getValue()); 17 | } 18 | 19 | @Test 20 | void equalsShouldReturnTrueIfKeyAndValueAreTheSame() { 21 | int key = 24124; 22 | String value = "fsdaf"; 23 | Pair pair1 = new PairImpl<>(key, value); 24 | Pair pair2 = new PairImpl<>(key, value); 25 | assertEquals(pair1, pair2); 26 | assertEquals(pair1.hashCode(), pair2.hashCode()); 27 | } 28 | 29 | @Test 30 | void equalsShouldReturnFalseIfKeysAreNotTheSame() { 31 | int key = 24124; 32 | String value = "fsdaf"; 33 | Pair pair1 = new PairImpl<>(key, value); 34 | Pair pair2 = new PairImpl<>(key + 1, value); 35 | assertNotEquals(pair1, pair2); 36 | } 37 | 38 | @Test 39 | void equalsShouldReturnFalseIfValuesAreNotTheSame() { 40 | int key = 24124; 41 | String value = "fsdaf"; 42 | Pair pair1 = new PairImpl<>(key, value); 43 | Pair pair2 = new PairImpl<>(key, value + "414"); 44 | assertNotEquals(pair1, pair2); 45 | } 46 | 47 | @Test 48 | void equalsShouldReturnFalseIfKeysAndValuesAreNotTheSame() { 49 | int key = 24124; 50 | String value = "fsdaf"; 51 | Pair pair1 = new PairImpl<>(key, value); 52 | Pair pair2 = new PairImpl<>(key + 123, value + "414"); 53 | assertNotEquals(pair1, pair2); 54 | } 55 | 56 | @Test 57 | void equalsShouldReturnFalseIfComparingWithDifferentClass() { 58 | assertNotEquals(new PairImpl<>("1", "341"), "1"); 59 | } 60 | 61 | @Test 62 | void shouldReturnCorrectToStringRepresentation() { 63 | int key = 24124; 64 | String value = "fsdfasdfdsfaf"; 65 | Pair pair = new PairImpl<>(key, value); 66 | assertEquals(String.format("(key=%s; value=%s)", key, value), pair.toString()); 67 | } 68 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/mutable/MutableBooleanTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertFalse; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | class MutableBooleanTest { 9 | 10 | @Test 11 | void testMutability() { 12 | MutableBoolean mutableBoolean = new MutableBoolean(false); 13 | assertFalse(mutableBoolean.isValue()); 14 | 15 | mutableBoolean.setValue(true); 16 | assertTrue(mutableBoolean.isValue()); 17 | } 18 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/mutable/MutableByteTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class MutableByteTest { 8 | 9 | @Test 10 | void testMutability() { 11 | MutableByte mutableByte = new MutableByte((byte) 2); 12 | assertEquals(2, mutableByte.getValue()); 13 | 14 | mutableByte.setValue((byte) 3); 15 | assertEquals(3, mutableByte.getValue()); 16 | } 17 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/mutable/MutableCharTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class MutableCharTest { 8 | 9 | @Test 10 | void testMutability() { 11 | MutableChar mutableChar = new MutableChar('a'); 12 | assertEquals('a', mutableChar.getValue()); 13 | 14 | mutableChar.setValue('b'); 15 | assertEquals('b', mutableChar.getValue()); 16 | } 17 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/mutable/MutableDoubleTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class MutableDoubleTest { 8 | 9 | @Test 10 | void testMutability() { 11 | MutableDouble mutableDouble = new MutableDouble(7.0); 12 | assertEquals(7.0, mutableDouble.getValue(), 0.0001); 13 | 14 | mutableDouble.setValue(10.0); 15 | assertEquals(10.0, mutableDouble.getValue(), 0.0001); 16 | } 17 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/mutable/MutableFloatTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class MutableFloatTest { 8 | 9 | @Test 10 | void testMutability() { 11 | MutableFloat mutableFloat = new MutableFloat(2.0F); 12 | assertEquals(2.0F, mutableFloat.getValue(), 0.0001); 13 | 14 | mutableFloat.setValue(3.0F); 15 | assertEquals(3.0F, mutableFloat.getValue(), 0.0001); 16 | } 17 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/mutable/MutableIntTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class MutableIntTest { 8 | 9 | @Test 10 | void testMutability() { 11 | MutableInt mutableInt = new MutableInt(Integer.MAX_VALUE); 12 | assertEquals(Integer.MAX_VALUE, mutableInt.getValue()); 13 | 14 | mutableInt.setValue(Integer.MIN_VALUE); 15 | assertEquals(Integer.MIN_VALUE, mutableInt.getValue()); 16 | } 17 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/mutable/MutableLongTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class MutableLongTest { 8 | 9 | @Test 10 | void testMutability() { 11 | MutableLong mutableLong = new MutableLong(Long.MAX_VALUE); 12 | assertEquals(Long.MAX_VALUE, mutableLong.getValue()); 13 | 14 | mutableLong.setValue(Long.MAX_VALUE - 1); 15 | assertEquals(Long.MAX_VALUE - 1, mutableLong.getValue()); 16 | } 17 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/mutable/MutableShortTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class MutableShortTest { 8 | 9 | @Test 10 | void testMutability() { 11 | MutableShort mutableShort = new MutableShort((short) 1); 12 | assertEquals(1, mutableShort.getValue()); 13 | 14 | mutableShort.setValue((short) 2); 15 | assertEquals(2, mutableShort.getValue()); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/collection/mutable/MutableValueTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.collection.mutable; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class MutableValueTest { 8 | 9 | @Test 10 | void testMutability() { 11 | MutableValue stringMutable = new MutableValue<>("aa"); 12 | assertEquals("aa", stringMutable.getValue()); 13 | 14 | stringMutable.setValue("bb"); 15 | assertEquals("bb", stringMutable.getValue()); 16 | } 17 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/lambda/CachedResultCheckedSupplierTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.lambda; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class CachedResultCheckedSupplierTest { 8 | 9 | @Test 10 | void testGetCalculatesValueOnlyOnce() { 11 | CheckedSupplier checkedSupplier = 12 | new CheckedSupplier() { 13 | private int value = 1; 14 | 15 | @Override 16 | public Integer get() throws IllegalArgumentException { 17 | return value++; 18 | } 19 | }; 20 | 21 | CheckedSupplier cachedResultCheckedSupplier = 22 | new CachedResultCheckedSupplier<>(checkedSupplier); 23 | 24 | assertEquals(1, cachedResultCheckedSupplier.get()); 25 | assertEquals(1, cachedResultCheckedSupplier.get()); 26 | assertEquals(1, cachedResultCheckedSupplier.get()); 27 | assertEquals(1, cachedResultCheckedSupplier.get()); 28 | assertEquals(1, cachedResultCheckedSupplier.get()); 29 | assertEquals(1, cachedResultCheckedSupplier.get()); 30 | } 31 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/lambda/CachedResultSupplierTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.lambda; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import java.util.function.Supplier; 6 | import org.junit.jupiter.api.Test; 7 | 8 | class CachedResultSupplierTest { 9 | 10 | @Test 11 | void testGetCalculatesValueOnlyOnce() { 12 | Supplier supplier = new Supplier() { 13 | private int value = 1; 14 | 15 | @Override 16 | public Integer get() { 17 | return value++; 18 | } 19 | }; 20 | 21 | Supplier cachedSupplier = new CachedResultSupplier<>(supplier); 22 | 23 | assertEquals(1, cachedSupplier.get()); 24 | assertEquals(1, cachedSupplier.get()); 25 | assertEquals(1, cachedSupplier.get()); 26 | assertEquals(1, cachedSupplier.get()); 27 | assertEquals(1, cachedSupplier.get()); 28 | assertEquals(1, cachedSupplier.get()); 29 | assertEquals(1, cachedSupplier.get()); 30 | assertEquals(1, cachedSupplier.get()); 31 | } 32 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/measure/MeasureConverterTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.measure; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | /** 8 | * Test cases for {@linkplain MeasureConverter}. 9 | * 10 | * @see MeasureConverter 11 | */ 12 | class MeasureConverterTest { 13 | 14 | @Test 15 | void millisToNanos() { 16 | final long millis = 23123; 17 | final long nanos = millis * 1000 * 1000; 18 | long convertedNanos = MeasureConverter.millisToNanos(millis); 19 | assertEquals(nanos, convertedNanos); 20 | } 21 | 22 | @Test 23 | void nanosToMillis() { 24 | final long nanos = 124124243469L; 25 | final long millis = nanos / 1000 / 1000; 26 | long convertedMillis = MeasureConverter.nanosToMillis(nanos); 27 | assertEquals(millis, convertedMillis); 28 | } 29 | 30 | @Test 31 | void millisToSeconds() { 32 | final long millis = 12314124L; 33 | final long seconds = millis / 1000; 34 | long convertedSeconds = MeasureConverter.millisToSeconds(millis); 35 | assertEquals(seconds, convertedSeconds); 36 | } 37 | 38 | @Test 39 | void nanosToSeconds() { 40 | final long nanos = 123123135235235L; 41 | final long seconds = nanos / 1000 / 1000 / 1000; 42 | long convertedSeconds = MeasureConverter.nanosToSeconds(nanos); 43 | assertEquals(seconds, convertedSeconds); 44 | } 45 | 46 | @Test 47 | void secondsToMillis() { 48 | final long seconds = 6906456L; 49 | final long millis = seconds * 1000; 50 | long convertedMillis = MeasureConverter.secondsToMillis(seconds); 51 | assertEquals(millis, convertedMillis); 52 | } 53 | 54 | @Test 55 | void secondsToNanos() { 56 | final long seconds = 123; 57 | final long nanos = seconds * 1000 * 1000 * 1000; 58 | long convertedNanos = MeasureConverter.secondsToNanos(seconds); 59 | assertEquals(nanos, convertedNanos); 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/measure/MeasureTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.measure; 2 | 3 | import static com.kirekov.juu.measure.MeasureConverter.nanosToMillis; 4 | import static com.kirekov.juu.measure.MeasureConverter.secondsToMillis; 5 | import static org.awaitility.Awaitility.await; 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | import static org.junit.jupiter.api.Assertions.assertNotNull; 8 | import static org.junit.jupiter.api.Assertions.assertThrows; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | import com.kirekov.juu.lambda.Action; 12 | import java.util.function.Supplier; 13 | import org.awaitility.Duration; 14 | import org.junit.jupiter.api.Test; 15 | 16 | /** 17 | * Test cases for {@linkplain Measure}. 18 | * 19 | * @see Measure 20 | */ 21 | class MeasureTest { 22 | 23 | @Test 24 | void throwsNullPointerIfActionIsNull() { 25 | assertThrows(NullPointerException.class, () -> Measure.executionTime((Supplier) null)); 26 | } 27 | 28 | @Test 29 | void throwsNullPointerIfSupplierIsNull() { 30 | assertThrows(NullPointerException.class, () -> Measure.executionTime((Action) null)); 31 | } 32 | 33 | @Test 34 | void executionTimeReturnsNotNull() { 35 | Measure measure1 = Measure.executionTime(() -> { 36 | }); 37 | Measure measure2 = Measure.executionTime(() -> 1); 38 | assertNotNull(measure1); 39 | assertNotNull(measure2); 40 | } 41 | 42 | @Test 43 | void inMillisMeasuresCorrectSupplier() { 44 | long millis = Measure.executionTime(() -> { 45 | await().pollDelay(Duration.ONE_SECOND).until(() -> true); 46 | return true; 47 | }).inMillis().getTime(); 48 | assertTrue(millis >= 1000); 49 | } 50 | 51 | @Test 52 | void inNanosMeasuresCorrectSupplier() { 53 | long nanos = Measure.executionTime(() -> { 54 | await().pollDelay(Duration.ONE_SECOND).until(() -> true); 55 | return true; 56 | }).inNanos().getTime(); 57 | assertTrue(nanosToMillis(nanos) >= 1000); 58 | } 59 | 60 | @Test 61 | void inSecondsMeasureCorrectSupplier() { 62 | long seconds = Measure.executionTime(() -> { 63 | await().pollDelay(Duration.ONE_SECOND).until(() -> true); 64 | return true; 65 | }).inSeconds().getTime(); 66 | assertTrue(secondsToMillis(seconds) >= 1000); 67 | } 68 | 69 | @Test 70 | void inMillisMeasuresCorrectAction() { 71 | long millis = Measure 72 | .executionTime(() -> await().pollDelay(Duration.ONE_SECOND).until(() -> true)).inMillis() 73 | .getTime(); 74 | assertTrue(millis >= 1000); 75 | } 76 | 77 | @Test 78 | void inNanosMeasuresCorrectAction() { 79 | long nanos = Measure 80 | .executionTime(() -> await().pollDelay(Duration.ONE_SECOND).until(() -> true)).inNanos() 81 | .getTime(); 82 | assertTrue(nanosToMillis(nanos) >= 1000); 83 | } 84 | 85 | @Test 86 | void inNanosReturnsCorrectResult() { 87 | final int result = 1103123; 88 | ExecutionResult executionResult = 89 | Measure.executionTime(() -> result) 90 | .inNanos(); 91 | assertEquals(result, executionResult.getResult()); 92 | } 93 | 94 | @Test 95 | void inMillisReturnsCorrectResult() { 96 | final String result = "SOME STRING RESULT"; 97 | ExecutionResult executionResult = 98 | Measure.executionTime(() -> result) 99 | .inMillis(); 100 | assertEquals(result, executionResult.getResult()); 101 | } 102 | 103 | @Test 104 | void inMillisSetsCorrectMeasureUnit() { 105 | ExecutionResult executionResult = 106 | Measure.executionTime(() -> { 107 | }).inMillis(); 108 | assertEquals(MeasureUnit.MILLIS, executionResult.getMeasureUnit()); 109 | } 110 | 111 | @Test 112 | void inNanosSetsCorrectMeasureUnit() { 113 | ExecutionResult executionResult = 114 | Measure.executionTime(() -> 1) 115 | .inNanos(); 116 | assertEquals(MeasureUnit.NANOS, executionResult.getMeasureUnit()); 117 | } 118 | 119 | @Test 120 | void inSecondsReturnsCorrectResult() { 121 | final int result = 12314; 122 | ExecutionResult executionResult = 123 | Measure.executionTime(() -> result) 124 | .inSeconds(); 125 | assertEquals(result, executionResult.getResult()); 126 | } 127 | 128 | @Test 129 | void inSecondsSetsCorrectMeasureUnit() { 130 | ExecutionResult executionResult = 131 | Measure.executionTime(() -> { 132 | }).inSeconds(); 133 | assertEquals(MeasureUnit.SECONDS, executionResult.getMeasureUnit()); 134 | } 135 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/measure/ProfilerTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.measure; 2 | 3 | import static com.kirekov.juu.measure.MeasureConverter.millisToNanos; 4 | import static com.kirekov.juu.measure.MeasureConverter.secondsToMillis; 5 | import static org.awaitility.Awaitility.await; 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | import static org.junit.jupiter.api.Assertions.assertTrue; 8 | 9 | import org.awaitility.Duration; 10 | import org.junit.jupiter.api.Test; 11 | 12 | class ProfilerTest { 13 | 14 | @Test 15 | void ifDefinedMeasureUnitIsCorrect() { 16 | Profiler profilerMillis = Profiler.startMeasuringInMillis(); 17 | Profiler profilerNanos = Profiler.startMeasuringInNanos(); 18 | Profiler profilerSeconds = Profiler.startMeasuringInSeconds(); 19 | 20 | assertEquals(MeasureUnit.MILLIS, profilerMillis.getMeasureUnit()); 21 | assertEquals(MeasureUnit.NANOS, profilerNanos.getMeasureUnit()); 22 | assertEquals(MeasureUnit.SECONDS, profilerSeconds.getMeasureUnit()); 23 | } 24 | 25 | @Test 26 | void multipleStopsReturnTheSame() { 27 | Profiler profilerMillis = Profiler.startMeasuringInMillis(); 28 | long millisTime1 = profilerMillis.stopMeasuring(); 29 | long millisTime2 = profilerMillis.stopMeasuring(); 30 | 31 | Profiler profilerNanos = Profiler.startMeasuringInNanos(); 32 | long nanosTime1 = profilerNanos.stopMeasuring(); 33 | long nanosTime2 = profilerNanos.stopMeasuring(); 34 | 35 | Profiler profilerSeconds = Profiler.startMeasuringInSeconds(); 36 | long secondsTime1 = profilerSeconds.stopMeasuring(); 37 | long secondsTime2 = profilerSeconds.stopMeasuring(); 38 | 39 | assertEquals(millisTime1, millisTime2); 40 | assertEquals(nanosTime1, nanosTime2); 41 | assertEquals(secondsTime1, secondsTime2); 42 | } 43 | 44 | @Test 45 | void ifMeasuringInMillisIsCorrect() { 46 | Profiler profilerMillis = Profiler.startMeasuringInMillis(); 47 | await().pollDelay(Duration.ONE_SECOND).until(() -> true); 48 | long res = profilerMillis.stopMeasuring(); 49 | assertTrue(res >= 1000); 50 | } 51 | 52 | @Test 53 | void ifMeasuringInNanosIsCorrect() { 54 | Profiler profilerMillis = Profiler.startMeasuringInNanos(); 55 | await().pollDelay(Duration.ONE_SECOND).until(() -> true); 56 | long res = profilerMillis.stopMeasuring(); 57 | assertTrue(millisToNanos(res) >= 1000); 58 | } 59 | 60 | @Test 61 | void ifMeasuringInSecondsIsCorrect() { 62 | Profiler profilerSeconds = Profiler.startMeasuringInSeconds(); 63 | await().pollDelay(Duration.ONE_SECOND).until(() -> true); 64 | long res = profilerSeconds.stopMeasuring(); 65 | assertTrue(secondsToMillis(res) >= 1000); 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /src/test/java/com/kirekov/juu/monad/TryTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.juu.monad; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 4 | import static org.junit.jupiter.api.Assertions.assertEquals; 5 | import static org.junit.jupiter.api.Assertions.assertFalse; 6 | import static org.junit.jupiter.api.Assertions.assertThrows; 7 | import static org.junit.jupiter.api.Assertions.assertTrue; 8 | import static org.mockito.Mockito.mock; 9 | import static org.mockito.Mockito.times; 10 | import static org.mockito.Mockito.verify; 11 | 12 | import com.kirekov.juu.lambda.CheckedSupplier; 13 | import java.util.NoSuchElementException; 14 | import java.util.Optional; 15 | import org.junit.jupiter.api.Test; 16 | import org.junit.jupiter.api.function.ThrowingSupplier; 17 | 18 | /** 19 | * Test cases for {@linkplain Try}. 20 | * 21 | * @see Try 22 | */ 23 | class TryTest { 24 | 25 | @Test 26 | void shouldReturnTheSuccessfulMonad() { 27 | Try t = Try.success("value"); 28 | assertEquals("value", t.orElseThrow()); 29 | } 30 | 31 | @Test 32 | void shouldThrowExceptionIfContainerIsEmpty() { 33 | Try t = Try.error(); 34 | assertThrows(NoSuchElementException.class, t::orElseThrow); 35 | } 36 | 37 | @Test 38 | void shouldActLazily() { 39 | Runnable runnable = mock(Runnable.class); 40 | Try t = 41 | Try.of(() -> { 42 | runnable.run(); 43 | return 1; 44 | }).map(val -> { 45 | runnable.run(); 46 | return val + 10; 47 | }).flatMap(val -> { 48 | runnable.run(); 49 | return Try.success(val + 12); 50 | }).filter(val -> { 51 | runnable.run(); 52 | return val > 0; 53 | }); 54 | verify(runnable, times(0)).run(); 55 | assertDoesNotThrow((ThrowingSupplier) t::orElseThrow); 56 | verify(runnable, times(4)).run(); 57 | } 58 | 59 | @Test 60 | void shouldCalculateTheValueIfAllMappingsSucceed() { 61 | Try t = Try.of(() -> 1) 62 | .map(val -> val + 10) 63 | .map(Object::toString) 64 | .map(val -> val + "12"); 65 | assertEquals("1112", t.orElseThrow()); 66 | } 67 | 68 | @Test 69 | void shouldThrowTheExceptionIfAnyMappingFails() { 70 | Try t = Try.of(() -> 1) 71 | .map(val -> val + 10) 72 | .map(Object::toString) 73 | .map(val -> { 74 | throw new Exception(); 75 | }) 76 | .map(val -> val + "12"); 77 | assertThrows( 78 | IllegalArgumentException.class, 79 | () -> t.orElseThrow(IllegalArgumentException::new) 80 | ); 81 | } 82 | 83 | @Test 84 | void shouldCalculateTheValueIfAllFlatMappingsSucceed() { 85 | Try t = Try.of(() -> 1) 86 | .flatMap(val -> Try.success(val + 10)) 87 | .flatMap(val -> Try.of(val::toString)) 88 | .flatMap(val -> Try.success(val + "12")); 89 | assertEquals( 90 | "1112", 91 | t.orElseThrow() 92 | ); 93 | } 94 | 95 | @Test 96 | void shouldThrowTheExceptionIfAnyFlatMappingFails() { 97 | Try t = Try.of(() -> 1) 98 | .flatMap(val -> Try.success(val + 10)) 99 | .flatMap(val -> Try.of(val::toString)) 100 | .flatMap(val -> Try.of(() -> { 101 | throw new IllegalStateException(); 102 | })) 103 | .flatMap(val -> Try.success(val + "12")); 104 | String result = t.orElseGet((reason) -> { 105 | assertTrue(reason instanceof IllegalStateException); 106 | return "default"; 107 | }); 108 | assertEquals("default", result); 109 | } 110 | 111 | @Test 112 | void shouldCalculateTheValueIfAllFilteringSucceed() { 113 | Try t = Try.success(56) 114 | .filter(val -> val > 0) 115 | .filter(val -> val < 100) 116 | .filter(val -> val % 2 == 0); 117 | assertEquals( 118 | 56, 119 | t.orElseGet(() -> 1) 120 | ); 121 | } 122 | 123 | @Test 124 | void shouldThrowTheExceptionIfAnyFilteringFails() { 125 | Try t = Try.success(56) 126 | .filter(val -> val > 0) 127 | .filter(val -> val < 100) 128 | .filter(val -> val % 2 == 0) 129 | .filter(val -> val < 30); 130 | assertEquals( 131 | 1, 132 | t.orElse(1) 133 | ); 134 | } 135 | 136 | @Test 137 | void shouldCalculateTheOrElseTryClauseIfThePreviousOneFails() { 138 | Try t = Try.of(() -> { 139 | throw new Exception(); 140 | }).orElseTry(() -> 56); 141 | assertEquals(56, t.orElseThrow()); 142 | } 143 | 144 | @Test 145 | void streamReturnsNotEmptyOne() { 146 | final int val = 444; 147 | Optional opt = 148 | Try.of(() -> val) 149 | .stream() 150 | .findFirst(); 151 | assertTrue(opt.isPresent()); 152 | assertEquals(val, opt.get()); 153 | } 154 | 155 | @Test 156 | void streamReturnsEmptyOne() { 157 | Optional opt = 158 | Try.of((CheckedSupplier) () -> { 159 | throw new Exception(); 160 | }).stream().findFirst(); 161 | assertFalse(opt.isPresent()); 162 | } 163 | } --------------------------------------------------------------------------------