Extracting sub-strings with Stream in Java - java

I'm new to Streams and Lambdas in Java. I have a variable like this -
List<String> lines = Arrays.asList("ab,12,bd","df,23,df","ef,98,dg");
I wanted these actions to happen.
Split each element in the list.
Extract 2nd element in the resulting array. (that is numbers).
Apply some function on it. To make it simple, let's I wanted to multiply it by 2.
Then collect the result as list, that is list containing 24,46,196.
I tried doing that in streams, but I'm not able to get. Any pointers will be helpful. Thank you.
Edit: I tried this way and got result -
List<Integer> result1 = lines.stream()
.map(l -> l.split(",")[1])
.map(l->Integer.parseInt(l))
.collect(Collectors.toList());
And got results as
[12, 23, 98]
Is this is correct way of doing stream?

It should be straightforward.
lines.stream().map(s -> Integer.parseInt(s.split(",")[1]) * 2).collect(Collectors.toList());
Update based on the updated question
Is this is correct way of doing stream?
Yes. You can further combine the two map operations into one like I have shown above.

Does the following work for you?
lines.stream().map(s -> s.split(",")[1])
.map(Integer::parseInt)
.map(f)
.collect(Collectors.toList());
where f is your function from Integer to the type you want to use in your final list.

What about:
List<Integer> result = lines.stream()
.map(line -> line.replaceFirst(".*,(\\d+),.*", "$1"))
.map(num -> 2 * Integer.parseInt(num))
.collect(Collectors.toList());
You may want to convert to int[] in that case you can use:
int[] result = lines.stream()
.map(line -> line.replaceFirst(".*,(\\d+),.*", "$1"))
.mapToInt(num -> 2 * Integer.parseInt(num))
.toArray();

Related

How to use Java streams to create a list out of a map site

Starting with a map like:
Map<Integer, String> mapList = new HashMap<>();
mapList.put(2,"b");
mapList.put(4,"d");
mapList.put(3,"c");
mapList.put(5,"e");
mapList.put(1,"a");
mapList.put(6,"f");
I can sort the map using Streams like:
mapList.entrySet()
.stream()
.sorted(Map.Entry.<Integer, String>comparingByKey())
.forEach(System.out::println);
But I need to get list (and a String) of the correspondent sorted elements (that would be: a b c d e f) that do correspond with the keys: 1 2 3 4 5 6.
I cannot find the way to do it in that Stream command.
Thanks
As #MA says in his comment I need a mapping and that is not explained in this question: How to convert a Map to List in Java?
So thank you very much #MA
Sometimes people are too fast into closing questions!
You can use a mapping collector:
var sortedValues = mapList.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.mapping(Entry::getValue, Collectors.toList()))
You could also use some of the different collection classes instead of streams:
List<String> list = new ArrayList<>(new TreeMap<>(mapList).values());
The downside being that if you do all that in a single line it can get quite messy, quite fast. Additionally you're throwing away the intermediate TreeMap just for the sorting.
If you want to sort on the keys and collect only the values, you need to use a mapping function to only preserve the values after your sorting. Afterwards you can just collect or do a foreach loop.
mapList.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.map(Map.Entry::getValue)
.collect(Collectors.toList());

Find Max of Multiple Lists

I'm pretty new to java streams and am trying to determine how to find the max from each list, in a list of lists, and end with a single list that contains the max from each sublist.
I can accomplish this by using a for loop and stream like so:
// databaseRecordsLists is a List<List<DatabaseRecord>>
List<DatabaseRecord> mostRecentRecords = new ArrayList<>();
for (List<DatabaseRecord> databaseRecords : databaseRecordsLists) {
mostRecentRecords.add(databaseRecords.stream()
.max(Comparator.comparing(DatabaseRecord::getTimestamp))
.orElseThrow(NoSuchElementException::new));
}
I've looked into the flatMap api, but then I'll only end up with a single map of all DatabaseRecord objects, where I need a max from each individual list.
Any ideas on a cleaner way to accomplish this?
You don't need flatMap. Create a Stream<List<DatabaseRecord>>, and map each List<DatabaseRecord> of the Stream to the max element. Then collect all the max elements into the output List.
List<DatabaseRecord> mostRecentRecords =
databaseRecordsLists.stream()
.map(list -> list.stream()
.max(Comparator.comparing(DatabaseRecord::getTimestamp))
.orElseThrow(NoSuchElementException::new))
.collect(Collectors.toList());
Based on the comments, I suggested to rather ignore the empty collection, otherwise, no result would be returned and NoSuchElementException thrown even the empty collection might (?) be a valid state. If so, you can improve the current solution:
databaseRecordsLists.stream()
.filter(list -> !list.isEmpty()) // Only non-empty ones
.map(list -> list.stream()
.max(Comparator.comparing(DatabaseRecord::getTimestamp)) // Get these with max
.orElseThrow(NoSuchElementException::new)) // Never happens
.collect(Collectors.toList()); // To List
If you use a version higher than Java 8:
As of Java 10, orElseThrow(NoSuchElementException::new) can be subsituted with orElseThrow().
As of Java 11, you can use Predicate.not(..), therefore the filter part would look like: .filter(Predicate.not(List::isEmpty)).

java 8 make a stream of the multiples of two

I'm practicing streams in java 8 and im trying to make a Stream<Integer> containing the multiples of 2. There are several tasks in one main class so I won't link the whole block but what i got so far is this:
Integer twoToTheZeroth = 1;
UnaryOperator<Integer> doubler = (Integer x) -> 2 * x;
Stream<Integer> result = ?;
My question here probably isn't related strongly to the streams, more like the syntax, that how should I use the doubler to get the result?
Thanks in advance!
You can use Stream.iterate.
Stream<Integer> result = Stream.iterate(twoToTheZeroth, doubler);
or using the lambda directly
Stream.iterate(1, x -> 2*x);
The first argument is the "seed" (ie first element of the stream), the operator gets applied consecutively with every element access.
EDIT:
As Vinay points out, this will result in the stream being filled with 0s eventually (this is due to int overflow). To prevent that, maybe use BigInteger:
Stream.iterate(BigInteger.ONE,
x -> x.multiply(BigInteger.valueOf(2)))
.forEach(System.out::println);
Arrays.asList(1,2,3,4,5).stream().map(x -> x * x).forEach(x -> System.out.println(x));
so you can use the doubler in the map caller

turn string separated by spaces to arraylist of integers in one line

Say I have the String "5 12 4". I'd like to turn that into an ArrayList of Integers containing 5,12 and 4 in a single functional line.
I feel there should be a way to do this by combining split(" "), turning it into a stream, using mapToInt(s->Integers.parseInt(s)) and collect(Collectors.toList()). Something like:
ArrayList<Integer> nextLine = Arrays.stream(inputLine.split(" "))
.mapToInt(s->Integer.parseInt(s))
.collect(Collectors.toList());
But that does not work, because mapToInt give me ints instead of Integers.
I know how to do it using a loop. I would like a way to do it in a single stream operation if it exists.
You can use Integer#valueOf. Note you should use Stream#map and not Steam#mapToInt though:
List<Integer> nextLine =
Arrays.stream(inputLine.split(" "))
.map(Integer::valueOf)
.collect(Collectors.toList());
mapToInt returns an IntStream and you cannot accumulate primitive elements into a ArrayList<T> therefore you can utilise the map operation which would yield a Stream<Integer> and then you can accumulate the elements into a ArrayList<T>.
That said, even if you change .mapToInt(s -> Integer.parseInt(s)) to .map(s -> Integer.parseInt(s)) your code still will not compile as the receiver type for the result is of type ArrayList<Integer> whereas the collect terminal operation will return a List<Integer> in this specific case.
Therefore to solve the remaining issue you can either have the receiver type as List<Integer> or leave the receiver type as is and then do .collect(Collectors.toCollection(ArrayList::new)); for the reduction operation yielding a specific List implementation.
Another variant to the already posted answer would be:
ArrayList<Integer> resultSet =
Pattern.compile(" ")
.splitAsStream(inputLine)
.map(Integer::valueOf)
.collect(Collectors.toCollection(ArrayList::new));

findFirst() for Java streams, but for n elements?

I want to collect the first n elements from a stream, without iterating through the entire thing. Is there a standard method that does this? Ala
MyList.stream()
.filter(x -> predicate(x))
.findFirstN(100)
would return a collection of up to 100 elements from the stream? My alternative is to evaluate the entire stream and then sample from the result, but that doesn't take advantage of the lazy evaluation inherent to streams.
MyList.stream()
.filter(x -> predicate(x))
.limit(100)

Categories

Resources