map pipeline operator associated with function interface - java

I can't understand why the String::toUpperCase() expression works fine inside the Stream map pipeline. When I look at this example here:
Stream.of("test1", "test2", "test3", "test4")
.filter(s -> s.contains("r"))
.map(s -> s + "map")
.map(String::toUpperCase)
.forEach(System.out::println);
When I look to the definition of the map operator used in the example below map(Function<? super String, ? extends String> mapper) I saw a function design pattern is been used.
In this example .map(s -> s + "map") is fine, as I understand we are looking for Function more precisely the R apply(T t);, it is totally what the lambda expression said s -> s + "map" here we have a function with a parameter s and it returns s + String "map" and it conforms to this spec. T and R, they are present.
On the other side the second one map(String::toUpperCase), I can't understand why the expression toUpperCase is considered as a Function interface, I should note the core of this function is like this
public String toUpperCase() {
return toUpperCase(Locale.getDefault());
}
and we are looking for R apply(T t); there is no T parameter in this method toUpperCase? Why does this one work?

What's much easier to understand in terms of the apply method of the Function interface is the anonymous class representation of the method reference String::toUpperCase. It goes like this -
new Function<String, String>() {
#Override
public String apply(String str) { // read as given a String return a String (uppercased)
return str.toUpperCase();
}
}
The string arguments(str) provided to the above apply method are the ones from the Stream after the previous map operation.

It's called a method reference and it's syntactic sugar for a lambda expression. In other words the following:
String::toUpperCase
is equivalent to:
s -> s.toUpperCase()
It's a method that takes a String s and returns a String with all letter from s uppercase, it's a Function<String, String>.

Related

Chaining a Function with a BiFunction in Java

I have a Function and BiFunction and I would like to chain them
Function<Integer, String> function = n -> "" + n;
BiFunction<String, Boolean, List<Character>> biFunction =
(str, isOK) -> Collections.EMPTY_LIST;
Is there a way to chain these two Functions such as the returned value from Function is used as an input to BiFunction?
Pseudocode:
public List<Character> myMethod(int n, boolean isOK) {
return function.andThen(biFunction).apply([output_of_function], isOK)
}
I couldn't find a way to provide the integer n to Function nor to supply BiFunction with the output of the first Function.
Is it doable?
Default methods andThen() and compose() declared in the interface Function expect another Function as an argument. Hence, it's not possible to fuse Function and BiFunction using these methods (BiFunction and Function doesn't extend each other).
On the other hand method BiFunction.andThen() expects a Function as argument. But unfortunately it would be applied after BiFunction (i.e. on the result produced by the BiFunction), but you need the opposite, so this option doesn't fit into your use-case.
As a possible workaround, you can combine a Function and a BiFunction into an aggregate BiFunction expecting the input of the Function function and a boolean value and producing the result generated by the by BiFunction like this:
public static <T, R, RR> BiFunction<T, Boolean, RR> getCombinedFunction(
Function<T, R> fun, BiFunction<R, Boolean, RR> biFun
) {
return (t, isOk) -> biFun.apply(fun.apply(t), isOk);
}
It can be used in the following way:
Function<Integer, String> function = // initializing function
BiFunction<String, Boolean, List<Character>> biFunction = // initializing biFunction
List<Character> chars = getCombinedFunction(function, biFunction).apply(12345, true);
Sidenote:
The preferred way of converting an int into a String is to use static method String.valueOf(). And the function from your example could be expressed as the following method reference:
Function<Integer, String> function = String::valueOf;
You can define a generic method that compose Function and BiFunction like this.
public static <A, B, C, D> BiFunction<A, C, D> compose(Function<A, B> f, BiFunction<B, C, D> bf) {
return (a, c) -> bf.apply(f.apply(a), c);
}
And you can use like this.
Function<Integer, String> function = n -> ""+n;
BiFunction<String, Boolean, List<Character>> biFunction = (str, isOK) -> Collections.emptyList();
public List<Character> myMethod(int n, boolean isOK) {
return compose(function, biFunction).apply(n, isOK);
}
Node: You should use Collections.emptyList() instead of Collections.EMPTY_LIST.
The latter gives a warning.

i have question about java function signature

I found that function interface and getMethod seem to be replaceable, What makes it work?
public class App {
public static void main(String[] args) {
Map<String, String> collect = Stream.of(new App(), new App(), new App())
.collect(Collectors.toMap(App::getString, (app) -> "aaa"));
}
public String getString() {
return "str";
}
}
But when I use lambda to replace getMethod, it fails . Why this does not work
Map<String, String> collect = Stream.of(new App(), new App(), new App())
.collect(Collectors.toMap(() -> "str", (app) -> "aaa"));
Collectors.toMap requires a Function<? super T, ? extends K> as its first parameter, where T is the type of elements in the stream, and K is the key type of the map you want.
In this case, you have a stream of Apps and you want a Map<String, String>, so T is App and K is String. In other words, you need a function that accepts an App, and returns a String.
App::getString is such a Function<? super T, ? extends K>. You might be wondering why it accepts a App when getString accepts no parameters. Notice how getString is an instance method, and you are referring to it without an instance! A method reference of the form ClassName::instanceMethodName implicitly accepts an extra parameter of type ClassName, because you need an instance of that class to call it!
On the other hand, your lambda is not such a function. It accepts no parameters, as indicated by the empty brackets at the start (()). Your lambda expression would be represented by the Supplier<String> functional interface, not the Function<App, String> that you need.
To use a lambda expression here, simply do what you did to the second parameter of toMap, and add a lambda parameter:
.collect(Collectors.toMap((app) -> "str", (app) -> "aaa"));
// ^^^
Note that this is required even if you don't use app in the lambda expression.

How to reference the result of reduce() operation in Java 8?

I was trying to write a mkString function in Java8, a la Scala's useful mkString and ran into 2 issues that I could use some help on:
I am unable to make the first argument of mkString a generic Collection reference like Collection<Object> c and have invokers call with ANY type of collection.
Unable to reference the returned result of reduce() in-line to access the result's length to remove the extra leading separator.
Here's the code :
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
System.out.println(mkString(numbers, ","));
}
public static String mkString(Collection<Integer> c, String sep) {
return c.stream()
.map(e -> String.valueOf(e))
.reduce("", (a, b) -> a + sep + b)
.substring(1, <<>>.length);
}
Note that if you're doing this not for self-education but to actually use it in some production code, you might want to consider the built-in Collectors.joining collector:
String result = numbers.stream()
.map(Object::toString)
// or
// .map(x -> x.toString()) // exactly the same
// or
// .map(String::valueOf) // handles nulls by turning them to the string "null"
.collect(Collectors.joining(","));
It has several overloads, similar to Scala's mkString. Still, this collector only accepts CharSequences, so you need to convert your values to strings explicitly as a separate map step.
Additionally, there is the String.join method, which also works for a collection of CharSequences. If you specifically have one of those (e.g. List<String>), it might be more convenient to use this method rather than converting the collection to a stream first:
List<String> strings = ...;
String result = String.join(",", strings);
// vs
String result = strings.stream().collect(Collectors.joining(","))
If I remember my java correctly, you can declare the argument type as Collection<?> to be able to pass a collection of any objects.
As to biting the separator off, I think, just .substring(1) will do what you want.
You can do it like :
public static <T> String mkString(Collection<T> c, String sep) { // generic impl
return c.stream()
.map(String::valueOf)
.reduce("", (a, b) -> a + sep + b)
.substring(1); // substring implementation to strip leading character
}
Any type of collection in java means Collection<?>, which semantically is the same as Collection<T> (in your case), it is said that if the type parameter is used only once) it can safely be replaced with a wildcard. But, since you want to be able to concat any collection, you should also ask for the callers to supply a Function that would transform from that type to a String representation, thus your method would become:
public static <T> String mkString(Collection<T> c,
Function<T, ? extends CharSequence> mapper,
String sep) {
return c.stream()
.map(mapper)
.collect(Collectors.joining(sep));
}
You can utilize String.join with a generic type:
public static <T> String mkString(Collection<T> c, String sep) {
return String.join(sep, c.stream()
.map(e -> String.valueOf(e))
.collect(Collectors.toList()));
}
Here it is in action with both Strings and other objects.

Enums Returning Arrays Stream

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

What do we need the BiFunction interface for?

The definition of the BiFunction interface contains a method apply(T t, U u), which accepts two arguments. However, I don't understand the use or purpose of this interface and method. What do we need this interface for?
The problem with this question is that it's not clear whether you see the purpose of a Function, which has a method apply(T t).
The value of all the functional types is that you can pass code around like data. One common use of this is the callback, and until Java 8, we used to have to do this with anonymous class declarations:
ui.onClick(new ClickHandler() {
public void handleAction(Action action) {
// do something in response to a click, using `action`.
}
}
Now with lambdas we can do that much more tersely:
ui.onClick( action -> { /* do something with action */ });
We can also assign them to variables:
Consumer clickHandler = action -> { /* do something with action */ };
ui.onClick(clickHandler);
... and do the usual things we do with objects, like put them in collections:
Map<String,Consumer> handlers = new HashMap<>();
handlers.put("click", handleAction);
A BiFunction is just this with two input parameters. Let's use what we've seen so far to do something useful with BiFunctions:
Map<String,BiFunction<Integer,Integer,Integer>> operators = new HashMap<>();
operators.put("+", (a,b) -> a + b);
operators.put("-", (a,b) -> a - b);
operators.put("*", (a,b) -> a * b);
...
// get a, b, op from ui
ui.output(operators.get(operator).apply(a,b));
One of usages of BiFunction is in the Map.merge method.
Here is an example usage of the Map.merge method, which uses a BiFunction as a parameter. What merge does is basically replaces the value of the given key with the given value if the value is null or the key does not have a value. Otherwise, replace the value of the given key after applying the BiFunction.
HashMap<String, String> map = new HashMap<>();
map.put("1", null);
map.put("2", "Hello");
map.merge("1", "Hi", String::concat);
map.merge("2", "Hi", String::concat);
System.out.println(map.get("1")); // Hi
System.out.println(map.get("2")); // HelloHi
If a BiFunction were not used, you would have to write a lot more code, even spanning several lines.
Here is a link that shows all the usages of BiFunction in the JDK: https://docs.oracle.com/javase/8/docs/api/java/util/function/class-use/BiFunction.html
Go check it out!
An extra example of BiFunction is reduce():
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(Arrays.asList(5,5,10));
Integer reduce = list.stream().reduce(0, (v1,v2) -> v1+v2);
System.out.println(reduce); // result is: 20
}

Categories