{"id":50187958,"url":"https://github.com/solubris/typedtuples","last_synced_at":"2026-05-25T11:06:09.901Z","repository":{"id":39967237,"uuid":"266243409","full_name":"solubris/typedtuples","owner":"solubris","description":"Strongly typed tuple library for java - immutable and mutable implementations","archived":false,"fork":false,"pushed_at":"2026-03-08T12:45:55.000Z","size":579,"stargazers_count":8,"open_issues_count":4,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2026-03-08T14:46:53.089Z","etag":null,"topics":["immutable","java","java-8","mutable","streams","tuples"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/solubris.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-05-23T01:50:44.000Z","updated_at":"2026-03-08T12:45:57.000Z","dependencies_parsed_at":"2025-01-29T16:28:06.263Z","dependency_job_id":"20b2bc0f-8fdf-440a-b8e9-99921488976e","html_url":"https://github.com/solubris/typedtuples","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/solubris/typedtuples","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solubris%2Ftypedtuples","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solubris%2Ftypedtuples/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solubris%2Ftypedtuples/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solubris%2Ftypedtuples/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/solubris","download_url":"https://codeload.github.com/solubris/typedtuples/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solubris%2Ftypedtuples/sbom","scorecard":{"id":837294,"data":{"date":"2025-08-11","repo":{"name":"github.com/solubris/typedtuples","commit":"a98ad869175b5436bc648f92c176274d9c49b713"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.5,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/1 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql-analysis.yml:42","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql-analysis.yml:43","Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Warn: no topLevel permission defined: .github/workflows/codeql-analysis.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/solubris/typedtuples/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/solubris/typedtuples/ci.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:47: update your workflow using https://app.stepsecurity.io/secureworkflow/solubris/typedtuples/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:56: update your workflow using https://app.stepsecurity.io/secureworkflow/solubris/typedtuples/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:60: update your workflow using https://app.stepsecurity.io/secureworkflow/solubris/typedtuples/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:71: update your workflow using https://app.stepsecurity.io/secureworkflow/solubris/typedtuples/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:85: update your workflow using https://app.stepsecurity.io/secureworkflow/solubris/typedtuples/codeql-analysis.yml/master?enable=pin","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":10,"reason":"SAST tool detected","details":["Info: SAST configuration detected: CodeQL","Info: SAST configuration detected: Sonar","Warn: 0 commits out of 29 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T19:26:58.442Z","repository_id":39967237,"created_at":"2025-08-23T19:26:58.442Z","updated_at":"2025-08-23T19:26:58.442Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33471569,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-25T06:32:55.349Z","status":"ssl_error","status_checked_at":"2026-05-25T06:32:35.322Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["immutable","java","java-8","mutable","streams","tuples"],"created_at":"2026-05-25T11:06:09.117Z","updated_at":"2026-05-25T11:06:09.888Z","avatar_url":"https://github.com/solubris.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://github.com/solubris/typedtuples/actions/workflows/ci.yml/badge.svg)](https://github.com/solubris/typedtuples/actions/workflows/ci.yml)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=solubris_typedtuples\u0026metric=alert_status)](https://sonarcloud.io/dashboard?id=solubris_typedtuples)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.solubris/typedtuples/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.solubris/typedtuples)\n[![Javadocs](http://www.javadoc.io/badge/com.solubris/typedtuples.svg)](http://www.javadoc.io/doc/com.solubris/typedtuples)\n\n# TypedTuples\n\nStrongly typed tuple library for java\n\n## Install\n\n- Maven\n```xml\n        \u003cdependency\u003e\n          \u003cgroupId\u003ecom.solubris\u003c/groupId\u003e\n          \u003cartifactId\u003etypedtuples\u003c/artifactId\u003e\n          \u003cversion\u003e2.1\u003c/version\u003e\n        \u003c/dependency\u003e\n```\n- Gradle\n```groovy\n        implementation 'com.solubris:typedtuples:2.1'\n```\n\n## Example\n\n### Multivalued Computation\n\nJava streams don't handle computation of multiple values nicely, eg:\n- compute sum(N), sum(N^2)\n\nThis could be done using arrays as follows:\n\n```java\n        int[] result = IntStream.rangeClosed(1, 3)\n                .mapToObj(i -\u003e new int[]{i, i * i})\n                .reduce(\n                        new int[2],\n                        (l, r) -\u003e new int[]{l[0] + r[0], l[1] + r[1]}\n                );\n```\n\nWith tuples and a tuple accumulator, this can be done as follows:\n    \n```java\n        CoupleAccumulator\u003cInteger, Integer\u003e accumulator = Accumulator.of(Integer::sum, Integer::sum);\n        Couple\u003cInteger, Integer\u003e result = IntStream.rangeClosed(1, 3)\n                .mapToObj(i -\u003e ImmutableTuple.of(i, i * i))\n                .reduce(\n                        ImmutableTuple.of(0, 0),\n                        accumulator::combine\n                );\n```\n\n### Enrichment\n\nEnriching a stream of data can be done as follows:\n\n```java\n        List\u003cStringStats\u003e result = Stream.of(\"abc\", \"1234\", \"zzz\")\n                .map(ImmutableTuple::of)\n                .map(s -\u003e s.mapAndAdd(String::length))\n                .map(sl -\u003e sl.mapFirstAndAdd(this::isPalindrome))\n                .map(ImmutableTuple.to(StringStats::new))\n                .collect(Collectors.toList());\n```\n\n## Usage\n\nThere are 3 types of tuples which can be created from the builder classes as follows:\n\n```java\n        ImmutableTuple.of(0, 0);\n        MutableTuple.of(0, 0);\n        Accumulator.of(Integer::sum, Integer::sum);\n```\n\nThe api can the be explored from the results of these methods.\n\n## Strong typing\n\nThe tuples are never converted to the Object class.\n\nAlternative tuple libraries have an Object[] toArray();\nIf this is the requirement, then its better to use a List\\\u003cObject\u003e directly.\nNo need to complicate the solution with tuples.\n\n## Not Serializable\n\nTuples are only meant to be used as temporary holders for manipulation.\nSo they should not be used as fields in other classes which means they don't need to be serializable.\n\n## Not Comparable\n\nWithout knowing if the types are comparable, then the tuples also can't be comparable (without casting).\n\nHowever, some custom comparators are provided as follows:\n\n```java\n        list.sort(Couple.compareByAllFieldsInOrder());\n        list.sort(Couple.compareByAllFieldsInReverseOrder());\n        list.sort(Single.compareByAllFieldsInOrder(String::length));\n        list.sort(Single.compareByAllFieldsInOrder(Comparator.nullsLast(Integer::compareTo));\n```\n\n## English names\n\nNo names like tuple2 because they are not fluent.\n\n## Stream friendly\n\nDesigned for use in streams where tuples are especially useful for manipulating the intermediate values.\nMethods like map are especially useful in stream operations.\n\n## Java 14 Record types\n\nWill record types eliminate the need for tuples?\nLet's look at the example of returning a Couple of values.\n\n```java\n        public Couple\u003cString, String\u003e getNameValue() {\n            return ImmutableTuple.of(\"name\", \"value\");\n        }\n```\n        \nHow could this be done with record types?\n\n```java\n        record NameValue(String name, String value){}\n        public NameValue getNameValue() {\n            return new NameValue(\"name\", \"value\");\n        }\n```\n        \nSo record types would still require the creation of a separate definition, however that definition would be very concise.\nThis is probably suitable for method returns,\nbut still not suitable for multistage stream enrichment as every stage would require a record definition.\nOther limitations:\n\n- it's not possible to have mutable record types\n- record types don't support generics\n- record types wont have the rich api of tuples\n\nRecord types can be a good target for the result of enrichment, eg:\n\n```java\n        ImmutableTuple.of(\"name\", \"value\").transform(NameValue::new);\n```\n\n## Alternatives\n\n|                     | Arity  | Naming    | self contained     | Immutable          | Mutable            | Strongly Typed     | Accumulators       | Comparators        | Collectors         | Overloaded Builders | Primitives         | Nullable Values    |\n|---------------------|--------|-----------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------|---------------------|--------------------|--------------------|\n| TypeTuples          | 0 - 10 | English   | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |                    | :heavy_check_mark:  |                    | :heavy_check_mark: |\n| javatuples          | 1 - 10 | English   | :heavy_check_mark: | :heavy_check_mark: |                    |                    |                    |                    |                    |                     |                    | :heavy_check_mark: |\n| jooq tuples         | 0 - 16 | Numerical |                    | :heavy_check_mark: |                    |                    |                    |                    | :heavy_check_mark: |                     |                    | :heavy_check_mark: |\n| apache lang3        | 2 - 3  | English   |                    | :heavy_check_mark: | :heavy_check_mark: |                    |                    |                    |                    |                     |                    | :heavy_check_mark: |\n| eclipse collections | 2      | English   |                    | :heavy_check_mark: |                    |                    |                    |                    |                    |                     | :heavy_check_mark: | :heavy_check_mark: |\n| reactor utils       | 2 - 8  | Numerical |                    | :heavy_check_mark: |                    |                    |                    |                    |                    | :heavy_check_mark:  |                    |                    |\n\n- org.javatuples\n\nIn the Tuple base class it holds the values here:\n\n    private final Object[] valueArray;\n    private final List\u003cObject\u003e valueList;\n\nThis is in addition to the Pair class (for instance) values:\n\n    private final A val0;\n    private final B val1;\n\nThe valueList is populated with Arrays.asList(valueArray), so it doesn't copy all the values again.\nIn summary, every value is stored twice and there are two extra references for each tuple.\nSo for a Pair, that's 4 extra object references (or 200%).\n    \n- org.eclipse.collections.impl.tuple.Tuples\n\n- reactor.util.function.Tuples\n\n- org.apache.commons.lang3.tuple\n\n   - is comparable - uses reflection to work out which comparator\n\n- org.jooq.lambda.tuple\n\n   - toArray(), Comparable, Serializable, Cloneable\n   - concat, split, limit, skip, swap, map, mapAll\n   - collect tuples from multiple collectors\n   - not self contained\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolubris%2Ftypedtuples","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsolubris%2Ftypedtuples","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolubris%2Ftypedtuples/lists"}