FFFF Suggestion of Stream usage in Javadocs · Issue #8660 · google/guava · GitHub
[go: up one dir, main page]

Skip to content

Suggestion of Stream usage in Javadocs #8660

Open

Description

@kilink

I wanted to give my perspective on the wording from helpers such as Lists.transform, namely the following:

* <p><b>Java 8+ users:</b> many use cases for this method are better addressed by {@link
* java.util.stream.Stream#map}. This method is not being deprecated, but we gently encourage you
* to migrate to streams.

I know it's a gentle suggestion to use Streams, but I still find Lists.transform and other lazy transformation methods superior for a lot of usage in the wild; for example, I often see a stream used to map to a target type, only to then be passed to an addAllFoo style method of a proto builder... which is just iterating and copying into another List. That sort of use case where you know it's a one shot iteration is generally more efficiently served via these Guava helpers from Lists, Collections2, Iterables, etc. No real action item here, just hoping that the Javadoc doesn't lead people to always reach for streams, as I find the Guava helpers still have their legitimate uses.

Activity

  1. cpovirk commented on Sep 5, 2026

    @cpovirk
    Member

    Thanks. It would be good to write more here. Some off-the-cuff thoughts:

    • We have seen occasionally horrifying bugs in which people do something like transform(list, this::executeRequestAsync), in which the called method returns a Future. The problem arises when the code then iterates over the result multiple times, resulting in multiple series of queries, each of which may complete at different times with different results. This is of course impossible in the simple case that you and others typically use the APIs for, but it's a very scary caveat.
      • This is perhaps especially surprising because it's different from the behavior of the similarly named Futures.transform, which executes eagerly and only once.
    • I have some in-progress work to make some Guava streams faster in common use cases. That could at least narrow the performance gap here, if obviously not the syntax gap.
      • Another alternative in the specific example you presented is if course to end the stream pipeline with .forEach(builder::addFoo). But that looks relatively unnatural, and it might well be less optimized on the proto side (on top of the stream overhead itself that I hope to address).
    • As usual, there is some value in presenting a single, well-lit path. Our static methods get ugly as soon as you need to both transform and filter (though there's also FluentIterable...). They also deviate from what is now Java's standard naming (like transform vs. map). But that's all a question of phrasing, so I don't think we disagree on any of it; we're nudging users in an overall reasonable direction, but we aren't giving them the reasons and caveats around it.

    I should really write that (and maybe more?) somewhere and link to it from the Javadoc... someday. There is potentially also opportunity for static analysis for users who want to mix and match.

  2. seanxuu commented on Sep 7, 2026

    @seanxuu

    I’d like to help improve the Javadocs here. I plan to add a concise explanation of when the lazy transformation APIs are preferable, the repeated-iteration/eager-vs-lazy caveat, and how the guidance relates to Streams and Futures, while keeping the existing API recommendation intact. Please let me know if this scope fits the project.

  3. cpovirk commented on Sep 10, 2026

    @cpovirk
    Member

    I suspect that this will be tricky to write in a way that we're happy with. The overall framing that we'll want is still going to be that Stream is the norm nowadays and that our various transform* and filter* APIs have various subtle advantages and disadvantages, which may change over time. I wouldn't want to let the perfect be the enemy of the good, but I am coming from the perspective that long documentation is overall a bad thing, so each new addition should have a moderately strong justification in order to qualify as "good" :) You can still give it a try if you want, but you can see from the already pending PRs that I am quite behind on reviews as I juggle other things, so the two possibilities here are likely "uncontroversially good enough to merge almost untouched" and "stuck waiting until I have a bunch of free time."

  4. kilink commented on Sep 10, 2026

    @kilink
    ContributorAuthor

    Thanks @cpovirk . I am curious about the performance improvements to streams you alluded to. Part of my concern around streams has to do with optimizing the hot path, where the overhead can add up.

  5. cpovirk commented on Sep 10, 2026

    @cpovirk
    Member

    Mainly, I want to make immutableCollection.stream().<small sequence of map and filter calls>.<collect or forEach> as fast as it can be. (Of particular note, stream().map(...).collect(toImmutableList()) can be a toArray() call, an iteration over that to perform the mapping in place, and a creation of ImmutableList directly from that array without copying.) It's been straightforward to show benefits in microbenchmarks, and I believe that they'll hold up across our fleet, but I want to get the support for various Java versions just right, and I don't have a single app I can point to that will clearly benefit enough that I've been able to prioritize it yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3no SLO

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0