Skip to content

refactor(specifications): replace dynamic array creation with static empty arrays for improved performance#883

Merged
balazs-szucs merged 2 commits into
grimmory-tools:developfrom
balazs-szucs:empty-array
Apr 26, 2026
Merged

refactor(specifications): replace dynamic array creation with static empty arrays for improved performance#883
balazs-szucs merged 2 commits into
grimmory-tools:developfrom
balazs-szucs:empty-array

Conversation

@balazs-szucs

@balazs-szucs balazs-szucs commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Description

Linked Issue: Fixes #

Changes

This pull request introduces a codebase-wide refactor to optimize array allocations when converting collections to arrays, especially for frequently used types like Specification, Predicate, and String. The main change is the introduction and use of static, pre-allocated empty arrays instead of creating new empty arrays each time, which improves performance and reduces unnecessary object creation. No logic or behavior is changed.

The most important changes are:

Performance improvements (array allocation):

  • Introduced static empty arrays (EMPTY_SPECIFICATION_ARRAY, EMPTY_PREDICATE_ARRAY, EMPTY_STRING_ARRAY) for Specification, Predicate, and String types in their respective classes to avoid repeated allocations of empty arrays.
  • Replaced all instances of toArray(new Type[0]) with toArray(EMPTY_TYPE_ARRAY) in methods that build specifications or predicates, across service and specification classes.

These changes are purely internal optimizations and do not affect application behavior or external interfaces.

Summary by CodeRabbit

  • Chores
    • Internal refactor across backend services to optimize small array handling and predicate construction. Improves efficiency and maintainability with no changes to functionality, APIs, or user-facing behavior.

@coderabbitai

coderabbitai Bot commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: c2e67b6c-e0a8-498b-b480-f78ca74b2a54

📥 Commits

Reviewing files that changed from the base of the PR and between e1e337c and f40fda5.

📒 Files selected for processing (5)
  • backend/src/main/java/org/booklore/app/service/AppBookService.java
  • backend/src/main/java/org/booklore/app/service/AppSeriesService.java
  • backend/src/main/java/org/booklore/app/specification/AppBookSpecification.java
  • backend/src/main/java/org/booklore/config/security/SecurityConfig.java
  • backend/src/main/java/org/booklore/service/BookRuleEvaluatorService.java
✅ Files skipped from review due to trivial changes (4)
  • backend/src/main/java/org/booklore/app/service/AppSeriesService.java
  • backend/src/main/java/org/booklore/config/security/SecurityConfig.java
  • backend/src/main/java/org/booklore/app/service/AppBookService.java
  • backend/src/main/java/org/booklore/app/specification/AppBookSpecification.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/src/main/java/org/booklore/service/BookRuleEvaluatorService.java
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Test Suite / Frontend Tests
  • GitHub Check: Test Suite / Backend Tests
  • GitHub Check: Analyze (java-kotlin)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Frontend Lint Threshold Check

📝 Walkthrough

Walkthrough

Replaces calls that convert collections to arrays from the explicit empty-array form (toArray(new T[0])) to the method-reference form (toArray(T[]::new)) across several backend classes, preserving existing control flow and logic while changing only the array conversion syntax.

Changes

Cohort / File(s) Summary
Book specification/service conversions
backend/src/main/java/org/booklore/app/service/AppBookService.java, backend/src/main/java/org/booklore/app/service/AppSeriesService.java
Replaced specs.toArray(new Specification[0]) with specs.toArray(Specification[]::new) when building specifications passed to AppBookSpecification.combine(...).
Specification predicate conversions
backend/src/main/java/org/booklore/app/specification/AppBookSpecification.java
Replaced multiple occurrences of predicates.toArray(new Predicate[0]) / where.toArray(new Predicate[0]) with toArray(Predicate[]::new), including uses in cb.and, cb.or, subquery where(...), and comic-creator combination logic.
Security config conversion
backend/src/main/java/org/booklore/config/security/SecurityConfig.java
Changed unauthenticatedEndpoints.toArray(new String[0]) to unauthenticatedEndpoints.toArray(String[]::new) for requestMatchers(...).permitAll() configuration.
Rule evaluator predicate conversions
backend/src/main/java/org/booklore/service/BookRuleEvaluatorService.java
Replaced new Predicate[0] usage with Predicate[]::new in buildPredicate(...) and buildArrayFieldPredicate(...) when converting predicate lists to arrays for cb.and/cb.or calls.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I hopped through lines, method refs in tow,

Arrays now born from a streamlined flow.
No extra zeros scattered on the ground,
Just tidy conversions, neat and sound.
A little hop for code, a joyful bound.


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (2 errors, 1 warning)

Check name Status Explanation Resolution
Title check ❌ Error The PR title claims to replace dynamic array creation with static empty arrays, but the summary shows method references (toArray(Type[]::new)) are used instead. Update the title to accurately reflect that the refactor replaces toArray(new Type[0]) with toArray(Type[]::new) method references, not static arrays.
Description check ❌ Error The description claims static empty arrays (EMPTY_SPECIFICATION_ARRAY, etc.) were introduced and used, but the actual changes show method references (toArray(Type[]::new)) are the pattern used instead. Correct the description to accurately describe that method references (toArray(Type[]::new)) replaced explicit array allocation (toArray(new Type[0])), not static pre-allocated arrays.
Docstring Coverage ⚠️ Warning Docstring coverage is 10.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
backend/src/main/java/org/booklore/app/service/AppBookService.java (1)

58-58: LGTM — behavior-preserving constant extraction.

The raw Specification[] declaration mirrors the pre-existing new Specification[0] pattern, so no new unchecked-warning surface is introduced. As a purely stylistic optional nit, you could declare it as Specification<?>[] to be slightly more precise, but it doesn't change the generated code or the call sites.

Also applies to: 1055-1055, 1122-1122

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/main/java/org/booklore/app/service/AppBookService.java` at line
58, Replace the raw Specification[] type of the constant
EMPTY_SPECIFICATION_ARRAY with the more precise generic form Specification<?>[]
(update the declaration of EMPTY_SPECIFICATION_ARRAY in AppBookService and the
same occurrences at the other two locations referenced) so the constant
expresses its wildcard element type without changing runtime behavior.
backend/src/main/java/org/booklore/config/security/SecurityConfig.java (1)

50-50: LGTM — safe and targeted.

No change to authorization semantics; only the empty-array allocation site is swapped. Out of scope for this PR, but since unauthenticatedEndpoints is built from COMMON_UNAUTHENTICATED_ENDPOINTS and never mutated in opdsBasicAuthSecurityChain, a future cleanup could pass COMMON_UNAUTHENTICATED_ENDPOINTS directly to requestMatchers(...) and drop the ArrayList/toArray round-trip entirely.

Also applies to: 89-89

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/main/java/org/booklore/config/security/SecurityConfig.java` at
line 50, Replace the dynamic construction of unauthenticatedEndpoints and the
toArray call with a direct use of the static array: where
opdsBasicAuthSecurityChain builds an ArrayList from
COMMON_UNAUTHENTICATED_ENDPOINTS and calls
requestMatchers(unauthenticatedEndpoints.toArray(EMPTY_STRING_ARRAY)), change it
to call requestMatchers(COMMON_UNAUTHENTICATED_ENDPOINTS) directly and remove
the temporary ArrayList and EMPTY_STRING_ARRAY constant since
COMMON_UNAUTHENTICATED_ENDPOINTS is never mutated.
backend/src/main/java/org/booklore/app/service/AppSeriesService.java (1)

36-36: LGTM — consistent with the AppBookService change.

Same pattern as AppBookService.EMPTY_SPECIFICATION_ARRAY; behavior is preserved. Since this constant is duplicated across AppBookService and AppSeriesService (and a Predicate[] variant exists in AppBookSpecification and BookRuleEvaluatorService), an optional follow-up would be to centralize them in a small EmptyArrays utility class to enforce single-source-of-truth — but that's purely cosmetic.

Also applies to: 377-377

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/main/java/org/booklore/app/service/AppSeriesService.java` at line
36, Duplicate empty-array constants exist
(AppSeriesService.EMPTY_SPECIFICATION_ARRAY and
AppBookService.EMPTY_SPECIFICATION_ARRAY, plus Predicate[] variants in
AppBookSpecification and BookRuleEvaluatorService); to centralize, create a
small utility class (e.g., EmptyArrays) exposing typed constants
(Specification<?>[] EMPTY_SPECIFICATION_ARRAY, Predicate<?>[]
EMPTY_PREDICATE_ARRAY), replace usages in AppSeriesService, AppBookService,
AppBookSpecification and BookRuleEvaluatorService to reference EmptyArrays, and
remove the now-redundant local constants so all code uses the single source of
truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@backend/src/main/java/org/booklore/app/service/AppBookService.java`:
- Line 58: Replace the raw Specification[] type of the constant
EMPTY_SPECIFICATION_ARRAY with the more precise generic form Specification<?>[]
(update the declaration of EMPTY_SPECIFICATION_ARRAY in AppBookService and the
same occurrences at the other two locations referenced) so the constant
expresses its wildcard element type without changing runtime behavior.

In `@backend/src/main/java/org/booklore/app/service/AppSeriesService.java`:
- Line 36: Duplicate empty-array constants exist
(AppSeriesService.EMPTY_SPECIFICATION_ARRAY and
AppBookService.EMPTY_SPECIFICATION_ARRAY, plus Predicate[] variants in
AppBookSpecification and BookRuleEvaluatorService); to centralize, create a
small utility class (e.g., EmptyArrays) exposing typed constants
(Specification<?>[] EMPTY_SPECIFICATION_ARRAY, Predicate<?>[]
EMPTY_PREDICATE_ARRAY), replace usages in AppSeriesService, AppBookService,
AppBookSpecification and BookRuleEvaluatorService to reference EmptyArrays, and
remove the now-redundant local constants so all code uses the single source of
truth.

In `@backend/src/main/java/org/booklore/config/security/SecurityConfig.java`:
- Line 50: Replace the dynamic construction of unauthenticatedEndpoints and the
toArray call with a direct use of the static array: where
opdsBasicAuthSecurityChain builds an ArrayList from
COMMON_UNAUTHENTICATED_ENDPOINTS and calls
requestMatchers(unauthenticatedEndpoints.toArray(EMPTY_STRING_ARRAY)), change it
to call requestMatchers(COMMON_UNAUTHENTICATED_ENDPOINTS) directly and remove
the temporary ArrayList and EMPTY_STRING_ARRAY constant since
COMMON_UNAUTHENTICATED_ENDPOINTS is never mutated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 121744c7-eb81-4190-902e-ee31bc74b6fb

📥 Commits

Reviewing files that changed from the base of the PR and between 42c7417 and e1e337c.

📒 Files selected for processing (5)
  • backend/src/main/java/org/booklore/app/service/AppBookService.java
  • backend/src/main/java/org/booklore/app/service/AppSeriesService.java
  • backend/src/main/java/org/booklore/app/specification/AppBookSpecification.java
  • backend/src/main/java/org/booklore/config/security/SecurityConfig.java
  • backend/src/main/java/org/booklore/service/BookRuleEvaluatorService.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Test Suite / Backend Tests
  • GitHub Check: Test Suite / Frontend Tests
  • GitHub Check: Analyze (java-kotlin)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Frontend Lint Threshold Check
🧰 Additional context used
📓 Path-based instructions (1)
backend/src/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

backend/src/**/*.java: Use 4-space indentation and match surrounding Java style in backend code
Prefer constructor injection via Lombok patterns already used in the codebase. Do not introduce @Autowired field injection in backend code
Use MapStruct for entity/DTO mapping in backend code
Keep JPA entities on the *Entity suffix in backend code

Files:

  • backend/src/main/java/org/booklore/app/service/AppSeriesService.java
  • backend/src/main/java/org/booklore/config/security/SecurityConfig.java
  • backend/src/main/java/org/booklore/service/BookRuleEvaluatorService.java
  • backend/src/main/java/org/booklore/app/specification/AppBookSpecification.java
  • backend/src/main/java/org/booklore/app/service/AppBookService.java
🧠 Learnings (4)
📚 Learning: 2026-04-04T14:03:28.295Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 372
File: frontend/src/app/features/book/service/app-books-api.service.ts:150-178
Timestamp: 2026-04-04T14:03:28.295Z
Learning: In `frontend/src/app/features/book/service/app-books-api.service.ts`, the `summaryToBook` function intentionally uses `as unknown as Book` to cast a partial object to the `Book` type. The returned object omits some required `Book` fields (e.g. `metadata.bookId`, `pdfProgress.page`) and includes an extra `bookFiles: []` property not in the `Book` interface. This is by design — consumers of `AppBooksApiService.books()` (e.g. `BookCardComponent`) only access the subset of properties that are populated from `AppBookSummary`, so no runtime issues occur. Do not flag this as a type safety error.

Applied to files:

  • backend/src/main/java/org/booklore/app/service/AppSeriesService.java
📚 Learning: 2026-04-04T15:36:56.558Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 372
File: booklore-api/src/main/java/org/booklore/app/service/AppBookService.java:357-359
Timestamp: 2026-04-04T15:36:56.558Z
Learning: In `booklore-api/src/main/java/org/booklore/app/service/AppBookService.java`, `shelfId` and `magicShelfId` are mutually exclusive navigation contexts in `getFilterOptions`. A request will never supply both at the same time, so the `else if (shelfId != null)` branch after the `magicBookIds != null` check is intentional and correct — there is no missing shelf-∩-magicShelf intersection case. Do not flag this pattern as a bug.

Applied to files:

  • backend/src/main/java/org/booklore/app/service/AppSeriesService.java
📚 Learning: 2026-04-10T08:15:37.436Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 449
File: booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java:139-145
Timestamp: 2026-04-10T08:15:37.436Z
Learning: When using Spring `ContentDisposition.builder(...).filename(name, StandardCharsets.UTF_8).build()` (i.e., explicitly providing UTF-8), the resulting header value should include both the quoted `filename="=?UTF-8?..."` and the RFC 5987 `filename*=` parameters. In this case, any extra ASCII fallback computation (e.g., deriving an ASCII `fallbackFilename` via `NON_ASCII_PATTERN` and calling `.filename(fallbackFilename)`) is likely redundant—prefer calling only `.filename(fallbackName?, StandardCharsets.UTF_8)` as appropriate and let Spring handle the UTF-8 header parameters. Verify by comparing the emitted header for `filename` and `filename*` before deciding to keep an ASCII fallback.

Applied to files:

  • backend/src/main/java/org/booklore/app/service/AppSeriesService.java
  • backend/src/main/java/org/booklore/config/security/SecurityConfig.java
  • backend/src/main/java/org/booklore/service/BookRuleEvaluatorService.java
  • backend/src/main/java/org/booklore/app/specification/AppBookSpecification.java
  • backend/src/main/java/org/booklore/app/service/AppBookService.java
📚 Learning: 2026-04-14T12:43:08.698Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 502
File: booklore-api/src/main/java/org/booklore/service/reader/ChapterCacheService.java:0-0
Timestamp: 2026-04-14T12:43:08.698Z
Learning: For this codebase (booklore-api), target Java 25 with `--enable-preview`, so `_` is intentionally used as an unnamed/ignored variable (e.g., lambda parameter or pattern variable) per Java’s preview feature JEP 456. Do not flag `_` in those contexts as an invalid/reserved identifier; only flag it if it’s used in a non-supported position (e.g., where an unnamed variable is not applicable for the Java preview rules).

Applied to files:

  • backend/src/main/java/org/booklore/app/service/AppSeriesService.java
  • backend/src/main/java/org/booklore/config/security/SecurityConfig.java
  • backend/src/main/java/org/booklore/service/BookRuleEvaluatorService.java
  • backend/src/main/java/org/booklore/app/specification/AppBookSpecification.java
  • backend/src/main/java/org/booklore/app/service/AppBookService.java
🔇 Additional comments (2)
backend/src/main/java/org/booklore/service/BookRuleEvaluatorService.java (1)

35-35: LGTM — straightforward empty-array reuse.

Predicate is non-generic (jakarta.persistence.criteria.Predicate), so no raw-type warnings are introduced, and the swap is behavior-preserving. As a tiny optional nit, you could co-locate EMPTY_PREDICATE_ARRAY with the other static field block (NUMERIC_FIELDS at line 957) for consistency, but it's not worth a separate change.

Also applies to: 92-93, 874-874

backend/src/main/java/org/booklore/app/specification/AppBookSpecification.java (1)

21-21: LGTM — broad but mechanical replacement.

All 10 call sites consistently substitute new Predicate[0] for the shared EMPTY_PREDICATE_ARRAY, including the varargs where(...) site at line 880, which correctly accepts the materialized array. No semantic change, and Predicate being non-generic avoids any raw-type concerns.

Also applies to: 279-279, 586-586, 618-618, 668-668, 733-733, 757-757, 828-828, 880-880, 885-886

Comment thread backend/src/main/java/org/booklore/app/service/AppBookService.java Outdated
@balazs-szucs balazs-szucs merged commit 2cfcc64 into grimmory-tools:develop Apr 26, 2026
16 checks passed
paulbovbel pushed a commit to paulbovbel/grimmory that referenced this pull request Apr 27, 2026
dsmouse pushed a commit to dsmouse/grimmory that referenced this pull request May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants