There is not a very much better way of doing it than you have it - it might be nicer if you extracted it to a method, but that's really it.
Another way might be to construct Optionals from all values, and then use Optional.filter to map empty values to empty optionals:
yearStreams.map(Optional::of)
.map(opt -> opt.filter(Predicate.not(String::isEmpty)));
Is this better? Probably not.
Yet another way would be to make use of something like Guava's Strings.emptyToNull (other libraries are available), which turns your empty strings into null first; and then use Optional.ofNullable to turn non-nulls and nulls into non-empty and empty Optionals, respectively:
yearStreams.map(Strings::emptyToNull)
.map(Optional::ofNullable)
Answer from Andy Turner on Stack OverflowThere is not a very much better way of doing it than you have it - it might be nicer if you extracted it to a method, but that's really it.
Another way might be to construct Optionals from all values, and then use Optional.filter to map empty values to empty optionals:
yearStreams.map(Optional::of)
.map(opt -> opt.filter(Predicate.not(String::isEmpty)));
Is this better? Probably not.
Yet another way would be to make use of something like Guava's Strings.emptyToNull (other libraries are available), which turns your empty strings into null first; and then use Optional.ofNullable to turn non-nulls and nulls into non-empty and empty Optionals, respectively:
yearStreams.map(Strings::emptyToNull)
.map(Optional::ofNullable)
You can just simply use filter to validate and then only map
Stream<Year> yearsFound = yearStrings.filter(yearString->!yearString.isEmpty()).map(Year::parse)
Just put the condition into the lambda itself, e.g.
animalMap.entrySet().stream()
.forEach(
pair -> {
if (pair.getValue() != null) {
myMap.put(pair.getKey(), pair.getValue());
} else {
myList.add(pair.getKey());
}
}
);
This can be simplified and made more readable using Map.forEach, as suggested by Jorn Vernee:
animalMap.forEach(
(key, value) -> {
if (value != null) {
myMap.put(key, value);
} else {
myList.add(key);
}
}
);
Of course, these solutions assume that both collections (myMap and myList) are declared and initialized prior to the above pieces of code.
In most cases, when you find yourself using forEach on a Stream, you should rethink whether you are using the right tool for your job or whether you are using it the right way.
Generally, you should look for an appropriate terminal operation doing what you want to achieve or for an appropriate Collector. Now, there are Collectors for producing Maps and Lists, but no out of-the-box collector for combining two different collectors, based on a predicate.
Now, this answer contains a collector for combining two collectors. Using this collector, you can achieve the task as
Pair<Map<KeyType, Animal>, List<KeyType>> pair = animalMap.entrySet().stream()
.collect(conditional(entry -> entry.getValue() != null,
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue),
Collectors.mapping(Map.Entry::getKey, Collectors.toList()) ));
Map<KeyType,Animal> myMap = pair.a;
List<KeyType> myList = pair.b;
But maybe, you can solve this specific task in a simpler way. One of you results matches the input type; it’s the same map just stripped off the entries which map to null. If your original map is mutable and you don’t need it afterwards, you can just collect the list and remove these keys from the original map as they are mutually exclusive:
List<KeyType> myList=animalMap.entrySet().stream()
.filter(pair -> pair.getValue() == null)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
animalMap.keySet().removeAll(myList);
Note that you can remove mappings to null even without having the list of the other keys:
animalMap.values().removeIf(Objects::isNull);
or
animalMap.values().removeAll(Collections.singleton(null));
If you can’t (or don’t want to) modify the original map, there is still a solution without a custom collector. As hinted in Alexis C.’s answer, partitioningBy is going into the right direction, but you may simplify it:
Map<Boolean,Map<KeyType,Animal>> tmp = animalMap.entrySet().stream()
.collect(Collectors.partitioningBy(pair -> pair.getValue() != null,
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
Map<KeyType,Animal> myMap = tmp.get(true);
List<KeyType> myList = new ArrayList<>(tmp.get(false).keySet());
The bottom line is, don’t forget about ordinary Collection operations, you don’t have to do everything with the new Stream API.
Java 8 streams weren't designed to support this kind of operation. From the jdk:
A stream should be operated on (invoking an intermediate or terminal stream operation) only once. This rules out, for example, "forked" streams, where the same source feeds two or more pipelines, or multiple traversals of the same stream.
If you can store it in memory you can use Collectors.partitioningBy if you have just two types and go by with a Map<Boolean, List>. Otherwise use Collectors.groupingBy.
Simply test each element, and act accordingly.
lines.forEach(line -> {
if (isTypeA(line)) processTypeA(line);
else processTypeB(line);
});
This behavior could be hidden in a helper method:
public static <T> Consumer<T> branch(Predicate<? super T> test,
Consumer<? super T> t,
Consumer<? super T> f) {
return o -> {
if (test.test(o)) t.accept(o);
else f.accept(o);
};
}
Then the usage would look like this:
lines.forEach(branch(this::isTypeA, this::processTypeA, this::processTypeB));
Tangential Note
The Files.lines() method does not close the underlying file, so you must use it like this:
try (Stream<String> lines = Files.lines(path, encoding)) {
lines.forEach(...);
}
Variables of Stream type throw up a bit of a red flag for me, so I prefer to manage a BufferedReader directly:
try (BufferedReader lines = Files.newBufferedReader(path, encoding)) {
lines.lines().forEach(...);
}
You can do something like this:
if(returnOptional().isPresent) {
List<Object> list = db.findAllById(id);
list.stream().map(object -> {
if(/*predicate logic*/) {
// perform function if predicate logic true
}
else {
// perform function if predicate logic false
}
return object;
});
}
but as philip mentioned technically you cannot split a stream into two streams and collect.
Optional.ofNullable(list).ifPresent( y -> {
if (y.stream().anyMatch(x -> /*predicate*/)) {
System.out.println("a");
} else {
System.out.println("b");
}
});
You've filtered out all elements that aren't PLACEHOLDER. You need to add that filter logic to your map operation:
final Map<String, String> output = input.entrySet().stream()
.map(e -> {
if (!e.getKey().equals("PLACEHOLDER")) {
return e;
}
if (condition) {
return new AbstractMap.SimpleImmutableEntry<>("Apple", e.getValue());
}
return new AbstractMap.SimpleImmutableEntry<>("Netflix", e.getValue());
}).collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
But as you are guaranteed to only have a single instance of PLACEHOLDER in the Map, you can just do
String placeholderData = input.remove("PLACEHOLDER");
if (placeholderData != null) {
input.put(condition ? "Apple" : "Netflix", placeholderData);
}
If you really want to do it using Streams, you just need to move the conditional logic to the collection phase, like that:
boolean condition = true;
map.entrySet().stream().collect(Collectors.toMap(
entry -> mapKey(entry.getKey(), condition), Map.Entry::getValue
));
where:
private static String mapKey(String key, boolean condition) {
if (!"PLACEHOLDER".equals(key)) {
return key;
}
if (condition) {
return "Apple";
} else {
return "Netflix";
}
}
However, the second part of Boris the Spider's answer using Map.remove and Map.put seems the best way to go.
Perhaps you are overcomplicating this.
List<User> users = new ArrayList<>();
users.stream()
.filter(Objects::nonNull)
.forEach(u -> u.setRole(u.isActive()?"ABC":"XYZ"));
I am guessing the desired behavior, please correct me if I'm wrong.
You can use a block inside lambda expressions:
List<User> users = userDao.getAllByCompanyId(companyId);
users.stream().filter(Objects::nonNull).forEach(user -> {
if (user.isPresent()) {
user.setRole("ABC");
} else {
user.setRole("XYZ");
}
});
Try using map:
map(x -> x % 2 == 0? x / 2: x * x);
Let me know if this works for you.
You can learn more about map and filter here
As you are transforming data (performing a math operation), you cannot use Filter here. Filter is used to filter out elements in your stream. For example if you only want to preserve the even numbers, you could use a Filter
What you need to use is the Map, as you already did. Do note that a map should always return data. Your code is missing this return statement.
To make it more readable, you could split your mapping logic in a method. This makes your stream easy to read and easy to follow (when you give the method a good name ofcourse).
Code example
List<Integer> output = intArray.stream()
.map(test::divideOrPow)
.collect(Collectors.toList());
private int divideOrPow(intx) {
if (x % 2 == 0) {
return x / 2;
} else {
return x * x;
}
}
You can use
public ShapeType resolveShapeType(final List<Shape> shapes) {
int sides = shapes.stream()
.mapToInt(Shape::getSideCount)
.filter(count -> count==4 || count==6)
.max().orElse(0);
return sides==6? ShapeType.HEXA: sides==4? ShapeType.RECT: ShapeType.GENERIC;
}
This maps each element to its side count and reduces them to the preferred type, which happens to be the maximum count here, so no custom reduction function is needed.
This isn’t short-circuiting, but for most use cases, it will be sufficient. If you want to reduce the number of operations to the necessary minimum, things will be more complicated.
public ShapeType resolveShapeType(final List<Shape> shapes) {
OptionalInt first = IntStream.range(0, shapes.size())
.filter(index -> {
int count = shapes.get(index).getSideCount();
return count == 6 || count == 4;
})
.findFirst();
if(!first.isPresent()) return ShapeType.GENERIC;
int ix = first.getAsInt(), count = shapes.get(ix).getSideCount();
return count==6? ShapeType.HEXA: shapes.subList(ix+1, shapes.size()).stream()
.anyMatch(shape -> shape.getSideCount()==6)? ShapeType.HEXA: ShapeType.RECT;
}
We know that we can stop at the first HEXA, but to avoid a second pass, it’s necessary to remember whether there was an occurence of RECT for the case there is no HEXA. So this searches for the first element that is either, a RECT or HEXA. If there is none, GENERIC is returned, otherwise, if the first was not a HEXA, the remaining elements are checked for an element of the HEXA kind. Note that for processing the remainder after the first RECT, no filter is needed as it is implied that shapes that are neither, RECT nor HEXA, can’t fulfill the condition.
But it should also be obvious that this code, trying to minimize the numbers of checks, is harder to read than an equivalent for loop.
Assuming that only the three types of shapes can be present in the list, an alternative would be:
Set<Integer> sides = shapes.stream()
.map(Shape::getSideCount)
.collect(toSet());
if (sides.contains(6)) return HEXA;
else if (sides.contains(4)) return RECTANGLE;
else return GENERIC;
But I think the most straightforward (and efficient) way would be a good old for loop:
ShapeType st = GENERIC;
for (Shape s : shapes) {
if (s.getSideCount() == 6) return HEXA;
if (s.getSideCount() == 4) st = RECTANGLE;
}
return st;
You may do it like so,
List<String> interchanged = cs.stream()
.map(s -> s.equals("manager") ? "agent" : s.equals("agent") ? "manager" : s)
.collect(Collectors.toList());
Another way to do that using List.replaceAll could be:
List<String> cs = Arrays.asList("agent", "manager", "admin");
cs.replaceAll(s -> {
if (s.equals("manager")) {
return "agent";
}
if (s.equals("agent")) {
return "manager";
}
return s;
});
This is what you want in case you use java-9 or newer (I recommend you to tag the version used):
String rel = release
.or(() -> project.getReleases().stream()
.filter(Release::isActive)
.map(Release::getName)
.findFirst())
.orElse("");
Basically Optional#or returns either the present value from the original Optional or provides a Supplier<Optional> providing such value in lazy way (will not be called in case the former value is present).
Use the advantage that Stream#findFirst returns Optional hence such call is compatible with the method needs. Finally call orElse("") as the last possible value if no usable value was present in the previous attempts.
You should use Optional#orElseGet for this
Why orElseGet and not orElse ?
Because orElseGet accepts a Supplier which will only be invoked if your Optional is empty. So you won't have the overhead of Stream creation etc.
Example
String rel = release.orElseGet(() -> project.getReleases()
.stream()
.filter(Release::isActive)
.map(Release::getName)
.findFirst()
.orElse(""));