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; + } } 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()); + } }