I have a list of valid values for a type:
Set<String> validTypes = ImmutableSet.of("TypeA", "TypeB", "TypeC");
From a given list I want to extract the first value which has a valid type. In this scenario I would write something of this sort:
public class A{
private String type;
private String member;
}
List<A> classAList;
classAList.stream()
.filter(a -> validTypes.contains(a.getType()))
.findFirst();
However I would like to give preference to TypeA, i.e. if classAList has TypeA and TypeB, I want the object which has typeA. To do this one approach I've is:
Set<String> preferredValidTypes = ImmutableSet.of("TypeA");
classAList.stream()
.filter(a -> preferredValidTypes.contains(a.getType()))
.findFirst()
.orElseGet(() -> {
return classAList.stream()
.filter(a -> validTypes.contains(a.getType()))
.findFirst();
}
Is there a better approach?
filter list by type, order by type, collect to list, then just get first element
List<A> collect = classAList.stream()
.filter(a -> validTypes.contains(a.getType()))
.sorted(Comparator.comparing(A::getType))
.collect(Collectors.toList());
System.out.println(collect.get(0));
You can use a custom comparator like:
Comparator<A> comparator = (o1, o2) -> {
if (preferredValidTypes.contains(o1.getType()) && !preferredValidTypes.contains(o2.getType())) {
return 1;
} else if (!preferredValidTypes.contains(o1.getType()) && preferredValidTypes.contains(o2.getType())) {
return -1;
} else {
return 0;
}
};
to sort the list and then findFirst from that list with your conditiion.
i don't like the answers already given which use Comparator. Sorting is an expensive operation. You can do it with one walk through the list. Once you find a preferred value, you can break out, otherwise you continue to the end to find a valid.
In this case anyMatch can provide the possibility to break out from the stream processing:
MyVerifier verifier=new MyVerifier(validTypes,preferredValidTypes);
classAList.stream()
.anyMatch(verifier);
System.out.println("Preferred found:"+verifier.preferred);
System.out.println("Valid found:"+verifier.valid);
public static class MyVerifier implements Predicate<A> {
private Set<String> validTypes;
private Set<String> preferredValidTypes;
A preferred=null;
A valid=null;
public MyVerifier(Set<String> validTypes, Set<String> preferredValidTypes) {
super();
this.validTypes = validTypes;
this.preferredValidTypes = preferredValidTypes;
}
#Override
public boolean test(A a) {
if(preferred==null && preferredValidTypes.contains(a.getType())) {
preferred=a;
// we can stop because we found the first preferred
return true;
} else if(valid==null && validTypes.contains(a.getType())) {
valid=a;
}
return false;
}
}
One can, of course, define two lists, one with all valid types, and one with the preferred types.
However, here is another approach. Define one list, or actually, a Map, with the keys being the valid types, and the boolean values being whether the type is preferred.
Map<String, Boolean> validTypes = ImmutableMap.of(
"TypeA", false,
"TypeB", false,
"TypeC", true
);
Using AtomicReference
One option is the following:
AtomicReference<A> ref = new AtomicReference<>();
listOfAs.stream()
.filter(t -> validTypes.containsKey(t.getType()))
.anyMatch(t -> validTypes.get(ref.updateAndGet(u -> t).getType()));
AtomicReference now contains a preferred A if available, or another valid A, or if the stream is empty, then it contains null. This stream operation short-circuits if an A with a preferred type is found.
The drawback of this option is that it creates side-effects, which is discouraged.
Using distinct()
Another suggestion would be the following. It uses the same map structure, using a boolean to indicate which values are preferred. However, it does not create side effects.
Map<Boolean, A> map = listOfAs.stream()
.filter(t -> validTypes.containsKey(t.getType()))
.map(t -> new Carrier<>(validTypes.get(t.getType()), t))
.distinct()
.limit(2)
.collect(Collectors.toMap(Carrier::getKey, Carrier::getValue));
It works as follows.
filter discards any element that is not a valid type.
Then, each element is mapped to a Carrier<Boolean, A> instance. A Carrier is a Map.Entry<K, V> which implements its equals and hashCode methods regarding only the key; the value does not matter. This is necessary for the following step,
distinct(), which discards any duplicate element. This way, only one preferred type and only one valid type is found.
We limit the stream to have 2 elements, one for each boolean. This is because the stream, which is lazy, stops evaluating after both booleans are found.
At last, we collect the Carrier elements into a Map.
The map contains now the following elements:
Boolean.TRUE => A with a preferred type
Boolean.FALSE => A with a valid type
Retrieve the appropriate element using
A a = map.getOrDefault(true, map.get(false)); // null if not found
Well you have to take care into account that sorting is stable, that is equal elements will appear in the same order as the initial source - and you need that to correctly get the first element from that List<A> that will satisfy your requirement, thus:
String priorityType = "TypeB";
Stream.of(new A("TypeA", "A"),
new A("TypeB", "B"),
new A("TypeC", "C"))
.sorted(Comparator.comparing(A::getType, Comparator.comparing(priorityType::equals)).reversed())
.filter(x -> validTypes.contains(priorityType))
.findFirst()
.orElseThrow(RuntimeException::new);
In Java8 you can use streams:
public static Carnet findByCodeIsIn(Collection<Carnet> listCarnet, String codeIsIn) {
return listCarnet.stream().filter(carnet -> codeIsIn.equals(carnet.getCodeIsin())).findFirst().orElse(null);
}
Additionally, in case you have many different objects (not only Carnet) or you want to find it by different properties (not only by cideIsin), you could build an utility class, to ecapsulate this logic in it:
public final class FindUtils {
public static <T> T findByProperty(Collection<T> col, Predicate<T> filter) {
return col.stream().filter(filter).findFirst().orElse(null);
}
}
public final class CarnetUtils {
public static Carnet findByCodeTitre(Collection<Carnet> listCarnet, String codeTitre) {
return FindUtils.findByProperty(listCarnet, carnet -> codeTitre.equals(carnet.getCodeTitre()));
}
public static Carnet findByNomTitre(Collection<Carnet> listCarnet, String nomTitre) {
return FindUtils.findByProperty(listCarnet, carnet -> nomTitre.equals(carnet.getNomTitre()));
}
public static Carnet findByCodeIsIn(Collection<Carnet> listCarnet, String codeIsin) {
return FindUtils.findByProperty(listCarnet, carnet -> codeIsin.equals(carnet.getCodeIsin()));
}
}
If you have preferred valid types in other collection so you can follow this code.
Map<String,A> groupByType = classAList
.stream()
/* additional filter to grouping by valid types.*/
//.filter(a->validTypes.contains(a.getType()))
.collect(Collectors.toMap(A::getType, Function.identity(),(v1, v2)->v1));
then use:
A result = preferredValidTypes
.stream()
.map(groupByType::get)
.findFirst()
.orElseThrow(RuntimeException::new);
or just group by preferred valid types
A result2 = classAList
.stream()
.filter(a -> preferredValidTypes.contains(a.getType()))
.collect(Collectors.toMap(A::getType, Function.identity(), (v1, v2) -> v1))
.entrySet()
.stream()
.findFirst()
.map(Map.Entry::getValue)
.orElseThrow(RuntimeException::new);
I have a requirement to validate a field against some predefined values (that can grow in future). So for this I have created a Enum and defined a method that returns the stream of the allowed values.
public enum EnumDemo {
VERSION("1.0.0","2.0.3");
private List<String> ver;
EnumDemo(String... ver) {
this.ver = Arrays.asList(ver);
}
public List<String> getVer() {
return ver;
}
public static Stream<EnumDemo> stream() {
return Arrays.stream(EnumDemo.values());
}
}
Now I need to validate a field against the values defined in this Enum.
I'm using:
Optional<EnumDemo> ab = EnumDemo.stream()
.map(l -> {l.getVer().stream()
.filter(c -> c.equals("2.0.3"))
.findFirst();})
.findFirst();
System.out.println(ab.get().getVer());
But it is giving me compilation error. Any help would be appreciated.
Edit:
Compilation Error:
The method map(Function<? super EnumDemo,? extends R>) in the type Stream<EnumDemo> is not applicable for the arguments ((<no type> l) -> {})
You should write it this way:
Optional<EnumDemo> ab = EnumDemo.stream().filter(l -> l.getVer().contains("2.0.3"))
.findFirst();
By the way, it wasn't working because you used {} for the lambda expression, so it was expecting a return statement in the {}. You could either remove the {} (along with the ;) or add in the return.
Anyway the original codes looked confusing, not sure if I guessed the intention correctly, but this implementation should be clearer.
Edit
Based on your comment, this is what you need:
EnumDemo.stream().flatMap(l -> l.getVer().stream())
.filter("2.0.3"::equals)
.findAny()
.ifPresent(System.out::println);
Update
Holger commented that there is a shorter and more meaningful way, with better performance:
if(EnumDemo.stream()
.anyMatch(l -> l.getVer().contains(userString))) {
System.out.println(userString);
}
To understand it, you have to think about lambdas. Lambdas represent interfaces but are specially treated by the JVM, so not every Lambda needs a class to represent. (Stateless lambdas can be just methods).
Now when looking at the map() method in the Stream interface:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
You see that it expects an implementation of the Function interface. You now have many different ways to provide that mapper. In this example lets map from Object to String:
1. Using an inline lambda:
.map(o -> o.toString())
2. Using a multiline lambda:
.map(o -> {
return o.toString();
})
3. Using method references:
.map(Object::toString)
4. Using an anonymous class:
.map(new Function<Object, String>(){
#Override
public String apply(Object o){
return o.toString();
}
})
Your current code uses the 2. approach. But without a return statement. This is even better seen when looking at the anonymous class at 4.. It seems natural, that when not using a return statement in a method that no value is returned.
And that's why you get the compilation error.
You just have to add the return statement:
.map(l -> {
return l.getVer().stream()
.filter(c -> c.equals("2.0.3"))
.findFirst();
});
Or remove the brackets {}:
.map(l -> l.getVer().stream()
.filter(c -> c.equals("2.0.3"))
.findFirst());
Or even use the approach provided by #Jai in his answer. Which works even better, than what you currently have.
You are using lambda expression and not returning any value so it is giving compilation error. It is better to use ifPresent()
String val="2.0.3";
EnumDemo.stream()
.flatMap(l -> l.getVer().stream())
.filter(c -> c.equals(val))
.findAny()
.ifPresent(x -> System.out.println(x));
I have an Optional object that contains a list. I want to map each object in this list to another list, and return the resulting list.
That is:
public List<Bar> get(int id) {
Optional<Foo> optfoo = dao.getById(id);
return optfoo.map(foo -> foo.getBazList.stream().map(baz -> baz.getBar()))
}
Is there a clean way of doing that without having streams within streams?
I think that flatMap might be the solution but I can't figure out how to use it here.
There isn't. flatMap in case of Optional is to flatten a possible Optional<Optional<T>> to Optional<T>. So this is correct.
public List<Bar> get(Optional<Foo> foo) {
return foo.map(x -> x.getBazList()
.stream()
.map(Baz::getBar)
.collect(Collectors.toList()))
.orElse(Collections.emptyList());
}
A Java 9 approach would be the folloing:
public List<Bar> get(Optional<Foo> foo) {
return foo.map(Foo::getBazList)
.stream()
.flatMap(Collection::stream)
.map(Baz::getBar)
.collect(Collectors.toList());
}
That said, you should avoid using Optionals as parameters, see here.
I'm in a bit of confusion right now, so I have a method that should return CompletableFuture<List<A>>
inside the method is:
CompletableFuture<List<String>> toReturn = asyncCall().thenApply(....)
.thenCompose(listOfStuff -> convertToList(listOfStuff.stream().map(
key -> asyncCall2(key)
.thenApply(optionalValue -> optionalValue.orElse(null))
).collect(Collectors.toList()));
and convertToList() simply joins futures to convert CompletableFuture<List<ComputableFuture<A>>> into CompletableFuture<List<A>>
Basically my intention is to filter null values that emerge from optionalValue.orElse(null) And it would be easy to do filter before collecting it all to list in the last line, but if I use it just before .collect it is working over CompletableFutures
I suspect there's a lot of restructuring I can do in my code.
EDIT:
private<T> CompletableFuture<List<T>> convertToList(List<CompletableFuture<T>> toConvert) {
return CompletableFuture.allOf(toConvert.toArray(new CompletableFuture[toConvert.size()]))
.thenApply(v -> toConvert.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList())
);
}
The best way would probably be to change convertToList() so that it does not return a future of list, but of stream instead:
private <T> CompletableFuture<Stream<T>> convertToFutureOfStream(List<CompletableFuture<T>> toConvert) {
return CompletableFuture.allOf(toConvert.stream().toArray(CompletableFuture[]::new))
.thenApply(
v -> toConvert.stream()
.map(CompletableFuture::join)
);
}
This will be more reusable as the method will allow better chaining and will not force the caller to work with a list, while still allowing to easily get a list with a simple collect.
You can then simply filter that stream to remove empty optionals:
CompletableFuture<List<String>> toReturn = asyncCall()
.thenCompose(listOfStuff -> convertToFutureOfStream(
listOfStuff.stream()
.map(this::asyncCall2)
.collect(Collectors.toList())
)
.thenApply(stream ->
stream.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList())
)
);
You can even improve this a little further by changing convertToFutureOfStream() to take a stream as argument as well:
private <T> CompletableFuture<Stream<T>> convertToFutureOfStream(Stream<CompletableFuture<T>> stream) {
CompletableFuture<T>[] futures = stream.toArray(CompletableFuture[]::new);
return CompletableFuture.allOf(futures)
.thenApply(v -> Arrays.stream(futures).map(CompletableFuture::join));
}
(unfortunately this raises an unchecked assignment warning because of the array of generic types)
Which then gives
CompletableFuture<List<String>> toReturn = asyncCall()
.thenCompose(listOfStuff -> convertToFutureOfStream(
listOfStuff.stream().map(this::asyncCall2)
)
.thenApply(stream ->
stream.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList())
)
);
Using Java 8 stream what is the best way to map a List<Integer> when you have no output for the input Integer ?
Simply return null? But now my output list size will be smaller than my input size...
List<Integer> input = Arrays.asList(0,1,2,3);
List<Integer> output = input.stream()
.map(i -> {
Integer out = crazyFunction(i);
if(out == null || out.equals(0))
return null;
return Optional.of(out);
})
.collect(Collectors.toList());
I don’t get why you (and all answers) make it so complicated. You have a mapping operation and a filtering operation. So the easiest way is to just apply these operation one after another. And unless your method already returns an Optional, there is no need to deal with Optional.
input.stream().map(i -> crazyFunction(i))
.filter(out -> out!=null && !out.equals(0))
.collect(Collectors.toList());
It may be simplified to
input.stream().map(context::crazyFunction)
.filter(out -> out!=null && !out.equals(0))
.collect(Collectors.toList());
But you seem to have a more theoretical question about what kind of List to generate, one with placeholders for absent values or one with a different size than the input list.
The simple answer is: don’t generate a list. A List is not an end in itself so you should consider for what kind of operation you need this list (or its contents) and apply the operation right as the terminal operation of the stream. Then you have your answer as the operation dictates whether absent values should be filtered out or represented by a special value (and what value that has to be).
It might be a different answer for different operations…
Replace the map call with flatMap. The map operation produces one output value per input value, whereas the flatMap operation produces any number of output values per input value -- include zero.
The most straightforward way is probably to replace the check like so:
List<Integer> output = input.stream()
.flatMap(i -> {
Integer out = crazyFunction(i);
if (out == null || out.equals(0))
return Stream.empty();
else
return Stream.of(out);
})
.collect(Collectors.toList());
A further refactoring could change crazyFunction to have it return an Optional (probably OptionalInt). If you call it from map, the result is a Stream<OptionalInt>. Then you need to flatMap that stream to remove the empty optionals:
List<Integer> output = input.stream()
.map(this::crazyFunctionReturningOptionalInt)
.flatMap(o -> o.isPresent() ? Stream.of(o.getAsInt()) : Stream.empty())
.collect(toList());
The result of the flatMap is a Stream<Integer> which boxes up the ints, but this is OK since you're going to send them into a List. If you weren't going to box the int values into a List, you could convert the Stream<OptionalInt> to an IntStream using the following:
flatMapToInt(o -> o.isPresent() ? IntStream.of(o.getAsInt()) : IntStream.empty())
For further discussion of dealing with streams of optionals, see this question and its answers.
Simpler variants of #Martin Magakian 's answer:
List<Integer> input = Arrays.asList(0,1,2,3);
List<Optional<Integer>> output =
input.stream()
.map(i -> crazyFunction(i)) // you can also use a method reference here
.map(Optional::ofNullable) // returns empty optional
// if original value is null
.map(optional -> optional.filter(out -> !out.equals(0))) // return empty optional
// if captured value is zero
.collect(Collectors.toList())
;
List<Integer> outputClean =
output.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList())
;
You can wrap the output into an Optional which may or may not contain a non-null value.
With an output: return Optional.of(out);
Without output: return Optional.<Integer>empty();
You have to wrap into an option because an array cannot contain any null value.
List<Integer> input = Arrays.asList(0,1,2,3);
List<Option<Integer>> output = input.stream()
.map(i -> {
Integer out = crazyFunction(i);
if(out == null || out.equals(0))
return Optional.<Integer>empty();
return Optional.of(out);
})
.collect(Collectors.toList());
This will make sure input.size() == output.size().
Later on you can exclude the empty Optional using:
List<Integer> outputClean = output.stream()
.filter(Optional::isPresent)
.map(i -> {
return i.get();
})
.collect(Collectors.toList());