Using Streams to filter objects [duplicate] - java

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

Related

Java Generics: getting return type from parameter

This is an odd question. I don't think there's a solution, but I thought I'd ask anyway.
Say I have an enum:
public enum Key {
RED(String.class),
GREEN(Integer.class),
BLUE(Short.class);
private Class<?> expectedType;
Key(Class<?> expectedType) { this.expectedType = expectedType; }
public Class<?> getExpectedType() { return expectedType; }
}
I want to use the 'expectedType' field from the Key enum as the return type of a method. See:
public class Cache {
private static Map<Key, Object> cache = new HashMap<>();
public void put(Key key, Object value) {
// Easy to validate that 'value' is of type key.getExpectedType()...
}
public <T> T get(Key key) {
Object value = cache.get(key);
// TODO need to define <T> as key.getExpectedType(). How?
}
}
See that TODO? I'd like for get() to define the return type of the 'expectedType' defined by the key parameter. E.g. if the key parameter were RED, the get() method would return a String and you could write:
String s = cache.get(Key.RED);
Is there a way to do that?
I'm thinking there isn't, but I'd love to hear of a clever solution.
Enums don't support generics, but you could use a regular class as a generic pseudo-enum:
public class Key<T> {
public static final Key<String> RED = new Key<>(String.class);
public static final Key<Integer> GREEN = new Key<>(Integer.class);
public static final Key<Short> BLUE = new Key<>(Short.class);
private final Class<T> expectedType;
private Key(Class<T> expectedType) { this.expectedType = expectedType; }
public Class<T> getExpectedType() { return expectedType; }
}
public class Cache {
private Map<Key<?>, Object> cache = new HashMap<>();
public <T> void put(Key<T> key, T value) {
cache.put(key, key.getExpectedType().cast(value));
}
public <T> T get(Key<T> key) {
return key.getExpectedType().cast(cache.get(key));
}
}
shmosel's answer is almost certainly sufficient for what you need; however, it has the slight limitation that you can't store/retrieve a generic type, because you can't get a class literal for a generic type.
Instead, you can use something like Guava's TypeCapture:
abstract class GenericKey<T> {
Type getExpectedType() {
return ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
which is a bit of reflective grossness that you shouldn't spend too much time looking at.
Notice that it's abstract, so you have to instantiate like:
new GenericKey<Integer>() {}
This is creating an anonymous subclass of GenericKey, which is part of the magic that makes it work with generic types.
Then, it's basically the same:
public class Cache {
private Map<GenericKey<?>, Object> cache = new HashMap<>();
public <T> void put(GenericKey<T> key, T value) {
cache.put(key.getExpectedType(), value);
}
public <T> T get(GenericKey<T> key) {
return (T) cache.get(key.getExpectedType());
}
}
Now you could have a GenericKey<List<Integer>>, using new new GenericKey<List<Integer>() {}, if you should so desire.
The downside of this approach is that you lose the ability to do checking on the value on the way in/out of the cache, so you could get heap pollution if you are careless with raw types.

Get Enum constant by value

I want to create a method, which returns enum constants by it's value.
I have multiple enum classes which looks like this one (some of them have getter variables with different names):
#AllArgsConstructor
public enum PhoneType {
MOBILE("Mobile", 3),
HOME("Home", 6),
WORK("Work", 7);
#Getter
String type;
#Getter
int id;
}
I used this stream to get the enum constant:
int phoneTypeId = 3;
PhoneType phoneType = Arrays.stream(PhoneType.values())
.filter(p -> p.getId() == phoneTypeId)
.findFirst()
.orElseThrow(() -> new RuntimeException("Not able to find Enum...."));
System.out.println(phoneType.getType());
output is: "Mobile"
Now I want to create a method which will work for different enum classes. I started with something like this, but I don't know how to rewrite the filter line, to make it work for any enum class. It would be ideal to pass this "p -> p.getId() == phoneTypeId" as input parameter to this method. Any ideas?
public static <E extends Enum<?>> E getEnumByValue(Class<E> enumClass) {
return Arrays.stream(enumClass.getEnumConstants())
.filter(p -> p.getId() == phoneTypeId)
.findFirst()
.orElseThrow(() -> new RuntimeException("Not able to find Enum...."));
}
(I know that it's possible to do that if I implement interface for all enum classes, but then enum variables would have to have the same names.)
You can pass it as a Predicate:
public static <E extends Enum<?>> Optional<E> getEnumByValue(Class<E> enumClass, Predicate<E> predicate) {
return Arrays.stream(enumClass.getEnumConstants())
.filter(predicate)
.findFirst();
}
(Note: the method should actually return an Optional<E>)
If all enums have ids, you can still implement a common interface:
interface Identifiable {
int getId();
}
enum PhoneType implements Identifiable {
...
}
public static <E extends Enum<?> & Identifiable> Optional<E> getEnumById(Class<E> enumClass, int id) {
return Arrays.stream(enumClass.getEnumConstants())
.filter(e -> e.getId() == id)
.findFirst();
}
Optional<PhoneType> phone = getEnumById(PhoneType.class, phoneTypeId);

How to use supplier interface to create instances of different classes? [duplicate]

Why do suppliers only support no-arg constructors?
If the default constructor is present, I can do this:
create(Foo::new)
But if the only constructor takes a String, I have to do this:
create(() -> new Foo("hello"))
But, a 1-arg constructor for T that takes a String is compatible with Function<String,T>:
Function<String, Foo> fooSupplier = Foo::new;
Which constructor is selected is treated as an overload selection problem, based on the shape of the target type.
That's just a limitation of the method reference syntax -- that you can't pass in any of the arguments. It's just how the syntax works.
If you like method references so much, you can write a bind method by yourself and use it:
public static <T, R> Supplier<R> bind(Function<T,R> fn, T val) {
return () -> fn.apply(val);
}
create(bind(Foo::new, "hello"));
The Supplier<T> interface represents a function with a signature of () -> T, meaning it takes no parameters and returns something of type T. Method references that you provide as arguments must follow that signature in order to be passed in.
If you want to create a Supplier<Foo> that works with the constructor, you can use the general bind method that #Tagir Valeev suggests, or you make a more specialized one.
If you want a Supplier<Foo> that always uses that "hello" String, you could define it one of two different ways: as a method or a Supplier<Foo> variable.
method:
static Foo makeFoo() { return new Foo("hello"); }
variable:
static Supplier<Foo> makeFoo = () -> new Foo("hello");
You can pass in the method with a method reference(create(WhateverClassItIsOn::makeFoo);), and the variable can be passed in simply using the name create(WhateverClassItIsOn.makeFoo);.
The method is a little bit more preferable because it is easier to use outside of the context of being passed as a method reference, and it's also able to be used in the instance that someone requires their own specialized functional interface that is also () -> T or is () -> Foo specifically.
If you want to use a Supplier that can take any String as an argument, you should use something like the bind method #Tagir mentioned, bypassing the need to supply the Function:
Supplier<Foo> makeFooFromString(String str) { return () -> new Foo(str); }
You can pass this as an argument like this: create(makeFooFromString("hello"));
Although, maybe you should change all the "make..." calls to "supply..." calls, just to make it a little clearer.
Why do suppliers only work with no-arg constructors?
Because a 1-arg constructor is isomorphic to a SAM interface with 1 argument and 1 return value, such as java.util.function.Function<T,R>'s R apply(T).
On the other hand Supplier<T>'s T get() is isomorphic to a zero arg constructor.
They are simply not compatible. Either your create() method needs to be polymorphic to accept various functional interfaces and act differently depending on which arguments are supplied or you have to write a lambda body to act as glue code between the two signatures.
What is your unmet expectation here? What should happen in your opinion?
Pair the Supplier with a FunctionalInterface.
Here's some sample code I put together to demonstrate "binding" a constructor reference to a specific constructor with Function and also different ways of defining and invoking the "factory" constructor references.
import java.io.Serializable;
import java.util.Date;
import org.junit.Test;
public class FunctionalInterfaceConstructor {
#Test
public void testVarFactory() throws Exception {
DateVar dateVar = makeVar("D", "Date", DateVar::new);
dateVar.setValue(new Date());
System.out.println(dateVar);
DateVar dateTypedVar = makeTypedVar("D", "Date", new Date(), DateVar::new);
System.out.println(dateTypedVar);
TypedVarFactory<Date, DateVar> dateTypedFactory = DateVar::new;
System.out.println(dateTypedFactory.apply("D", "Date", new Date()));
BooleanVar booleanVar = makeVar("B", "Boolean", BooleanVar::new);
booleanVar.setValue(true);
System.out.println(booleanVar);
BooleanVar booleanTypedVar = makeTypedVar("B", "Boolean", true, BooleanVar::new);
System.out.println(booleanTypedVar);
TypedVarFactory<Boolean, BooleanVar> booleanTypedFactory = BooleanVar::new;
System.out.println(booleanTypedFactory.apply("B", "Boolean", true));
}
private <V extends Var<T>, T extends Serializable> V makeVar(final String name, final String displayName,
final VarFactory<V> varFactory) {
V var = varFactory.apply(name, displayName);
return var;
}
private <V extends Var<T>, T extends Serializable> V makeTypedVar(final String name, final String displayName, final T value,
final TypedVarFactory<T, V> varFactory) {
V var = varFactory.apply(name, displayName, value);
return var;
}
#FunctionalInterface
static interface VarFactory<R> {
// Don't need type variables for name and displayName because they are always String
R apply(String name, String displayName);
}
#FunctionalInterface
static interface TypedVarFactory<T extends Serializable, R extends Var<T>> {
R apply(String name, String displayName, T value);
}
static class Var<T extends Serializable> {
private String name;
private String displayName;
private T value;
public Var(final String name, final String displayName) {
this.name = name;
this.displayName = displayName;
}
public Var(final String name, final String displayName, final T value) {
this(name, displayName);
this.value = value;
}
public void setValue(final T value) {
this.value = value;
}
#Override
public String toString() {
return String.format("%s[name=%s, displayName=%s, value=%s]", getClass().getSimpleName(), this.name, this.displayName,
this.value);
}
}
static class DateVar extends Var<Date> {
public DateVar(final String name, final String displayName) {
super(name, displayName);
}
public DateVar(final String name, final String displayName, final Date value) {
super(name, displayName, value);
}
}
static class BooleanVar extends Var<Boolean> {
public BooleanVar(final String name, final String displayName) {
super(name, displayName);
}
public BooleanVar(final String name, final String displayName, final Boolean value) {
super(name, displayName, value);
}
}
}
When looking for a solution to the parametrized Supplier problem, I found the above answers helpful and applied the suggestions:
private static <T, R> Supplier<String> failedMessageSupplier(Function<String,String> fn, String msgPrefix, String ... customMessages) {
final String msgString = new StringBuilder(msgPrefix).append(" - ").append(String.join("\n", customMessages)).toString();
return () -> fn.apply(msgString);
}
It is invoked like this:
failedMessageSupplier(String::new, msgPrefix, customMsg);
Not quite satisfied yet with the abundant static function parameter, I dug further and with Function.identity(), I came to the following result:
private final static Supplier<String> failedMessageSupplier(final String msgPrefix, final String ... customMessages) {
final String msgString = new StringBuilder(msgPrefix).append(" - ").append(String.join("\n", customMessages)).toString();
return () -> (String)Function.identity().apply(msgString);
};
Invocation now without the static function parameter:
failedMessageSupplier(msgPrefix, customMsg)
Since Function.identity() returns a function of the type Object, and so does the subsequent call of apply(msgString), a cast to String is required - or whatever the type, apply() is being fed with.
This method allows for e. g. using multiple parameters, dynamic string processing, string constants prefixes, suffixes and so on.
Using identity should theoretically also have a slight edge over String::new, which will always create a new string.
As Jacob Zimmerman already pointed out, the simpler parametrized form
Supplier<Foo> makeFooFromString(String str1, String str2) {
return () -> new Foo(str1, str2);
}
is always possible. Whether or not this makes sense in a context, depends.
As also described above, static Method reference calls require the corresponding method's number and type of return / parameters to match the ones expected by the function-consuming (stream) method.
If you have a constructor for new Klass(ConstructorObject) then you can use Function<ConstructorObject, Klass> like this:
interface Interface {
static Klass createKlass(Function<Map<String,Integer>, Klass> func, Map<String, Integer> input) {
return func.apply(input);
}
}
class Klass {
private Integer integer;
Klass(Map<String, Integer> map) {
this.integer = map.get("integer");
}
public static void main(String[] args) {
Map<String, Integer> input = new HashMap<>();
input.put("integer", 1);
Klass klazz = Interface.createKlass(Klass::new, input);
System.out.println(klazz.integer);
}
}

Java Map with variable generics as values

So here's a slightly tricky question (for me).
I have a generic object. Call it MyObject. This object has a method which returns something of the type T:
public class MyObject<T>
{
private T _t;
public MyObject(T t)
{
_t = t;
}
//...
public T get()
{
return _t;
}
}
(Obviously my "MyObject" does a bit more but that's the gist).
Now, I want to have a map of this type:
Map<String, MyObject<?>> m = new HashMap<>();
I want to be able to fetch maps using some predefined string name, and these maps can be of any MyObject. For example, I could call:
m.put("map_1", new MyObject<String>("String"));
m.put("map_2", new MyObject<Integer>(new Integer(3));
m.put("map_3", new MyObject<Long>(new Long(5));
etc.
But - and here's the tricky part - I want the map to "remember" the parameterized type of MyObject when I fetch some value from the map. Using
m.get("map_1");
would return a
MyObject<Object>
type, since the map was defined as containing
MyObject<?>
values. Thus:
m.get("map_1").get() // <-- This is an Object, not a String!
What modification (if any) is possible, in order to be able to get the correct - full - information regarding the MyObject fetched object, such that invoking the last line (m.get("map_1")) would return a
MyObject<String>
Thanks :)
Amir.
Typesafe Heterogeneous Containers from Joshua Bloch's Effective Java might work here. Basically you add a Class object to represent the type.
public class MyObject<T>
{
private T _t;
private Class<T> type;
public MyObject( Class<T> type, T t)
{
_t = t;
this.type = type;
}
//...
public T get()
{
return _t;
}
public Class<T> getType() { return type; }
}
Then you could do something like this:
public <T> T get( Map<String, MyObject<?>> map, String key, Class<T> type ) {
return type.cast( m.get( key ).get() );
}
Which is safe and will compile, but will throw a runtime error if you get the type wrong.
(Note I didn't actually compile that, so I might have syntax errors floating around. But most folks don't know how to use Class to cast objects.)
You can get the class.
Class c = m.get("map_1").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
Here is a full test.
public class GenericsTest {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Map<String, MyObject<?>> map = new HashMap<>();
MyObject<String> obj = new MyObject<>("hello");
map.put("greeting", obj);
Class c = map.get("greeting").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
}
static class MyObject<T> {
T t;
public MyObject(T t) {
this.t = t;
}
T get() {
return t;
}
}
}
The type system only knows about types, not objects, and therefore can not distinguish "key1" from "key2", because both are of type String.
If keys have different types, the easiest way is to encapsulate a weakly typed map, and use reflective casts to prove to the compiler the types are correct:
class Favorites {
private Map<Class<?>,?> map = new HashMap<>();
<V> V get(Class<V> clazz) {
return clazz.cast(map.get(clazz));
}
<V> void put(Class<V> clazz, V value) {
map.put(clazz, value);
}
}
Favorites favs = new Favorites();
favs.put(String.class, "hello");
favs.put(Integer.class, 42);
favs.get(String.class).charAt(1);

Finding enum value with Java 8 Stream API

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

Categories

Resources