java.util.stream.Collectors with EnumSet Stream
Asked Answered
L

2

46

I'm trying to use in place of bitmask below is the code

public static Set<Amenities> fromBitFlags(int bitFlag) {
    return ALL_OPTS.stream().filter(a -> (a.ameityId & bitFlag) > 0).collect(Collectors.toSet());
}

I would like to return EnumSet instead of a plain set(dont want to loose out on EnumSet's usefulness just because of casting).

Need some directions on how to create a Custom Collector to collect EnumSet.

Ladin answered 3/2, 2016 at 13:23 Comment(0)
C
90

You may use toCollection(Supplier):

return ALL_OPTS.stream().filter(a -> (a.ameityId & bitFlag) > 0)
               .collect(Collectors.toCollection(() -> EnumSet.noneOf(Amenities.class)));

The toCollection method receives a lambda which should create an empty collection to store the result. Here we create empty EnumSet using EnumSet.noneOf call. Note that for EnumSet you must always specify (implicitly or explicitly) which enum is this set for.

Claudieclaudina answered 3/2, 2016 at 13:29 Comment(8)
Thanks a lot, would be really helpful if you could explain this lambda in brief () -> EnumSet.noneOf(Amenities.class) , how this creates a Supplier that can automatically call EnumSet.add()Ladin
@SomasundaramSekar, added some explanation. Hopefully this helps.Claudieclaudina
Mind the alternative of EnumSet<Amenities> s=EnumSet.copyOf(ALL_OPTS); s.removeIf(a -> (a.ameityId & bitFlag) <= 0);Fugitive
@Fugitive that's a bad alternative, as it might throw an unnecessary IllegalArgumentException if the Collection ALL_OPTS is empty and not an instance of EnumSet.Wite
@Wite the naming style suggests that ALL_OPTS is a constant containing all options, with a predictable non-empty content. The OP’s concerns about losing EnumSet's usefulness also suggest that ALL_OPTS is an EnumSet in the first place. Of course, it would be easy to fix if that’s not the case.Fugitive
If ALL_OPTS is an EnumSet, chances are high that its not actually a constant, as JumboEnumSet and RegularEnumSet are both mutable and I don't think that the OP implemented an unmodifiable implementation of EnumSet. And yes, for this special case it is likely that ALL_OPTS is not empty (even though depending on the actual case ALL_OPTS could be none opts at all, I guess). But as people often copy-and-paste code found on SO, even if it doesn't match their case 100%, I felt the need to point out the chance of producing an Exception when using your — in my opinion bad — alternative.Wite
@Wite if ALL_OPTS is an EnumSet, it doesn’t matter whether it is empty, as EnumSet.copyOf(ALL_OPTS) will always work. In fact, I’d consider the case that ALL_OPTS is empty worth producing an exception, rather than proceeding with an empty result set.Fugitive
@Holger's alternative is fine. It's highly unlikely that ALL_OPTS is an empty non-EnumSet. If it is, that's probably a bug, and I want to get an exception. Anyway, in most cases you probably don't need ALL_OPTS anyway (premature optimization?) and you can replace EnumSet.copyOf(ALL_OPTS) by EnumSet.allOf(Amenities.class).Garate
S
0

If you'd like to have a reusable toEnumSet() collector:

<E extends Enum<E>> Collector<E, ?, EnumSet<E>> toEnumSet() {
    return Collectors.collectingAndThen(Collectors.toSet(), EnumSet::copyOf);
}

EnumSet<Amenities> fromBitFlags(int bitFlag) {
    return ALL_OPTS.stream()
            .filter(a -> (a.ameityId & bitFlag) > 0)
            .collect(toEnumSet()));
}

If you don't need to reuse it, you can of course in-line it:

EnumSet<Amenities> fromBitFlags(int bitFlag) {
    return ALL_OPTS.stream()
            .filter(a -> (a.ameityId & bitFlag) > 0)
            .collect(collectingAndThen(toSet(), EnumSet::copyOf));
}

(assuming static imports from Collectors)


Alternatively, you can just wrap your original Set<Amenities> result:

EnumSet<Amenities> fromBitFlags(int bitFlag) {
    return EnumSet.copyOf(
            ALL_OPTS.stream()
                    .filter(a -> (a.ameityId & bitFlag) > 0)
                    .collect(toSet()));
}

Just for fun of it — you can also use reduce:

            .reduce(EnumSet.noneOf(Amenities.class), // identity
                    EnumSet::add, // accumulator
                    EnumSet::addAll)); // combiner

but that's hard to reuse, so maybe better a reducing collector:

            .collect(reducing(
                    EnumSet.noneOf(Amenities.class), // identity
                    EnumSet::of, // mapper
                    EnumSet::addAll)); // combiner
Schadenfreude answered 11/7, 2024 at 16:37 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.