How to use Stream instead for each loop - java

I have a loop which update an String object:
String result = "";
for (SomeObject obj: someObjectList) {
result = someMetohd(obj, result);
}
An implementation of someMethod is irrelevant:
private String someMethod(SomeObject obj, String result) {
result = result.concat(obj.toString());
return result;
}
And I want to use Stream instead a loop. How to implement it with Stream?

#SuppressWarnings("OptionalGetWithoutIsPresent")
String result = Stream.concat(Stream.of(""), someObjectList.stream())
.reduce(this::someMethod)
.get();
Your someMethod should be associative as specified in the documentation, however this is only important for parallel streams, while your code is explicitly sequential
As you always add to the result, you can consider it a first element of the stream and then use reduce method which will always merge first two elements - current result and next element
result has to be the first parameter of your someMethod
Because all elements in the stream have to be of the same type, while you have String result and SomeObject elements, you need to change the signature of someMethod to accept two Objects (and do the casts inside the method): private String someMethod(Object result, Object obj). This is the most ugly part of this solution.
You can inline the initial value of the result - no need to define result upfront
You might want to change this::someMethod depending on where this method is declared
Finally, you don't need to worry about handling Optional result, because the stream always has at least one element so it's safe to just call get()

final StringBuilder resultBuilder = new StringBuilder();
someObjectList.stream().map(SomeObject::toString).forEach(resultBuilder::append);
final String result = resultBuilder.toString();
To know more about Streams, you can check this page: http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/, I think it's very helpful.

Although the functional equivalent of what you're trying to achieve here is possible with streams, it's worth reminding you that functional and iterative ways of thinking are not necessarily compatible.
Generally you think of each element on its own, and you don't have visibility over other elements, unless you're using a special function like reduce.
Here's something that does what you've asked for:
final List<Object> objectList = Arrays.asList("a", "b", "c", "d");
String concatString = objectList.stream()
.map(e -> e.toString())
.reduce((result, element) -> result.concat(e))
.get();
Map turns the entire stream into a list, but with the toString function called separately on every element. Reduce is more complex. It can be described as an accumulation. It executes a function between the result, and the current element. In this case, it takes the first element, and concatenates it to the second. It then takes the first/second concatenation, and applies the same function to the third. And so on.
Instead of dealing with lambdas, you can also pass in methods directly, to tighten up your code a bit:
String result = objectList.stream()
.map(Object::toString)
.reduce(String::concat)
.get();

Related

type of map() and filter() operations of Optional

Are the map() and filter() of Optional are lazy like Stream?
How can I confirm their type?
There is a fundamental difference between a Stream and an Optional.
A Stream encapsulates an entire processing pipeline, gathering all operations before doing anything. This allows the implementation to pick up different processing strategies, depending on what result is actually requested. This also allows to insert modifiers like unordered() or parallel() into the chain, as at this point, nothing has been done so far, so we can alter the behavior of the subsequent actual processing.
An extreme example is Stream.of(1, 2, 3).map(function).count(), which will not process function at all in Java 9, as the invariant result of 3 can be determined without.
In contrast, an Optional is just a wrapper around a value (if not empty). Each operation will be performed immediately, to return either, a new Optional encapsulating the new value or an empty Optional. In Java 8, all methods returning an Optional, i.e.map, flatMap or filter, will just return an empty optional when being applied to an empty optional, so when chaining them, the empty optional becomes a kind of dead-end.
But Java 9 will introduce Optional<T> or​(Supplier<? extends Optional<? extends T>>), which may return a non-empty optional from the supplier when being applied to an empty optional.
Since an Optional represents a (possibly absent) value, rather than a processing pipeline, you can query the same Optional as many times you want, whether the query returns a new Optional or a final value.
It’s easy to verify. The following code
Optional<String> first=Optional.of("abc");
Optional<String> second=first.map(s -> {
System.out.println("Running map");
return s + "def";
});
System.out.println("starting queries");
System.out.println("first: "+(first.isPresent()? "has value": "is empty"));
System.out.println("second: "+(second.isPresent()? "has value": "is empty"));
second.map("second's value: "::concat).ifPresent(System.out::println);
will print
Running map
starting queries
first: has value
second: has value
second's value: abcdef
demonstrating that the mapping function is evaluated immediately, before any other query, and that we still can query the first optional after we created a second via map and query optionals multiple times.
In fact, it is strongly recommended to check via isPresent() first, before calling get().
There is no equivalent stream code, as re-using Stream instances this way is invalid. But we can show that intermediate operations are not performed before the terminal operation has been commenced:
Stream<String> stream=Stream.of("abc").map(s -> {
System.out.println("Running map");
return s + "def";
});
System.out.println("starting query");
Optional<String> result = stream.findAny();
System.out.println("result "+(result.isPresent()? "has value": "is empty"));
result.map("result value: "::concat).ifPresent(System.out::println);
will print
starting query
Running map
result has value
result value: abcdef
showing that the mapping function is not evaluated before the terminal operation findAny() starts. Since we can’t query a stream multiple times, findAny() even uses Optional as return value, which allows us to do that with the final result.
There are other semantic differences between the operations of the same name, e.g. Optional.map will return an empty Optional if the mapping function evaluates to null. For a stream, it makes no difference whether the function passed to map returns null or a non-null value (that’s why we can count the elements without knowing whether it does).
String r = Optional.of("abc")
.map(s -> {
System.out.println("Running map");
return s + "def";
})
.filter(s -> {
System.out.println("First Filter");
return s.equals("abcdef");
})
.map(s -> {
System.out.println("mapping");
return s + "jkl";
})
.orElse("done");
System.out.println(r);
Running this will produce:
Running map, First Filter, mapping abcdefjkl
On the other hand running this:
String r = Optional.of("mnt") //changed
.map(s -> {
System.out.println("Running map");
return s + "def";
})
.filter(s -> {
System.out.println("First Filter");
return s.equals("abcdef");
})
.map(s -> {
System.out.println("mapping");
return s + "jkl";
})
.orElse("done");
Running map, First Filter, done
I always thought that since the map is only execute based on the previous filter, this would be considered lazy. It turns out this is not true:
Optional.of("s").map(String::toUpperCase)
Stream.of("test").map(String::toUpperCase)
The map from Optional will get executed; while the one from Stream will not.
EDIT
go and up-vote the other answer here. This is edited because of the other one.

Streams : how map in Streams work

To understand the map function in Streams better I was trying something like this:
String inputString="1+3+5";
Stream.of(inputString.split("\\+")).map(
eachStringLiteral -> {
output += mapOfStringAndNumber.get(eachStringLiteral) + literal;
}
);
Where inputString is:
String inputString = "1+3+5";
however, the compiler complains and I don't know why:
The method map(Function) in the type
Stream is not applicable for the arguments ((
eachStringLiteral) -> {})
I also need some help in order to understand the syntax in
Function<? super String,? extends R>.
Update
This is whole code illustrating what I am trying to achieve:
HashMap<String,Double> mapOfStringAndNumber=new HashMap<String,Double>();
mapOfStringAndNumber.put("1",270.5);
mapOfStringAndNumber.put("2",377.5);
mapOfStringAndNumber.put("3",377.5);
String inputString="1+3+5";
String literal="+";
String output;
java.util.stream.Stream.of(inputString.split("+")).map(eachStringLiteral->
output+=mapOfStringAndNumber.get(eachStringLiteral)+literal
Assuming that you provide a Double value to each input String in your map mapOfStringAndNumber, you could use Pattern#splitAsStream(CharSequence input) to split your input String directly as a Stream and use Collectors.joining(CharSequence delimiter) to build your output with + as delimiter, so your final code could be:
Map<String,Double> mapOfStringAndNumber = new HashMap<>();
mapOfStringAndNumber.put("1",270.5);
mapOfStringAndNumber.put("3",377.5);
mapOfStringAndNumber.put("5",377.5);
String inputString = "1+3+5";
String output = Pattern.compile("\\+")
.splitAsStream(inputString)
.map(mapOfStringAndNumber::get)
.map(d -> Double.toString(d))
.collect(Collectors.joining("+"));
System.out.println(output);
Output:
270.5+377.5+377.5
I have a couple of suggestions for your code:
If you use a {}-block and you want to create a Function (not a Runnable, Consumer or similar) you have to return something.
Unless you have a very special case, use Integer.parseInt(String) instead of a Map of Strings to Integers
You must not modify output from within a Stream. That could lead to concurrancy issues.
Do you really need literal?
My example code for your usecase would look like this:
String inputString="1+3+5";
return
// split input and create stream
Stream.of(inputString.split("\\+"))
// convert each item to Integer
.mapToInt(Integer::parseInt)
// sum up everything
.sum();
map, by definition, takes an input stream and applies a Function to each element of this stream, creating a new stream out of those modified elements.
So the count of items in input stream has to be the same as the count of items in output stream.
The error you are mentioning occurs, because your mapping function does not return anything: output+=mapOfStringAndNumber.get(eachStringLiteral)+literal only modifies the output variable and returns nothing.
From what I'm seeing here, you want to perform map and reduce operations:
map operation changes each element of input stream into its value from mapOfStringAndNumber and adding literal to it
reduce operation, in your case, is summing up all elements of your stream
To achieve this using Java 8 features, you need to first map the elements of your stream and then sum them up. You can do it like this:
String sum = Stream.of(inputString.split("\\+"))
.map(stringLiteral->
mapOfStringAndNumber.get(stringLiteral).toString())
//join all the strings in the stream with a provided delimiter
.collect(Collectors.joining(literal));
output+=mapOfStringAndNumber.get(eachStringLiteral)+literal; is an expression. Lambda expressions that have a body consisting of a single expression should not have them placed within curly braces:
Stream.of(inputString.split("+")).map(eachStringLiteral ->
output+=mapOfStringAndNumber.get(eachStringLiteral)+literal;
);
See the section of the Java Language Specification concerning lambda bodies:
A lambda body is either a single expression or a block (§14.2)...
LambdaBody:
Expression
Block
Alternatively you can just add a return clause before the output variable to make it a statement.

Converting Array and copying it into a List

To be clear I don't have any problems and don't really need help but I wanted to ask anyway:
Let's say we have a String array
String[] sarr = new String[]{"POTATO", "TOMATO"};
and we have an enum
public enum Food{POTATO, TOMATO, PIZZA}
If I wanted to check if all Strings in sarr are present in Food, I'd do the following:
ArrayList<String> foodstrings = new ArrayList<>();
Arrays.asList(Food.values()).forEach((in) -> foodstrings.add(in.toString()));
if (!foodstrings.containsAll(Arrays.asList(sarr))) doStuff();
Is there a way to do this in less lines of code? Or simply a more elegant way?
You want to determine if all element in your array are contained in the list of food names.
A possible solution is to convert the food names to a Set (to have a O(1) contains); then, we need to determine if all elements in the array are contained in this set:
public static void main(String[] args) {
String[] sarr = new String[]{"POTATO", "TOMATO"};
Set<String> set = Arrays.stream(Food.values()).map(Enum::name).collect(Collectors.toSet());
boolean result = Arrays.stream(sarr).allMatch(set::contains);
}
In your current solution, you are mutating an external variable with forEach, which is a bad practice.
I believe a better version of the first two lines would be:
Set<String> foodstrings = Arrays.stream(Food.values()).map(Enum::name).collect(Collectors.toSet());
Using Set instead of List will improve performance of containsAll, and the code is entirely streamed, instead of using forEach and an external collector.
The if is still good, although you could just combine it all into a single statement (formatted for readability):
if (! Arrays.stream(Food.values())
.map(Enum::name)
.collect(Collectors.toSet())
.containsAll(Arrays.asList(sarr))) {
doStuff();
}
if (Stream.of(sarr).allMatch(s -> Stream.of(Food.values()).anyMatch(t -> s.equals(t.name()))))
{
// all match
}
Create a stream out of sarr (which could be any Collection of objects amenable to the Stream API introduced in Java 1.8)
We ask for the value allMatch, which only returns true if a Predicate (i.e. a function that returns true/false).
For the Predicate expected by allMatch, we provide a lambda that iterates over a second set of objects via a stream, and calls anyMatch: a simple Predicate that will return true if any member object satisfies a provided condition (once again, a boolean function).
We provide yet another lambda to anyMatch which compares members of the 2 collections via their equals implementations.
This solution is semantically equivalent to the invariant
A \subset B
which in our case is
sarr \subset Food.values()
and the following Java < 1.8 code shown below with short-circuiting to mimic the specification (minus the streams overhead):
// assume success, since if both sets are empty the invariant holds
boolean subset = true;
for (String a : sarr)
{
if (null == a) continue;
boolean contained = false;
for (Food b : Food.values())
if (b.name().equals(a)) { contained = true; break; }
if (!contained) { subset = false; break; }
}
if (subset)
{
// all match
}
You could, of course, substitute different collection types and conditions, as well as use parallelStream() to make better use of the hardware available.

Java stream of optionals to optional stream

I need to convert Stream<Optional<Integer>> to Optional<Stream<Integer>>.
The output Optional<Stream<Integer>> should be an empty value when at least one value ofStream<Optional<Integer>> is empty.
Do you know any functional way to solve the problem? I tried to use collect method, but without success.
Well, the tricky thing here is that if you're just given a Stream, you can only use it once.
To be stateless and avoid redundant copying, one way is to just catch NoSuchElementException:
static <T> Optional<Stream<T>> invert(Stream<Optional<T>> stream) {
try {
return Optional.of(
stream.map(Optional::get)
.collect(Collectors.toList())
.stream());
} catch (NoSuchElementException e) {
return Optional.empty();
}
}
A simple inversion would be:
static <T> Optional<Stream<T>> invert(Stream<Optional<T>> stream) {
return Optional.of(stream.map(Optional::get));
}
But to find out if it contains an empty element, you need to actually traverse it which also consumes it.
If you're given the source of the stream, you can traverse it without collecting it:
static <T> Optional<Stream<T>> invert(
Supplier<Stream<Optional<T>>> supplier) {
// taking advantage of short-circuiting here
// instead of allMatch(Optional::isPresent)
return supplier.get().anyMatch(o -> !o.isPresent()) ?
Optional.empty() : Optional.of(supplier.get().map(Optional::get));
}
List<Optional<Integer>> myInts =
Arrays.asList(Optional.of(1), Optional.of(2), Optional.of(3));
Optional<Stream<Integer>> inverted = invert(myInts::stream);
That's probably a more interesting approach. (But it's prone to a race condition because the stream() is taken twice. If some other thread adds an empty element in between and gets away with it, we have a problem.)
Though this has already been answered yet to add to the list, with Java-9 introducing Optional.stream, this should be achievable as:
// initialized stream of optional
Stream<Optional<Integer>> so = Stream.empty();
// mapped stream of T
Stream<Integer> s = so.flatMap(Optional::stream);
// constructing optional from the stream
Optional<Stream<Integer>> os = Optional.of(s);
Similar to Radiodef's answer, though this one avoids the exception handling and the intermediate list.
private static <T> Optional<Stream<T>> invertOptional(Stream<Optional<T>> input) {
return input.map(integer -> integer.map(Stream::of))
.collect(Collectors.reducing((l, r) -> l.flatMap(lv -> r.map(rv -> Stream.concat(lv, rv)))))
.orElse(Optional.empty());
}
The way this works is it maps to a Stream of Optional Streams of T. The Optional.map is used in this case, so each one of the Optional<Stream<T>> items in the resultant stream is a either a Stream of 1, or an empty Optional.
Then it collects these streams by reducing them together. the l.flatMap will return an empty Optional if l is empty or the r.map returns an empty. if r.map isn't empty, it calls the Stream.concat, which combines the left and right stream values.
The whole collect reduction produces an Optional<Optional<Stream<T>>>, so we narrow that down with the .orElse(Optional.empty)
Note: Code is tested, and appears to work. The unspecified "edge case" of an empty input Stream is treated as an an empty Optional, but can be easily changed.
final Stream<Optional<Integer>> streamOfInts = Stream.of(Optional.of(1), Optional.of(2), Optional.of(3), Optional.of(4), Optional.of(5));
// false - list of Optional.empty(); true -> list of Optional.of(Integer)
final Map<Boolean, List<Optional<Integer>>> collect = streamOfInts.collect(Collectors.partitioningBy(Optional::isPresent));
final Function<List<Optional<Integer>>, Stream<Integer>> mapToStream = List->List.stream().filter(o->o.isPresent()).map(o->o.get());
Optional<Stream<Integer>> result = Optional
.of(Optional.of(collect.get(false)).filter(list->list.size()>0).orElse(collect.get(true)))
.filter(list->list.size()>0)
.filter(list->list.get(0).isPresent())
.map(mapToStream)
.map(Optional::of)
.orElse(Optional.empty());

Turn different sets with different datatypes into a string separated by a specified delimiter in one function

For Example
a set{1,2,3,4,5} with a delimiter #
I want to have a return string of 1#2#3#4#5
The tricky part is, I have many different sets,
Set<Integer>, Set<String>, Set<Long> and so on.
And I know I can do it in separate functions using a loop. But I just don't want to be tedious and want to do it in one function using Java Generics or whatever that can help. I have done some brain storm but in vain. :(
Any idea is appreciated!
You can use the join method of StringUtils class of the Commons Lang library
Example:
Set<Integer> theSet = new HashSet<Integer>();
theSet.add(1);
theSet.add(2);
theSet.add(3);
theSet.add(4);
theSet.add(5);
System.out.println(StringUtils.join(theSet,'#'));
Output: 1#2#3#4#5
Edit: the answer from mabbas is a better idea than this.
It's not particularly great, but will work assuming that your Set contains objects whose toString representations you want to concatenate
public String concatenateSet(Set<?> set) {
return set.stream()
.map(Object::toString)
.reduce((previous, current) -> previous + "#" + current)
.orElse("");
}
Step by step:
Calling stream() gets you a stream of items in the set
Calling map(Object::toString) calls toString on each item of the set
Calling reduce((previous, current) -> previous + "#" + current) concatenates the toString representations of each object with # as a delimiter between each item
Calling orElse("") will ensure that an empty sting is returned if, for example, the set is empty
You're over thinking it. You can just take a Set as a parameter and it will cover every type of set. Just make sure your objects implement toString().
public static String setToString(Set set)
{
Iterator it = set.iterator();
StringBuffer out = new StringBuffer(it.next().toString());
while (it.hasNext())
{
out.append("#" + it.next());
}
return out.toString();
}

Categories

Resources