I use this code to to generate chart with data:
#GetMapping("/terminals")
public ResponseEntity<Map<String, List<TopTerminalsDTO>>> getTopTerminals(
#RequestParam(value = "start_date", required = true) String start_date,
#RequestParam(value = "end_date", required = true) String end_date) {
final List<PaymentTransactionsDailyFacts> list = dashboardService.findTop_Terminals(start_dateTime, end_dateTime);
final Collector<PaymentTransactionsDailyFacts, List<TopTerminalsDTO>, List<TopTerminalsDTO>> terminalsCollector = Collector
.of(ArrayList::new, (terminals, p) -> terminals.add(mapper.toTopTerminalsDTO(p)),
(accumulator, terminals) -> {
accumulator.addAll(terminals);
return accumulator;
});
final Map<String, List<TopTerminalsDTO>> final_map = list.stream().filter(p -> p.getTerminal_id() != null)
.collect(Collectors.groupingBy(p -> getTerminalName(p.getTerminal_id()), terminalsCollector));
return ResponseEntity.ok(final_map);
}
private String getTerminalName(Integer id) {
Optional<Terminals> obj = terminalService.findById(id);
return obj.map(Terminals::getName).orElse("");
}
But I noticed that getTerminalName is called more than 10 times to translate the name from number. Do you know how I can reduce the number of calls with some optimization?
Modify findTop_Terminals and PaymentTransactionsDailyFacts to include the name (using a SQL LEFT JOIN clause).
Alternative, scan the list for all terminal ids, then call a List<Terminals> list = terminalService.findByIds(idList); method to get all those terminals using a SQL IN clause.
Note: Beware limit on how many ? markers can be in a SQL statement.
Then build a Map<Integer, String> mapping terminal id to name, and replace getTerminalName method with map lookup.
Sounds like a case for a temporary cache, scoped to just this request, or perhaps longer if the terminal names are stable enough.
Clearly something like ehCache behind the scenes would suit this well, but I am often tempted with a tactical bit of caching, especially if I don't want to keep the cached values beyond this immediate request.
For example:
TerminalNameCache cache = new TerminalNameCache();
final Map<String, List<TopTerminalsDTO>> final_map = list.stream()
.filter(p -> p.getTerminal_id() != null)
.collect(Collectors.groupingBy(
p -> cache.getTerminalName(p.getTerminal_id()),
terminalsCollector));
Then the TerminalNameCache is just an inner class in the parent Controller class. This is to allow it to call the existing private String getTerminalName(Integer id) method from the question (shown as on ParentControllerClass below):
private class TerminalNameCache {
private final Map<Integer, String> cache = new ConcurrentHashMap<>();
private String getTerminalName(Integer id) {
return cache.computeIfAbsent(id,
id2 -> ParentControllerClass.this.getTerminalName(id2));
}
}
If this looks like emerging into a pattern, it would be worth refactoring the caching to something more re-useable. E.g. this could based around caching calls to a generic function:
public class CachedFunction<T, R> implements Function<T, R> {
private final Function<T, R> function;
private final Map<T, R> cache = new ConcurrentHashMap<>();
public CachedFunction(Function<T, R> function) {
this.function = function;
}
#Override
public R apply(T t) {
return cache.computeIfAbsent(t, t2 -> function.apply(t2));
}
}
This would then be used like this:
CachedFunction<Integer, String> cachedFunction = new CachedFunction<>(
id -> getTerminalName(id));
final Map<String, List<TopTerminalsDTO>> final_map = list.stream()
.filter(p -> p.getTerminal_id() != null)
.collect(Collectors.groupingBy(
p -> cachedFunction.apply(p.getTerminal_id()),
terminalsCollector));
Related
Suppose there is a simple enum called Type defined like this:
enum Type{
X("S1"),
Y("S2");
private String s;
private Type(String s) {
this.s = s;
}
}
Finding the correct enum for given s is trivially done with static method with for-loop (assume the method is defined inside enum), e.g.:
private static Type find(String val) {
for (Type e : Type.values()) {
if (e.s.equals(val))
return e;
}
throw new IllegalStateException(String.format("Unsupported type %s.", val));
}
I think the functional equivalent of this expressed with Stream API would be something like this:
private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.reduce((t1, t2) -> t1)
.orElseThrow(() -> {throw new IllegalStateException(String.format("Unsupported type %s.", val));});
}
How could we write this better and simpler? This code feels coerced and not very clear. The reduce() especially seems clunky and abused as it doesn't accumulate anything, performs no calculation and always simply returns t1 (provided the filter returns one value - if it doesn't that's clearly a disaster), not to mention t2 is there superfluous and confusing. Yet I couldn't find anything in Stream API that simply somehow returns directly a T from a Stream<T>.
Is there a better way?
I would use findFirst instead:
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
Though a Map could be better in this case:
enum Type{
X("S1"),
Y("S2");
private static class Holder {
static Map<String, Type> MAP = new HashMap<>();
}
private Type(String s) {
Holder.MAP.put(s, this);
}
public static Type find(String val) {
Type t = Holder.MAP.get(val);
if(t == null) {
throw new IllegalStateException(String.format("Unsupported type %s.", val));
}
return t;
}
}
I learnt this trick from this answer. Basically the class loader initializes the static classes before the enum class, which allows you to fill the Map in the enum constructor itself. Very handy !
Hope it helps ! :)
The accepted answer works well, but if you want to avoid creating a new stream with a temporary array you could use EnumSet.allOf().
EnumSet.allOf(Type.class)
.stream()
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
Arrays.stream(Type.values()).filter(v -> v.s.equals(val)).findAny().orElseThrow(...);
How about using findAny() instead of reduce?
private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findAny()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
I think the second answer of Alexis C. (Alexis C.'s answer) is the good one in term of complexity. Instead of searching in O(n) each time you look for a code using
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
you could use O(n) time at the loading of the class by putting all elements into the map, and then access to the code of the type in constant time O(1) using the map.
enum Type{
X("S1"),
Y("S2");
private final String code;
private static Map<String, Type> mapping = new HashMap<>();
static {
Arrays.stream(Type.values()).forEach(type-> mapping.put(type.getCode(), type));
}
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static Type forCode(final String code) {
return mapping.get(code);
}
}
I know this question is old but I came here from a duplicate. My answer is not strictly answering the OP's question about how to solve the problem using Java Streams. Instead, this answer expands the Map-based solution proposed in the accepted answer to become more (IMHO) manageable.
So here it is: I propose to introduce a special helper class that I named EnumLookup.
Assuming the Type enumeration is slightly better written (meaningful field name + getter), I inject an EnumLookup constant to it like below:
enum Type {
X("S1"),
Y("S2");
private static final EnumLookup<Type, String> BY_CODE = EnumLookup.of(Type.class, Type::getCode, "code");
private final String code;
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static EnumLookup<Type, String> byCode() {
return BY_CODE;
}
}
The usage then becomes (again, IMO) really readable:
Type type = Type.byCode().get("S1"); // returns Type.X
Optional<Type> optionalType = Type.byCode().find("S2"); // returns Optional(Type.Y)
if (Type.byCode().contains("S3")) { // returns false
// logic
}
Finally, here's the code of the EnumLookup helper class:
public final class EnumLookup<E extends Enum<E>, ID> {
private final Class<E> enumClass;
private final ImmutableMap<ID, E> valueByIdMap;
private final String idTypeName;
private EnumLookup(Class<E> enumClass, ImmutableMap<ID, E> valueByIdMap, String idTypeName) {
this.enumClass = enumClass;
this.valueByIdMap = valueByIdMap;
this.idTypeName = idTypeName;
}
public boolean contains(ID id) {
return valueByIdMap.containsKey(id);
}
public E get(ID id) {
E value = valueByIdMap.get(id);
if (value == null) {
throw new IllegalArgumentException(String.format(
"No such %s with %s: %s", enumClass.getSimpleName(), idTypeName, id
));
}
return value;
}
public Optional<E> find(ID id) {
return Optional.ofNullable(valueByIdMap.get(id));
}
//region CONSTRUCTION
public static <E extends Enum<E>, ID> EnumLookup<E, ID> of(
Class<E> enumClass, Function<E, ID> idExtractor, String idTypeName) {
ImmutableMap<ID, E> valueByIdMap = Arrays.stream(enumClass.getEnumConstants())
.collect(ImmutableMap.toImmutableMap(idExtractor, Function.identity()));
return new EnumLookup<>(enumClass, valueByIdMap, idTypeName);
}
public static <E extends Enum<E>> EnumLookup<E, String> byName(Class<E> enumClass) {
return of(enumClass, Enum::name, "enum name");
}
//endregion
}
Note that:
I used Guava's ImmutableMap here, but a regular HashMap or LinkedHashMap can be used instead.
If you mind the lack of lazy initialization in the above approach, you can delay building of the EnumLookup until byCode method is first called (e.g. using the lazy-holder idiom, like in the accepted answer)
You'd need a getter for String s, but this is the pattern I use:
private static final Map<String, Type> TYPE_MAP =
Collections.unmodifiableMap(
EnumSet.allOf(Type.class)
.stream()
.collect(Collectors.toMap(Type::getS, e -> e)));
public static Type find(String s) {
return TYPE_MAP.get(s);
}
No for loops, only streams. Quick lookup as opposed to building a stream every time the method is called.
I can't add a comment yet, so I am posting an answer to complement the above answer, just following the same idea but using java 8 approach:
public static Type find(String val) {
return Optional
.ofNullable(Holder.MAP.get(val))
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
You need a getter for String s.
In the example below this method is getDesc():
public static StatusManifestoType getFromValue(String value) {
return Arrays.asList(values()).stream().filter(t -> t.getDesc().equals(value)).findAny().orElse(null);
}
I have two types like these:
#Data
public class SomePersonType {
private String name;
private int age;
}
and
#Data
#Builder
public class SomeOtherPersonType {
private String name;
private int age;
}
I need to convert a map of one type to the other. So, my code looks like this:
public class Main {
public static void main(final String... args) {
final Map<String, SomePersonType> somePersonTypeMap = new HashMap<>();
// ...
// populating the map above with some values
// ...
final Map<String, SomeOtherPersonType> someOtherPersonTypeMap = somePersonTypeMap.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> SomeOtherPersonType.builder()
.name(entry.getValue().getName())
.age(entry.getValue().getAge())
.build()
));
}
}
My real code contains many other attributes. So, I don't want to do entry.getValue() multiple times. I want to put it in a local variable and then use it. Something like this:
public class Main {
public static void main(final String... args) {
final Map<String, SomePersonType> somePersonTypeMap = new HashMap<>();
// ...
// populating the map above with some values
// ...
final Map<String, SomeOtherPersonType> someOtherPersonTypeMap = somePersonTypeMap.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> {
final SomePersonType somePersonType = entry.getValue();
SomeOtherPersonType.builder()
.name(somePersonType.getName())
.age(somePersonType.getAge())
.build();
}
));
}
}
But I get two compiler errors:
Map.Entry::getKey -> Non-static method cannot be referenced from a static context.
entry.getValue() -> Cannot resolve method 'getValue()'
Can someone please point me what I am doing wrong? Thanks.
As far as the lambda expression is concerned, the final statement should return a value so that the value mapping Function returns the mapped value:
Map<String, SomeOtherPersonType> someOtherPersonTypeMap = somePersonTypeMap.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> {
final SomePersonType somePersonType = entry.getValue();
return SomeOtherPersonType.builder()
.name(somePersonType.getName())
.age(somePersonType.getAge())
.build();
}
));
However, since you're using a builder, then why not have the builder take care of creating the SomeOtherPersonType from a SomePersonType:
public SomeOtherPersonType fromSomePersonType(SomePersonType) {
...
}
This way you can do:
Map<String, SomeOtherPersonType> someOtherPersonTypeMap = somePersonTypeMap.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> SomeOtherPersonType.builder()
.fromSomePersonType(entry.getValue())
.build()
));
Suppose I have a cache implemented as java.util.Map which stores (arbitrary) values for keys. As the values are not mandatorily present, the cache returns an java.util.Optional and is able to be provided with a java.util.function.Supplier to calculate the value for a given non-existing key.
My first naive approach was
public class Cache0 {
private final Map<String, String> mapping = new HashMap<>();
public Optional<String> get(String key, Supplier<Optional<String>> supplier) {
final Optional<String> valueOptional;
if (this.mapping.containsKey(key)) {
final String value = this.mapping.get(key);
valueOptional = Optional.of(value);
} else {
valueOptional = supplier.get();
if (valueOptional.isPresent()) {
this.mapping.put(key, valueOptional.get());
}
}
return valueOptional;
}
}
but I found this very inelegant and as I learned about java.util.Map#computeIfAbsent I changed the code to the following
public class Cache1 {
private final Map<String, String> mapping = new HashMap<>();
public Optional<String> get(String key, Supplier<Optional<String>> supplier) {
final String value = this.mapping.computeIfAbsent(key, absentKey -> this.getValue(supplier));
return Optional.ofNullable(value);
}
private String getValue(Supplier<Optional<String>> supplier) {
return supplier.get()
.orElse(null);
}
}
but what now bothers me is the redundant use of java.util.Optional#ofNullable in combination with the null result of the getValue method which is needed to provide java.util.Map#computeIfAbsent with the "default" value not to be inserted into the map.
In an ideal situation, something like the following would be possible
public class Cache2 {
private final Map<String, String> mapping = new HashMap<>();
public Optional<String> get(String key, Supplier<Optional<String>> supplier) {
return this.mapping.computeIfAbsent(key, absentKey -> supplier.get());
}
}
where java.util.Map#computeIfAbsent would skip the insertion if the second parameter represents an empty java.util.Optional and returns an java.util.Optional#empty instead but unfortunately the use of java.util.Optional#empty as "default" insert value for java.util.Map#computeIfAbsent is not supported and the code does not compile.
A further possibility would be to store a mapping of String to java.util.Optional but then the java.util.Map would store the java.util.Optional#empty as value contradicting my use-case again to be forced to store invalid mappings and removing/replacing them by hand later.
public class Cache3 {
private final Map<String, Optional<String>> mapping = new HashMap<>();
public Optional<String> get(String key, Supplier<Optional<String>> supplier) {
return this.mapping.computeIfAbsent(key, absentKey -> supplier.get());
}
}
Is anyone aware of a better approach to handle this kind of use-case or do I have to fall back to my implementation of Cache1?
To do this kind of thing I usually use an Optional in my map - this way
map.get()!=null means I've cached the access and map.get().isPresent() tells me if a sensible value was returned.
In this case I'd use a Suplier<String> that returns null when the value is not present. Then the implementation would look like this:
public class Cache {
private final Map<String, Optional<String>> mapping = new HashMap<>();
public Optional<String> get(String key, Suplier<String> supplier) {
return mapping.computeIfAbsent(key,
unused -> Optional.ofNullable(supplier.get()) );
}
}
Absent keys do get inserted into the map, but marked as missing.
It sounds to me like you are re-inventing a Guava LoadingCache (read here about Guava Caches). While this is definitely an interesting programming exercise, the existing solution is time-proven, can be configured to your needs and works under extremely heavy load.
An example definition would be:
Cache<Key, Value> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.build(); // look Ma, no CacheLoader
...
try {
// If the key wasn't in the "easy to compute" group, we need to
// do things the hard way.
cache.get(key, new Callable<Value>() {
#Override
public Value call() throws AnyException {
return doThingsTheHardWay(key);
}
});
} catch (ExecutionException e) {
throw new OtherException(e.getCause());
}
This is somewhat equivalent to your usage scenario, i.e. the calculation can be different on a per-key level. Usually, you don't need this, so you'd prefer a stored calculation method inside the cache:
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
...
try {
return graphs.get(key);
} catch (ExecutionException e) {
throw new OtherException(e.getCause());
}
One of the missing features in the Streams API is the "partition by" transformation, for example as defined in Clojure. Say I want to reproduce Hibernate's fetch join: I want to issue a single SQL SELECT statement to receive this kind of objects from the result:
class Family {
String surname;
List<String> members;
}
I issue:
SELECT f.name, m.name
FROM Family f JOIN Member m on m.family_id = f.id
ORDER BY f.name
and I retrieve a flat stream of (f.name, m.name) records. Now I need to transform it into a stream of Family objects, with a list of its members inside. Assume I already have a Stream<ResultRow>; now I need to transform it into a Stream<List<ResultRow>> and then act upon that with a mapping transformation which turns it into a Stream<Family>.
The semantics of the transformation are as follows: keep collecting the stream into a List for as long as the provided discriminator function keeps returning the same value; as soon as the value changes, emit the List as an element of the output stream and start collecting a new List.
I hope to be able to write this kind of code (I already have the resultStream method):
Stream<ResultRow> dbStream = resultStream(queryBuilder.createQuery(
"SELECT f.name, m.name"
+ " FROM Family f JOIN Member m on m.family_id = f.id"
+ " ORDER BY f.name"));
Stream<List<ResultRow> partitioned = partitionBy(r -> r.string(0), dbStream);
Stream<Family> = partitioned.map(rs -> {
Family f = new Family(rs.get(0).string(0));
f.members = rs.stream().map(r -> r.string(1)).collect(toList());
return f;
});
Needless to say, I expect the resulting stream to stay lazy (non-materialized) as I want to be able to process a result set of any size without hitting any O(n) memory limits. Without this crucial requirement I would be happy with the provided groupingBy collector.
The solution requires us to define a custom Spliterator which can be used to construct the partitioned stream. We shall need to access the input stream through its own spliterator and wrap it into ours. The output stream is then constructed from our custom spliterator.
The following Spliterator will turn any Stream<E> into a Stream<List<E>> provided a Function<E, ?> as the discriminator function. Note that the input stream must be ordered for this operation to make sense.
import java.util.*;
import java.util.Spliterators.AbstractSpliterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.util.Comparator.naturalOrder;
public class PartitionBySpliterator<E> extends AbstractSpliterator<List<E>> {
private final Spliterator<E> spliterator;
private final Function<? super E, ?> partitionBy;
private HoldingConsumer<E> holder;
private Comparator<List<E>> comparator;
public PartitionBySpliterator(
Spliterator<E> toWrap,
Function<? super E, ?> partitionBy
) {
super(Long.MAX_VALUE, toWrap.characteristics() & ~SIZED | NONNULL);
this.spliterator = toWrap;
this.partitionBy = partitionBy;
}
public static <E> Stream<List<E>> partitionBy(
Function<E, ?> partitionBy, Stream<E> in
) {
return StreamSupport.stream(
new PartitionBySpliterator<>(in.spliterator(), partitionBy), false);
}
#Override
public boolean tryAdvance(Consumer<? super List<E>> action) {
final HoldingConsumer<E> h;
if (holder == null) {
h = new HoldingConsumer<>();
if (!spliterator.tryAdvance(h)) {
return false;
}
holder = h;
} else {
h = holder;
}
final ArrayList<E> partition = new ArrayList<>();
final Object partitionKey = partitionBy.apply(h.value);
boolean didAdvance;
do {
partition.add(h.value);
}
while ((didAdvance = spliterator.tryAdvance(h))
&& Objects.equals(partitionBy.apply(h.value), partitionKey));
if (!didAdvance) {
holder = null;
}
action.accept(partition);
return true;
}
static final class HoldingConsumer<T> implements Consumer<T> {
T value;
#Override
public void accept(T value) {
this.value = value;
}
}
#Override
public Comparator<? super List<E>> getComparator() {
final Comparator<List<E>> c = this.comparator;
return c != null ? c : (this.comparator = comparator());
}
private Comparator<List<E>> comparator() {
#SuppressWarnings({"unchecked", "rawtypes"})
final Comparator<? super E> innerComparator =
Optional.ofNullable(spliterator.getComparator())
.orElse((Comparator) naturalOrder());
return (left, right) -> {
final int c = innerComparator.compare(left.get(0), right.get(0));
return c != 0 ? c : innerComparator.compare(
left.get(left.size() - 1), right.get(right.size() - 1));
};
}
}
For those of you who just want to partition a stream, there are mappers and collectors for that.
class Person {
String surname;
String forename;
public Person(String surname, String forename) {
this.surname = surname;
this.forename = forename;
}
#Override
public String toString() {
return forename;
}
}
class Family {
String surname;
List<Person> members;
public Family(String surname, List<Person> members) {
this.surname = surname;
this.members = members;
}
#Override
public String toString() {
return "Family{" + "surname=" + surname + ", members=" + members + '}';
}
}
private void test() {
String[][] data = {
{"Kray", "Ronald"},
{"Kray", "Reginald"},
{"Dors", "Diana"},};
// Their families.
Stream<Family> families = Arrays.stream(data)
// Build people
.map(a -> new Person(a[0], a[1]))
// Collect into a Map<String,List<Person>> as families
.collect(Collectors.groupingBy(p -> p.surname))
// Convert them to families.
.entrySet().stream()
.map(p -> new Family(p.getKey(), p.getValue()));
families.forEach(f -> System.out.println(f));
}
It can be done by collapse with StreamEx
StreamEx.of(queryBuilder.createQuery(
"SELECT f.name, m.name"
+ " FROM Family f JOIN Member m on m.family_id = f.id"
+ " ORDER BY f.name"))
.collapse((a, b) -> a.string(0).equals(b.string(0)), Collectors.toList())
.map(l -> new Family(l.get(0).string(0), StreamEx.of(l).map(r -> r.string(1)).toList()))
.forEach(System.out::println);
Suppose there is a simple enum called Type defined like this:
enum Type{
X("S1"),
Y("S2");
private String s;
private Type(String s) {
this.s = s;
}
}
Finding the correct enum for given s is trivially done with static method with for-loop (assume the method is defined inside enum), e.g.:
private static Type find(String val) {
for (Type e : Type.values()) {
if (e.s.equals(val))
return e;
}
throw new IllegalStateException(String.format("Unsupported type %s.", val));
}
I think the functional equivalent of this expressed with Stream API would be something like this:
private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.reduce((t1, t2) -> t1)
.orElseThrow(() -> {throw new IllegalStateException(String.format("Unsupported type %s.", val));});
}
How could we write this better and simpler? This code feels coerced and not very clear. The reduce() especially seems clunky and abused as it doesn't accumulate anything, performs no calculation and always simply returns t1 (provided the filter returns one value - if it doesn't that's clearly a disaster), not to mention t2 is there superfluous and confusing. Yet I couldn't find anything in Stream API that simply somehow returns directly a T from a Stream<T>.
Is there a better way?
I would use findFirst instead:
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
Though a Map could be better in this case:
enum Type{
X("S1"),
Y("S2");
private static class Holder {
static Map<String, Type> MAP = new HashMap<>();
}
private Type(String s) {
Holder.MAP.put(s, this);
}
public static Type find(String val) {
Type t = Holder.MAP.get(val);
if(t == null) {
throw new IllegalStateException(String.format("Unsupported type %s.", val));
}
return t;
}
}
I learnt this trick from this answer. Basically the class loader initializes the static classes before the enum class, which allows you to fill the Map in the enum constructor itself. Very handy !
Hope it helps ! :)
The accepted answer works well, but if you want to avoid creating a new stream with a temporary array you could use EnumSet.allOf().
EnumSet.allOf(Type.class)
.stream()
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
Arrays.stream(Type.values()).filter(v -> v.s.equals(val)).findAny().orElseThrow(...);
How about using findAny() instead of reduce?
private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findAny()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
I think the second answer of Alexis C. (Alexis C.'s answer) is the good one in term of complexity. Instead of searching in O(n) each time you look for a code using
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
you could use O(n) time at the loading of the class by putting all elements into the map, and then access to the code of the type in constant time O(1) using the map.
enum Type{
X("S1"),
Y("S2");
private final String code;
private static Map<String, Type> mapping = new HashMap<>();
static {
Arrays.stream(Type.values()).forEach(type-> mapping.put(type.getCode(), type));
}
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static Type forCode(final String code) {
return mapping.get(code);
}
}
I know this question is old but I came here from a duplicate. My answer is not strictly answering the OP's question about how to solve the problem using Java Streams. Instead, this answer expands the Map-based solution proposed in the accepted answer to become more (IMHO) manageable.
So here it is: I propose to introduce a special helper class that I named EnumLookup.
Assuming the Type enumeration is slightly better written (meaningful field name + getter), I inject an EnumLookup constant to it like below:
enum Type {
X("S1"),
Y("S2");
private static final EnumLookup<Type, String> BY_CODE = EnumLookup.of(Type.class, Type::getCode, "code");
private final String code;
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static EnumLookup<Type, String> byCode() {
return BY_CODE;
}
}
The usage then becomes (again, IMO) really readable:
Type type = Type.byCode().get("S1"); // returns Type.X
Optional<Type> optionalType = Type.byCode().find("S2"); // returns Optional(Type.Y)
if (Type.byCode().contains("S3")) { // returns false
// logic
}
Finally, here's the code of the EnumLookup helper class:
public final class EnumLookup<E extends Enum<E>, ID> {
private final Class<E> enumClass;
private final ImmutableMap<ID, E> valueByIdMap;
private final String idTypeName;
private EnumLookup(Class<E> enumClass, ImmutableMap<ID, E> valueByIdMap, String idTypeName) {
this.enumClass = enumClass;
this.valueByIdMap = valueByIdMap;
this.idTypeName = idTypeName;
}
public boolean contains(ID id) {
return valueByIdMap.containsKey(id);
}
public E get(ID id) {
E value = valueByIdMap.get(id);
if (value == null) {
throw new IllegalArgumentException(String.format(
"No such %s with %s: %s", enumClass.getSimpleName(), idTypeName, id
));
}
return value;
}
public Optional<E> find(ID id) {
return Optional.ofNullable(valueByIdMap.get(id));
}
//region CONSTRUCTION
public static <E extends Enum<E>, ID> EnumLookup<E, ID> of(
Class<E> enumClass, Function<E, ID> idExtractor, String idTypeName) {
ImmutableMap<ID, E> valueByIdMap = Arrays.stream(enumClass.getEnumConstants())
.collect(ImmutableMap.toImmutableMap(idExtractor, Function.identity()));
return new EnumLookup<>(enumClass, valueByIdMap, idTypeName);
}
public static <E extends Enum<E>> EnumLookup<E, String> byName(Class<E> enumClass) {
return of(enumClass, Enum::name, "enum name");
}
//endregion
}
Note that:
I used Guava's ImmutableMap here, but a regular HashMap or LinkedHashMap can be used instead.
If you mind the lack of lazy initialization in the above approach, you can delay building of the EnumLookup until byCode method is first called (e.g. using the lazy-holder idiom, like in the accepted answer)
You'd need a getter for String s, but this is the pattern I use:
private static final Map<String, Type> TYPE_MAP =
Collections.unmodifiableMap(
EnumSet.allOf(Type.class)
.stream()
.collect(Collectors.toMap(Type::getS, e -> e)));
public static Type find(String s) {
return TYPE_MAP.get(s);
}
No for loops, only streams. Quick lookup as opposed to building a stream every time the method is called.
I can't add a comment yet, so I am posting an answer to complement the above answer, just following the same idea but using java 8 approach:
public static Type find(String val) {
return Optional
.ofNullable(Holder.MAP.get(val))
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
You need a getter for String s.
In the example below this method is getDesc():
public static StatusManifestoType getFromValue(String value) {
return Arrays.asList(values()).stream().filter(t -> t.getDesc().equals(value)).findAny().orElse(null);
}