{"id":18221894,"url":"https://github.com/karlicoss/autohash","last_synced_at":"2026-02-08T04:34:05.728Z","repository":{"id":57720707,"uuid":"56383860","full_name":"karlicoss/autohash","owner":"karlicoss","description":"AutoValue extension which speeds up `hashCode` calculation for immutable objects","archived":false,"fork":false,"pushed_at":"2016-12-30T13:49:21.000Z","size":114,"stargazers_count":5,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-11-08T23:36:20.152Z","etag":null,"topics":[],"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/karlicoss.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-04-16T13:03:36.000Z","updated_at":"2019-12-06T09:46:59.000Z","dependencies_parsed_at":"2022-09-26T21:41:07.896Z","dependency_job_id":null,"html_url":"https://github.com/karlicoss/autohash","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/karlicoss/autohash","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karlicoss%2Fautohash","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karlicoss%2Fautohash/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karlicoss%2Fautohash/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karlicoss%2Fautohash/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karlicoss","download_url":"https://codeload.github.com/karlicoss/autohash/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karlicoss%2Fautohash/sbom","scorecard":{"id":550451,"data":{"date":"2025-08-11","repo":{"name":"github.com/karlicoss/autohash","commit":"4b473dbad79fcf53246f54ff2173e74a410befd7"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"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":-1,"reason":"no workflows found","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":-1,"reason":"No tokens found","details":null,"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":"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":"Code-Review","score":0,"reason":"Found 0/25 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":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: gradle/wrapper/gradle-wrapper.jar:1"],"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":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"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":"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":"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":"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":"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":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE.txt: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":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 10 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-20T10:42:07.696Z","repository_id":57720707,"created_at":"2025-08-20T10:42:07.696Z","updated_at":"2025-08-20T10:42:07.696Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29220520,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-08T03:18:47.732Z","status":"ssl_error","status_checked_at":"2026-02-08T03:15:31.985Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-03T22:04:54.166Z","updated_at":"2026-02-08T04:34:05.665Z","avatar_url":"https://github.com/karlicoss.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/karlicoss/autohash.svg?branch=master)](https://travis-ci.org/karlicoss/autohash)  [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.karlicoss.auto.value/autohash/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.karlicoss.auto.value/autohash)\n\nTLDR: cache your hash!\n\nImagine you've got an immutable [AutoValue](https://github.com/google/auto/tree/master/value) entity. AutoValue generates `hashCode` for us, but if you pass your immutable\nobject around in your code, each time `hashCode` is called, it will be recomputed. Sounds like a waste of precious CPU given that the object is immutable, right?\n\nAutoHash is an extension which makes your immutable value classes cache their `hashCode` result in a thread safe and efficient manner.\n\nThe implementation is similar to `java.lang.String::hashCode`.\n\n# Usage\nFirst, beware that you should only use this if you know that your objects are logically immutable\n (e.g. `ArrayList` is totally mutable, but for your specific object you might have the contract of immutability).\n If immutability is the case though, just add the `@AutoHash` annotation to your value class definition. It's that simple!\n\n```java\n@AutoHash\n@AutoValue\nabstract class Person {\n    abstract String name();\n    abstract String passportNumber();\n    abstract List\u003cInteger\u003e genes();\n}\n```\n\n, and that's it! Now if you call `hashCode` on a `Person` multiple times, it will only be computed once\n(in worst case, once for each thread which uses your object).\n\n# Download\n\nThe library is published on Maven Central/JCenter.\n\nFor regular Java Maven/Gradle project, you just need the dependency `com.github.karlicoss.auto.value:autohash:\u003cversion\u003e`\n in `provided` configuration.\n\nFor Android, you're gonna need the [android-apt](https://bitbucket.org/hvisser/android-apt) plugin. \nYou need a dependency in `provided` and `apt` configurations:\n\n```groovy\ndependencies {\n    def autoHashVersion = 'com.github.karlicoss.auto.value:autohash:\u003cversion\u003e'\n    provided autoHashVersion\n    apt autoHashVersion\n}\n```\n\n# Benchmarks\n\nSee detailed benchmark reports in `benchmarks` directory.\n\n## JMH (desktop JVM benchmark)\n\n### Running\n\n    ./gradlew autohash:jmh\n\n### Benchmark results\n\n    Benchmark                                   (cachingOn)  Mode  Cnt  Score    Error  Units\n    TestBenchmark.combineHashCodes                    false  avgt   30  0.087 ±  0.001  us/op\n    TestBenchmark.combineHashCodes                     true  avgt   30  0.088 ±  0.001  us/op\n    TestBenchmark.getFromSmallHashSet                 false  avgt   30  0.195 ±  0.001  us/op\n    TestBenchmark.getFromSmallHashSet                  true  avgt   30  0.192 ±  0.001  us/op\n    TestBenchmark.putInHashSet                        false  avgt   30  0.147 ±  0.023  us/op\n    TestBenchmark.putInHashSet                         true  avgt   30  0.140 ±  0.021  us/op\n    TestBenchmark.putInHashSetAndGetOnce              false  avgt   30  0.222 ±  0.035  us/op\n    TestBenchmark.putInHashSetAndGetOnce               true  avgt   30  0.147 ±  0.001  us/op\n    TestBenchmark.putInHashSetAndGetTenTimes          false  avgt   30  0.952 ±  0.140  us/op\n    TestBenchmark.putInHashSetAndGetTenTimes           true  avgt   30  0.209 ±  0.001  us/op\n\nSome explanations:\n\n* `combineHashCodes` is just a sanity check; it invokes `hashCode` only once for each object, so performance should be roughly the same for caching and non caching versions.\n* `getFromSmallHashSet`: queries items from a `HashSet` which contains none of them. As expected, performance is roughly the same.\n* `putInHashSet`: just builds a `HashSet` from all items. As expected, performance is roughly the same.\n* `putInHashSetAndGetOnce`: builds a `HashSet` from all items and then queries them. As expected, performance is better with caching.\n* `putInHashSetAndGetTenTimes`: same as above, but queries each item ten times. As expected, version with caching is way faster.\n\n## Spanner (Android benchmark)\n\nAndroid microbenchmarking tools do not seem to be as advanced as desktop JVM ones. Anyway, I used \n[Spanner](https://github.com/cmelchior/spanner) project which is a fork of [Caliper](https://github.com/google/caliper) \nframework with some improvements. Nicest thing about Spanner is it provides Gradle plugin, so we can run our benchmarks \nas JUnit tests. Both Spanner and Caliper lack proper documentation though, so I hope I got everything right.\n\nIf you want to learn more about Spanner/Caliper benchmarks, look at examples and their source code:\n\n* https://github.com/google/caliper/tree/master/examples/src/main/java/examples\n* https://github.com/google/caliper/tree/master/tutorial\n* https://github.com/cmelchior/spanner/tree/master/sample\n\n### Running\n\nMake sure your device is connected (emulator would not be a good benchmarking target). Run with: \n\n    ./gradlew android-benchmark:connectedAndroidTest\n\n### Benchmark results\n\nBenchmarks were ran on Nexus 6P (see `deviceinfo.txt` in the benchmark directory).\n\nEasiest way to collect results is just use `adb logcat -s Spanner` command.\nAlso, results are saved on the device in the file specified by the `SpannerConfig.Builder#saveResults` method.\n\n      Benchmark Methods:   [combineHashCodes, getFromSmallHashSet, putInHashSet, putInHashSetAndGetOnce, putInHashSetAndGetTenTimes]\n      Instruments:   [RuntimeInstrument]\n      User parameters:   {}\n      Selection type:    null\n    This selection yields 10 experiments.\n    Trial Report (1 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=combineHashCodes, parameters={cachingOn=false}}\n      Results:\n        runtime(ns): min=20573.00, 1st qu.=71888.25, median=118047.00 (-), mean=102183.15, 3rd qu.=123580.75, max=264792.00\n    Trial Report (2 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=combineHashCodes, parameters={cachingOn=true}}\n      Results:\n        runtime(ns): min=18802.00, 1st qu.=119739.25, median=123125.00 (-), mean=123047.22, 3rd qu.=126653.25, max=278386.00\n    Trial Report (3 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=getFromSmallHashSet, parameters={cachingOn=false}}\n      Results:\n        runtime(ns): min=85469.00, 1st qu.=164284.00, median=192292.00 (-), mean=203028.47, 3rd qu.=207226.00, max=1193437.00\n    Trial Report (4 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=getFromSmallHashSet, parameters={cachingOn=true}}\n      Results:\n        runtime(ns): min=64479.00, 1st qu.=193815.00, median=202161.50 (-), mean=205173.97, 3rd qu.=216576.00, max=1213385.00\n    Trial Report (5 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=putInHashSet, parameters={cachingOn=false}}\n      Results:\n        runtime(ns): min=64739.00, 1st qu.=166797.00, median=174505.00 (-), mean=174710.40, 3rd qu.=186210.75, max=352292.00\n    Trial Report (6 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=putInHashSet, parameters={cachingOn=true}}\n      Results:\n        runtime(ns): min=61198.00, 1st qu.=149636.00, median=163046.50 (-), mean=163324.18, 3rd qu.=173242.00, max=335313.00\n    Trial Report (7 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=putInHashSetAndGetOnce, parameters={cachingOn=false}}\n      Results:\n        runtime(ns): min=37553.00, 1st qu.=163229.00, median=167969.00 (-), mean=172181.58, 3rd qu.=179675.00, max=474115.00\n    Trial Report (8 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=putInHashSetAndGetOnce, parameters={cachingOn=true}}\n      Results:\n        runtime(ns): min=55104.00, 1st qu.=176914.25, median=190547.00 (-), mean=187500.89, 3rd qu.=205768.75, max=888334.00\n    Trial Report (9 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=putInHashSetAndGetTenTimes, parameters={cachingOn=false}}\n      Results:\n        runtime(ns): min=116042.00, 1st qu.=310273.00, median=319193.00 (-), mean=311776.21, 3rd qu.=324726.25, max=489479.00\n    Trial Report (10 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=putInHashSetAndGetTenTimes, parameters={cachingOn=true}}\n      Results:\n        runtime(ns): min=89271.00, 1st qu.=236302.00, median=243698.00 (-), mean=248912.01, 3rd qu.=271068.00, max=363594.00\n    Collected 3000 measurements from:\n      1 instrument(s)\n      10 benchmark(s)\n    Execution complete: 7.856 min.\n\n\nFrom the results, you can notice that for `combineHashCodes`, `getFromSmallHashSet` and `putInHashSet`, there is almost no difference as expected. For `putInHashSetAndGetOnce` cached version is slightly worse, however for `putInHashSetAndGetTenTimes` we can finally spot the performance improvement.\n\nTo take a closer look, I ran same benchmark, but with 10 objects being cached instead of 1:\n\n    Experiment selection:\n      Benchmark Methods:   [combineHashCodes, getFromSmallHashSet, putInHashSet, putInHashSetAndGetOnce, putInHashSetAndGetTenTimes]\n      Instruments:   [RuntimeInstrument]\n      User parameters:   {}\n      Selection type:    null\n    This selection yields 10 experiments.\n    Trial Report (1 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=combineHashCodes, parameters={cachingOn=false}}\n      Results:\n        runtime(ns): min=66042.00, 1st qu.=182526.00, median=188541.00 (-), mean=194221.17, 3rd qu.=196549.00, max=1485520.00\n    Trial Report (2 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=combineHashCodes, parameters={cachingOn=true}}\n      Results:\n        runtime(ns): min=58072.00, 1st qu.=197813.00, median=205026.50 (-), mean=202310.79, 3rd qu.=213060.00, max=584583.00\n    Trial Report (3 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=getFromSmallHashSet, parameters={cachingOn=false}}\n      Results:\n        runtime(ns): min=67865.00, 1st qu.=309492.25, median=317552.00 (-), mean=319214.55, 3rd qu.=331718.75, max=805729.00\n    Trial Report (4 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=getFromSmallHashSet, parameters={cachingOn=true}}\n      Results:\n        runtime(ns): min=122709.00, 1st qu.=289544.00, median=297943.00 (-), mean=296414.07, 3rd qu.=308138.00, max=483282.00\n    Trial Report (5 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=putInHashSet, parameters={cachingOn=false}}\n      Results:\n        runtime(ns): min=128229.00, 1st qu.=307904.00, median=319245.00 (-), mean=314665.10, 3rd qu.=326966.00, max=536771.00\n    Trial Report (6 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=putInHashSet, parameters={cachingOn=true}}\n      Results:\n        runtime(ns): min=126302.00, 1st qu.=316822.50, median=337500.00 (-), mean=332563.06, 3rd qu.=350507.25, max=795208.00\n    Trial Report (7 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=putInHashSetAndGetOnce, parameters={cachingOn=false}}\n      Results:\n        runtime(ns): min=111458.00, 1st qu.=337656.00, median=364714.00 (-), mean=359480.19, 3rd qu.=383332.75, max=706823.00\n    Trial Report (8 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=putInHashSetAndGetOnce, parameters={cachingOn=true}}\n      Results:\n        runtime(ns): min=103282.00, 1st qu.=307292.00, median=336458.00 (-), mean=332103.98, 3rd qu.=348073.00, max=496406.00\n    Trial Report (9 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=putInHashSetAndGetTenTimes, parameters={cachingOn=false}}\n      Results:\n        runtime(ns): min=282812.00, 1st qu.=820547.25, median=835130.00 (-), mean=830399.96, 3rd qu.=841393.00, max=1272136.00\n    Trial Report (10 of 10):\n      Experiment {instrument=RuntimeInstrument, benchmarkMethod=putInHashSetAndGetTenTimes, parameters={cachingOn=true}}\n      Results:\n        runtime(ns): min=158490.00, 1st qu.=471067.50, median=494687.00 (-), mean=484364.10, 3rd qu.=502955.25, max=904583.00\n    Collected 3000 measurements from:\n      1 instrument(s)\n      10 benchmark(s)\n    Execution complete: 7.917 min.\n\nHere, the difference between cached and non cached versions is more noticable: you can see improvement for `putInHashSetAndGetOnce` and `putInHashSetAndGetTenTimes`.\n\nAnyway, the Android benchmark was way harder to conduct, and I wouldn't call it very reliable since the benchmarking tool is not as advanced as JMH. Although you can notice the general trend.\n\n\n# License\n\n    Copyright 2016 Dmitrii Gerasimov.\n    \n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n    \n       http://www.apache.org/licenses/LICENSE-2.0\n    \n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarlicoss%2Fautohash","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarlicoss%2Fautohash","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarlicoss%2Fautohash/lists"}