From 5c7cb6c44c0d3d714af614c5407d188c1d9e1273 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2026 06:12:01 +0000 Subject: [PATCH 1/2] Optimize Hashtag streams to reduce allocation overhead This commit replaces the use of Java Streams (`stream().mapToInt().sum()`) in `HashtagTimelineFragment.updateHeader` with explicit loops inside the `Hashtag` model (`getWeekPosts()`, `getWeekAccounts()`). This reduces object allocation and GC overhead during UI rendering. Co-authored-by: dlukt <201112286+dlukt@users.noreply.github.com> --- .jules/bolt.md | 4 ++++ .../techidon/fragments/HashtagTimelineFragment.java | 4 ++-- .../src/main/java/de/icod/techidon/model/Hashtag.java | 10 ++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 001b3a552b..3e2f320bbd 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -49,3 +49,7 @@ ## 2025-02-13 - Cached compiled regular expressions in filtering logic **Learning:** `HtmlParser.applyFilterHighlights` was compiling a regular expression (`Pattern.compile(...)`) for each keyword matched against the text inside a `while` and `for` loop, leading to repeated pattern compilation overhead in a rendering hot path. Using `LruCache` avoids unbounded growth while significantly reducing allocations and compilation time. **Action:** Always precompile or cache expensive `Pattern.compile` calls, especially inside nested loops or frequently invoked methods like `onBindViewHolder` equivalents or text formatters. + +## 2025-05-28 - [Stream Allocation in HashtagTimelineFragment] +**Learning:** `HashtagTimelineFragment.updateHeader` was using `hashtag.history.stream().mapToInt(h->h.uses).sum()` and similar stream operations to compute post/account counts. Since this executes during UI updates, the repeated stream allocation contributes to GC churn in the hot path. +**Action:** Replaced stream usages with explicit loop methods (`getWeekPosts()`, `getWeekAccounts()`) directly in the `Hashtag` model, minimizing allocation overhead and improving rendering performance. diff --git a/mastodon/src/main/java/de/icod/techidon/fragments/HashtagTimelineFragment.java b/mastodon/src/main/java/de/icod/techidon/fragments/HashtagTimelineFragment.java index d1190815d6..064ae23a75 100644 --- a/mastodon/src/main/java/de/icod/techidon/fragments/HashtagTimelineFragment.java +++ b/mastodon/src/main/java/de/icod/techidon/fragments/HashtagTimelineFragment.java @@ -362,9 +362,9 @@ private void updateHeader(){ return; if(hashtag.history!=null && !hashtag.history.isEmpty()){ - int weekPosts=hashtag.history.stream().mapToInt(h->h.uses).sum(); + int weekPosts=hashtag.getWeekPosts(); int todayPosts=hashtag.history.get(0).uses; - int numAccounts=hashtag.history.stream().mapToInt(h->h.accounts).sum(); + int numAccounts=hashtag.getWeekAccounts(); int hSpace=V.dp(8); SpannableStringBuilder ssb=new SpannableStringBuilder(); ssb.append(getResources().getQuantityString(R.plurals.x_posts, weekPosts, weekPosts)); diff --git a/mastodon/src/main/java/de/icod/techidon/model/Hashtag.java b/mastodon/src/main/java/de/icod/techidon/model/Hashtag.java index f20c5739c9..efd18eaa17 100644 --- a/mastodon/src/main/java/de/icod/techidon/model/Hashtag.java +++ b/mastodon/src/main/java/de/icod/techidon/model/Hashtag.java @@ -56,4 +56,14 @@ public int getWeekPosts(){ } return sum; } + + public int getWeekAccounts(){ + // Optimization: Use loop instead of Stream to avoid allocation overhead in hot paths. + if(history==null) return 0; + int sum=0; + for(History h:history){ + sum+=h.accounts; + } + return sum; + } } From eda287f35ca6c7606dc2c947d6699078c0cc5dd3 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2026 10:15:43 +0000 Subject: [PATCH 2/2] Add tests for Hashtag.getWeekAccounts() Addresses code review feedback by adding tests to cover the functionality of Hashtag.getWeekAccounts(), verifying proper summation as well as behavior when the history list is empty or null. Co-authored-by: dlukt <201112286+dlukt@users.noreply.github.com> --- .../de/icod/techidon/model/HashtagTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/mastodon/src/test/java/de/icod/techidon/model/HashtagTest.java b/mastodon/src/test/java/de/icod/techidon/model/HashtagTest.java index 476a0a8c9f..9c4ba3dc9e 100644 --- a/mastodon/src/test/java/de/icod/techidon/model/HashtagTest.java +++ b/mastodon/src/test/java/de/icod/techidon/model/HashtagTest.java @@ -40,4 +40,39 @@ public void testGetWeekPosts_NullHistoryReturnsZero() { hashtag.history = null; assertEquals(0, hashtag.getWeekPosts()); } + + + @Test + public void testGetWeekAccounts_CalculatesCorrectSum() { + Hashtag hashtag = new Hashtag(); + hashtag.history = new ArrayList<>(); + + History h1 = new History(); + h1.accounts = 5; + hashtag.history.add(h1); + + History h2 = new History(); + h2.accounts = 10; + hashtag.history.add(h2); + + History h3 = new History(); + h3.accounts = 3; + hashtag.history.add(h3); + + assertEquals(18, hashtag.getWeekAccounts()); + } + + @Test + public void testGetWeekAccounts_EmptyHistoryReturnsZero() { + Hashtag hashtag = new Hashtag(); + hashtag.history = Collections.emptyList(); + assertEquals(0, hashtag.getWeekAccounts()); + } + + @Test + public void testGetWeekAccounts_NullHistoryReturnsZero() { + Hashtag hashtag = new Hashtag(); + hashtag.history = null; + assertEquals(0, hashtag.getWeekAccounts()); + } }