This question already has answers here:
Why can a method be called on another method?
(7 answers)
Java Pass Method as Parameter
(17 answers)
Closed 1 year ago.
Can some please help me with syntax of following java code?
list.stream()
.filter(name -> name.startsWith("f"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);`
is method filter(name -> name.startsWith("f") is being called on the result of list.stream() and then .map(String::toUpperCase) is being called on its result ? What version of java is this? Where I can read about this more?
Your code snippet is a Java stream, and consists of a pipeline of several processing steps. Here is an explanation:
list.stream()
.filter(name -> name.startsWith("f")) // retain only names starting with 'f'
.map(String::toUpperCase) // map the name to uppercase
.sorted() // sort the stream ascending by name
.forEach(System.out::println); // print out each name
In plain English, this stream says to take an input list of names, remove any name not starting with 'f', uppercase all names, sort them ascending by name, and finally print out each name to the console.
In answer to your question, the lambda you're seeing (name -> name.startsWith("f")) is a feature added in Java 8. This version of Java also adds method references, which are essentially a quicker way to write many lambdas which merely call an existing method. The line .map(String::toUpperCase) could have been written as .map(name -> name.toUpperCase()). Similarly .forEach(System.out::println) could have been .forEach(name -> System.out.println(name)).
Documentation on Oracle.com for further reading:
Lambda Expressions
Method references
list.stream()
.filter(name -> name.startsWith("f"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
The whole idea of Java streams is to enable functional-style operations on streams of elements, using Lambda experession and Method reference.
Method references are a special type of lambda expressions
In functional-style operations the execution is performed from left to right that means
stream() is called on result of list
filter() is called on result of list.stream()
map() is called on result of list.stream().filter(name -> name.startsWith("f"))
sorted() is called on result of list.stream().filter(name -> name.startsWith("f")).map(String::toUpperCase)
forEach is called on the result of list.stream().filter(name -> name.startsWith("f")).map(String::toUpperCase).sorted()
To explore your knowledge you can read from Oracle Java (version 8 onward) Documents
Specially for the topics Lambda expression and method reference :-
1. Lamda Expression
2. Method Reference
The stream is one of the functional element of Java which uses Lambda Expressions (unnamed function).
You have a list and convert it to a stream.
For each element you will use the filter function which removes the elements that do not safisfy the criteria. This selection criteria is given as a Lambda Function. Its argument is the element and it should return with a boolean value. So the name should start with 'f'.
Then you use the 'map' function to convert the elements of the list into another type using a Lambda Function again. In this case your list contains string and your LambadaFunction is the String::toUpperCase. This a short hand format of (name) -> {return String.toUpperCase(name);}. In other words, the toUpperCase function will be called with each element of the list and the 'map' will produce a new stream which contains the results.
Next you sort the stream.
Finally, Each element of the stream is given as an argument of the 'println' method.
The result should contains a sorted list of names starting with F.
Related
This question already has answers here:
Dealing with a null attribute using java 8 streams and sorting using lambda expressions
(6 answers)
Closed 1 year ago.
What is the best way to check if d1.getDept() and d2.getDept() are null in the following lambda before doing the compare? Should the null check be done before the Collections.sort() or can it be done within the lambda statement?
if (sortBy.contains("Dept")) {
Collections.sort(inventoryVariances, (d1, d2) -> d1.getDept().compareTo(d2.getDept()));
}
I need to ensure the Dept data is not null for each object prior to trying to sort by Dept.
you can try filtring empty values :
inventoryVariances
.stream()
.filter(c -> c.getDept() !=null)
.sort((d1, d2) -> d1.getDept().compareTo(d2.getDept())
Duplicate of Dealing with a null attribute using java 8 streams and sorting using lambda expressions
Straight answer is "no" the collections does not support null values. The use of java 8 comparator nullfirst and nullLast also can be a tricky case for collections as the documentation suggests:
"If the specified comparator is null, then the returned comparator considers all non-null values to be equal."
The simplest way though would be to filter out nulls first:
Assuming that your collection on which you are sorting is an arraylist and you are converting it to another sorted list you can do something like:
inventoryVariances.stream().filter(d -> d.getDept() !=null).sorted(Variance::getDept).collect(Collectors.toCollection(LinkedList::new ));
I am learning Java 8 and came across a situation. Where in I have to iterate over a list of strings and then convert them to upperCase. The possible solutions would be to stream the list. Among many suggestions from Intellij the below two seems to be useful.
list.stream()
.map(String::toUpperCase)
or
list.stream().
forEach(p -> p.toUpperCase())
I am confused on which one to use and the use cases for all the Suggestions. Can I get help regarding which method to use and how to understand using all those suggestions?
Stream.map() will never run unless you end the pipeline in a terminal operation, like forEach(). But calling toUpperCase() in a forEach() won't do anything either, because strings are immutable. String.toUpperCase() doesn't change the string; it returns a new one.
If you just want to update the list in-place, you can use
list.replaceAll(String::toUpperCase);
which actually replaces each element with the result of the passed function.
If you want the results in a new list, use the map() snippet with a collector:
List<String> list2 = list.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
forEach is an terminal operation that makes a difference through side effects. map is a non-terminal operation that makes a direct mapping from one element to another. For example, here is a canonical usage of forEach:
stream.forEach(System.out::println);
This will invoke, on each element of the stream, the equivalent of System.out.println(element);. However, the stream will be closed after this, and no operations may be executed on stream afterwards. map, on the other hand, may be used like this:
streamOfObjects.map(Object::toString).collect(Collectors.toList());
In this case, each Object within streamOfObjects is mapped to a String, created by invocation of toString. Then, the stream of Strings produced by map is collected into a List using a Collector.
In any case, I'd suggest using replaceAll for this use case, as suggested by #shmosel.
As for how to understand suggestions provided by autocomplete, I would strongly suggest reading JavaDocs on the related classes.
This question already has answers here:
UnsupportedOperationException when trying to remove from the list returned by Array.asList
(6 answers)
Closed 3 years ago.
I am new To Java 8 and in below example I have created a Map having keyvalue as String and value as ArrayList of integer.
Map<String,List<Integer>> mapLstInteger=new HashMap<String,List<Integer>>() {
{
put("A",Arrays.asList(1,2,3));
put("B",Arrays.asList(4,5,6));
put("C",Arrays.asList(7,8,9));
}
};
I wrote below code to perform the sum for arrayList elements against every key and was trying to store the sum value in seperate ArrayList.
List<Integer> sumLst=mapLstInteger.entrySet().stream().map(e->e.getValue())
.reduce((inputLst, outputLst)->{
int sum=0;
for(int count=0;count<inputLst.size();count++)
{
sum=sum+inputLst.get(count);
}
outputLst.add(sum);
return outputLst;
}).get();
When I am trying to execute the below code I am getting below exception.
Exception in thread "main" java.lang.UnsupportedOperationException at
java.util.AbstractList.add(AbstractList.java:148) at
java.util.AbstractList.add(AbstractList.java:108) at
com.calculation.sum.client.Client.lambda$1(Client.java:43) at
java.util.stream.ReduceOps$2ReducingSink.accept(ReduceOps.java:123)
at
java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at
java.util.HashMap$EntrySpliterator.forEachRemaining(HashMap.java:1696)
at
java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at
java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at
java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at
java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at
java.util.stream.ReferencePipeline.reduce(ReferencePipeline.java:479)
at com.calculation.sum.client.Client.main(Client.java:37)
Can anybody please let me know what I did wrong in above code>
First you are using Arrays::asList which is documented as Returns a fixed-size list backed by the specified array, I think fixed size should tell you what you are doing wrong.
Than you are using an anti-pattern of creating a HashMap in place - by creating an anonymous inner class that extends HashMap, via that Map<String,List<Integer>> mapLstInteger=new HashMap<String,List<Integer>>().....
Than, you are violating the specification of reduce, which is supposed to return a new Object all the time, but you always put into outputLst.
Than, you are creating a Map when all you care about is its values - create a List<List<Integer>> in such a case.
Even your sentence I wrote below code to perform the sum for arrayList elements against every key is not correct according to your code. I would just make up my mind on the actual thing I want to achieve and then try to do it if I were you.
That happens because you're using the original AbstractList produced by Arrays.asList.That List<T> abstract implementation does not allow adding or removing elements.
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
But anyway, back at your problem. You can obtain what you want also via a custom Collector, where you can supply your custom List<T> implementation, be it an ArrayList, LinkedList, or whatever you feel like is better.
mapLstInteger.values()
.stream()
.collect(Collector.of(
() -> new ArrayList<>(), // Supplier
(output, toSumList) -> { // Accumulator
output.add(toSumList.stream()
.mapToInt(Integer::intValue)
.sum());
},
// The Combiner implementation will be called
// in case of a "parallel" Stream.
// No need to worry about it here.
// But in case, we would need to merge the partial results
(output, partial) -> {
output.addAll(partial);
return output;
}
));
A more succinct version is
mapLstInteger.values()
.stream()
.map(l -> l.stream().mapToInt(Integer::intValue).sum())
.collect(Collectors.toCollection(ArrayList::new));
That will correctly output [6, 15, 24]
You should be doing the following:
mapLstInteger.values().stream()
.flatMapToInt(list -> list.stream()
.filter(Objects::nonNull)
.mapToInt(Integer::intValue)).sum();
Added the filter to ensure you get no null pointers in the case of null Integers. As a general rule if you are forced to use a conventional loop inside of a stream you are probably doing something wrong. By mutating the list of ints to an int value we can easily sum as you see above.
Initially misunderstood the question thinking you wanted the overall sum alas here is an updated solution for the actual problem:
mapLstInteger.values().stream()
.map(list -> list.stream()
.filter(Objects::nonNull)
.mapToInt(Integer::intValue).sum())
.collect(Collectors.toList());
I have a Set<String> of "hostname:port" pairs and from that I'd like to create a Set<InetSocketAddress>. I tried it like:
Set<InetSocketAddress> ISAAddresses = StrAddresses
.stream().map(addr -> new InetSocketAddress(
addr.split(":")[0],
Integer.parseInt(addr.split(":")[1])));
But this produces the following error in IntelliJ:
Incompatible types. Required Set<InetSocketAddress> but 'map' was
inferred to Stream<R>: no instance(s) of type variable(s) R exist so
that Stream<R> conforms to Set<InetSocketAddress>
Something must be wrong with how I'm using the map and the lambda.
The Stream#map function does not return a Map. It transforms (maps) the current elements of your stream to other elements. So it generates from a Stream<X> a Stream<Y> using the given transformation function which takes X and outputs Y.
StrAddresses.stream() // String
.map(addr -> new InetSocketAddress(
addr.split(":")[0],
Integer.parseInt(addr.split(":")[1]))); // InetSocketAddress
You start with a Stream<String> and end up with a Stream<InetSocketAddress>.
To quote from its documentation:
Returns a stream consisting of the results of applying the given function to the elements of this stream.
If you want to transform that stream into a Set you need to use the Stream#collect method like so:
StrAddresses.stream()
.map(addr -> new InetSocketAddress(
addr.split(":")[0],
Integer.parseInt(addr.split(":")[1])))
.collect(Collectors.toSet());
The utility method Collectors.toSet() returns a collector for a well optimized Set. If you for example explicitly want a HashSet you can use this instead:
.collect(Collectors.toCollection(HashSet::new));
From its documentation:
Performs a mutable reduction operation on the elements of this stream. A mutable reduction is one in which the reduced value is a mutable result container, such as an ArrayList [...]
As a small note, you currently split the same element twice each time:
addr.split(":")[0], // First
Integer.parseInt(addr.split(":")[1]))) // Second
You could save that additional split procedure by memorizing the value before. In this case this can be done elegantly by using a second Stream#map call. First we transform from Stream<String> to Stream<String[]> and then to Stream<InetSocketAddress>:
StrAddresses.stream() // String
.map(addr -> addr.split(":")) // String[]
.map(addrData -> new InetSocketAddress(
addrData[0], Integer.parseInt(addrData[1]))) // InetSocketAddress
.collect(Collectors.toSet());
Note that Stream#map is a lazy operation. This means that Java will not transform the whole Stream from A to B once you call the method. It will wait until a non-lazy (finalizing) operation like Stream#collect comes, then traverse the Stream and apply each lazy operation element-wise. So you can add as many Stream#map calls as you like without producing extra loops over the whole Stream.
You need to collect the Stream of InetSocketAddress addresses returned after mapping to Set. This can be done as -
Set<InetSocketAddress> ISAAddresses = StrAddresses.stream()
.map(addr -> new InetSocketAddress(addr.split(":")[0], Integer.parseInt(addr.split(":")[1])))
.collect(Collectors.toSet());
This question is related to the Java 8 map and flatMap that is present both in Streams and Optionals. It is worth to note that C# has a similar construct named SelectMany.
I have learned about the two methods, in particular that in Streams you can use flatMap to get a Collection<T> from a Collection<Collection<T>> which is what I want.
In my example I have a nested class structure (from a DTD I have no control over) in which I want to compute a sum of values. I will not redact the class names for laziness.
class DatiTelematico {
private Adempimento adempimento;
}
class Adempimento {
private List<DatiNegozio> datiNegozio;
}
class DatiNegozio {
private List<Negozio> negozio;
}
class Negozio {
private List<Tassazione> tassazione;
}
class Tassazione {
private BigDecimal importo;
}
Given an Optional instance of a DatiTelematico class I would like to sum (importo) from telematico join adempimento join datiNegozio join negozio join tassazione.
The best I could do was to use nested lambdas and plain map method
optionalTelematico.map(DatiTelematico::getAdempimento)
.map(Adempimento::getDatiNegozio)
.map(l -> l.stream().map(DatiNegozio::getNegozio)
.map(n -> n.stream()
.map(Negozio::getTassazione)
.map(t -> t.stream()
.map(Tassazione::getImporto)
.reduce(BigDecimal.ZERO,
BigDecimal::add))
.reduce(BigDecimal.ZERO, BigDecimal::add))
.reduce(BigDecimal.ZERO, BigDecimal::add))
.orElse(BigDecimal.ZERO));
I have tried to start writing something like
optionalTelematico.map(DatiTelematico::getAdempimento)
.map(Adempimento::getDatiNegozio)
.map(l->l.stream().flatMap(n->n.getNegozio().stream().flatMap(s->s.getTassazione().stream().flatMap(Tassazione::getImporto)))....TBD
But then I get a compiler error
Method binding must be directly contained in a class (OTJLD A.3.1).
How do I smartly switch from an Optional<T> (singleton) to a Collection<U> that is to be summed over?
I am asking this to increase my knowledge of Java lambdas.
Both Optional and Stream actually only represent a single piece of data - in case of optional, that piece of data might be either absent or present, in case of stream, there may be other pieces of data coming before or after, but in current moment we have this piece only.
Now,
map method is essentially a kind of type transformation for both Optional and Stream: mapping takes a function I -> R, applying which one can make transformation Optional<I> -> Optional<R> (or Stream<I> -> Stream<R>).
flatMap method is a kind of transformation that can:
Transform optional value into another optional (possibly empty). That means a function type I -> Optional<R>
Transform each item in stream into another stream (having 0..n number of elements in it). That means function type I -> Stream<R>. Note that for streams this operation can change the number of elements that are contained in stream (but it won't change the fact that there always effectively one stream element being processed at a time).
In your particular case, by making transformation to optional, you may obtain up to Optional<List<DatiNegozio>> directly:
Optional<List<DatiNegozio>> optDatiNegozio = optionalDatiTelematico
.map(DatiTelematico::getAdempimento) // Optional<Adempimento>
.map(Adempimento::getDatiNegozio);
Every List<DatiNegozio> you can easily convert to Optional<BigDecimal> summing and accessing elements via Stream:
static Optional<BigDecimal> sumImporto(List<DatiNegozio> datiNegozio) {
return datiNegozio.stream() // Stream<DatiNegozio>
.map(DatiNegozio::getNegozio) // Stream<List<Negozio>>
// unroll stream of collections into a stream of collection elements
.flatMap(List::stream) // Stream<Negozio>
.map(Negozio::getTassazione) // Stream<List<Tassazione>>
// again, unroll stream of collections into a stream of collection elements
.flatMap(List::stream)
.map(Tassazione::getImporto) // Stream<BigDecimal>
// last thing we need to do is just reduce
.reduce(BigDecimal::add);
}
As you can see, second snippet allows you to convert List<DatiNegozio> into an Optional<BigDecimal>. After this, you have two options (stylistic choice):
There is a variant of reduce that yields BigDecimal instead of Optional<BigDecimal>:
.reduce(BigDecimal.ZERO, BigDecimal::add); // it yields concrete type instead of optional because even in case when there is no elements in stream, we can at least return value from which we started - ZERO
You can use second code snippet to produce Function that is usable in flatMap-ing an optional:
optionalDatiTelematico
.map(DatiTelematico::getAdempimento)
.map(Adempimento::getDatiNegozio)
.flatMap(Example::sumImporto) // reference to method from 2nd code snippet
.orElse(BigDecimal.ZERO); // what to do if we had Optional.empty at any point
You can use Collection.stream() method to convert Collection to Stream and use it in flatMap. So combination of .map(d -> d.getList()).flatMap(Collection::stream) returns stream for all internal lists of Stream<D>.
In your case it can looks like:
Optional.of(datiTelematico)
.map(DatiTelematico::getAdempimento)
.map(Adempimento::getDatiNegozio)
.map(Collection::stream)
.orElseGet(Stream::empty)
.map(DatiNegozio::getNegozio)
.flatMap(Collection::stream)
.map(Negozio::getTassazione)
.flatMap(Collection::stream)
.map(Tassazione::getImporto)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Let say
Optional<DatiNegozio> abc = optionalTelematico.map(DatiTelematico::getAdempimento)
.map(Adempimento::getDatiNegozio)
Now when you say abc.map(xyz). xyz must be function which takes instance of DatiNegozio as one and only argument. In your case xyz is a lambda which takes one parameter l whose type should be DatiNegozio. You are now doing l.stream() which throws compile error beacsue stream() does not exist in l(instance ofDatiNegozio).