I am doing a test case to use Optional.ofNullable and Stream together, and I faced to ways to do the same, both have the same output.
List<String> list1 = List.of("abc","abc");
Optional.ofNullable(list1).stream().flatMap(List::stream).map(e -> e.concat("def")).collect(Collectors.toList());
List<String> list2 = List.of("abc","abc");
Stream<String> stream = Optional.ofNullable(list2).map(List::stream).orElseGet(Stream::empty);
stream.map(e-> e.concat("def")).collect(Collectors.toList());
My question is, why at the first example I use a flatMap and a map and at the second one I use two map's to do the same job. If I try to use two map's at the first example it doesn't work.
To understand what's happening it can help to look at all the return types:
First example:
List<String> list1 = List.of("abc","abc");
List<String> result =
Optional.ofNullable(list1) // Optional<List<String>>
.stream() // Stream<List<String>>
.flatMap(List::stream) // Stream<String>
.map(e -> e.concat("def")) // Stream<String>
.collect(Collectors.toList()); // List<String>
In this example you go straight from an Optional<List<String>> to a Stream<List<String>> and then use the methods of the latter interface. The methods used here are (in order):
Optional#ofNullable(T)
Optional#stream()
Stream#flatMap(Function)
Stream#map(Function)
Stream#collect(Collector)
Second example:
List<String> list2 = List.of("abc","abc");
List<String> result =
Optional.ofNullable(list2) // Optional<List<String>>
.map(List::stream) // Optional<Stream<String>>
.orElseGet(Stream::empty); // Stream<String>
.map(e-> e.concat("def")) // Stream<String>
.collect(Collectors.toList()); // List<String>
In this example you go from an Optional<List<String>> to an Optional<Stream<String>> (another optional) and then extract the Stream<String> via Optional#orElseGet(Supplier). Afterwards, you use the methods of the Stream interface. The methods used here are (in order):
Optional#ofNullable(T)
Optional#map(Function)
Optional#orElseGet(Supplier)
Stream#map(Function)
Stream#collect(Collector)
Related
I have a list of names and a list of versions. I want to get all permutations which are constructed by concatenating the string from two lists. I am using two for loop to do this but I want to switch to a more functional style approach. Here is my solution:
List<String> names = new ArrayList<>();
List<String> versions = new ArrayList<>();
List<String> result = new ArrayList<>();
names.forEach(name -> versions.stream().map(version -> result.add(name.concat(version))));
Is there a better way to do it?
You are looking for the "Cartesian Product" of names and versions — basically the return set/list from the aforementioned sets/lists.
final Stream<List<String>> result = names.stream()
.flatMap(s1 -> versions.stream().flatMap(s2 -> Stream.of(Arrays.asList(s1, s2))));
result.forEach(System.out::println);
Keep in mind that operation is super expensive. Google's Guava have this implemented also under com.google.common.collect.Sets.cartesianProduct(s1, s2).
You should look forward to use flatMap while streaming over names and then performing map operation further correctly as:
List<String> result = names.stream() // for each name
.flatMap(name -> versions.stream() // for each version
.map(version -> name.concat(version))) // concat version to the name
.collect(Collectors.toList()); // collect all such names
Or a bit tidier:
final List<String> result = names.stream() // Stream the Names...
.flatMap(name -> versions.stream() // ...together with Versions.
.map (version -> name.concat(version))) // Combine Name+Version
.collect(Collectors.toList()); // & collect in List.
I have 2 arrays and want to make a list of role.getRoleName() only with elements that are in both arrays using streams.
final List<String> roleNames = new ArrayList<>();
roleNames = Arrays.stream(roles).filter(role -> role.getRoleId()
== Arrays.stream(permissions).map(permission -> permission.getRoleId()));
when I write the above code I get
Operator '==' cannot be applied to 'int', 'java.util.stream.Stream'
I understand the error, but I don't know the solution of how to make the permissions stream in only permission.getRoleId integers.
There is no way to compare such incompatible types as int and Stream.
Judging from what you've shown, Stream#anyMatch might a good candidate.
roleNames = Arrays.stream(roles)
.map(Role::getRoleId)
.filter(id -> Arrays.stream(permissions).map(Role::getRoleId).anyMatch(p -> p.equals(id)))
.collect(Collectors.toList());
This part Arrays.stream(permissions).map(Role::getRoleId) may be pre-calculated and stored into a Set.
final Set<Integer> set = Arrays.stream(permissions)
.map(Role::getRoleId)
.collect(Collectors.toSet());
roleNames = Arrays.stream(roles)
.filter(role -> set.contains(role.getRoleId()))
.map(Role::getRoleName)
.collect(Collectors.toList());
What you can do is collect unique roleIds for the array of Permissions into a Set as a computed result and perform a contains check as you iterate through the array of Roles. This could be done as :
final Set<Integer> uniqueRoleForPermissions = Arrays.stream(permissions)
.map(Permission::getRoleId)
.collect(Collectors.toSet());
final List<String> roleNames = Arrays.stream(roles)
.filter(role -> uniqueRoleForPermissions.contains(role.getRoleId()))
.map(Role::getRoleName)
.collect(Collectors.toList());
This code will add all elements from mylist to mylist2.
How can I change it to add elements if it finds a comma, and separates them?
ArrayList<String> mylist = new ArrayList<String>();
mylist.add("test");
mylist.add("test2, test3");
ArrayList<String> mylist2 = new ArrayList<String>();
mylist.stream()
.forEach(s -> mylist2.add(s));
mylist2.stream().forEach(System.out::println);
So, current output is:
test
test2, test3
and I need the second arraylist to contain and then print:
test
test2
test3
I know in Java 8 there are filters. Could I do something like
.filter(p -> p.contains(",")...
then something to split it?
You could do a flatMap including a Pattern::splitAsStream like
Pattern splitAtComma = Pattern.compile("\\s*,\\s*");
List<String> myList2 = mylist.stream().flatMap(splitAtComma::splitAsStream)
.collect(Collectors.toList());
You could split each string and flatMap the resulting array:
mylist.stream()
.flatMap(x -> Arrays.stream(x.split(", ")))
.forEach(System.out::println);
You can use :
mylist.forEach(str -> mylist2.addAll(Arrays.asList(str.split("\\s*,\\s*"))));
Output
test
test2
test3
and I need the second arraylist to contain and then print:
You could create two streams : one for creating the target List and another one for displaying it.
List<String> list2 = mylist.stream()
.flatMap(x -> Arrays.stream(x.split(", ")))
.collect(Collectors.toList());
list2.stream().forEach(System.out::println);
or with one stream by using peak() to output the element of the Stream before collecting into a List :
List<String> list2 = mylist.stream()
.flatMap(x -> Arrays.stream(x.split(", ")))
.peek(System.out::println)
.collect(Collectors.toList());
I have a Map<String, List<String>>. I want to transform this map to a List after filtering on the map's key.
Example:
Map<String, List<String>> words = new HashMap<>();
List<String> aList = new ArrayList<>();
aList.add("Apple");
aList.add("Abacus");
List<String> bList = new ArrayList<>();
bList.add("Bus");
bList.add("Blue");
words.put("A", aList);
words.put("B", bList);
Given a key, say, "B"
Expected Output: ["Bus", "Blue"]
This is what I am trying:
List<String> wordsForGivenAlphabet = words.entrySet().stream()
.filter(x-> x.getKey().equalsIgnoreCase(inputAlphabet))
.map(x->x.getValue())
.collect(Collectors.toList());
I am getting an error. Can someone provide me with a way to do it in Java8?
Your sniplet wil produce a List<List<String>> not List<String>.
You are missing flatMap , that will convert stream of lists into a single stream, so basically flattens your stream:
List<String> wordsForGivenAlphabet = words.entrySet().stream()
.filter(x-> x.getKey().equalsIgnoreCase(inputAlphabet))
.map(Map.Entry::getValue)
.flatMap(List::stream)
.collect(Collectors.toList());
You can also add distinct(), if you don't want values to repeat.
Federico is right in his comment, if all you want is to get the values of a certain key (inside a List) why don't you simply do a get (assuming all your keys are uppercase letters already) ?
List<String> values = words.get(inputAlphabet.toUpperCase());
If on the other hand this is just to understand how stream operations work, there is one more way to do it (via java-9 Collectors.flatMapping)
List<String> words2 = words.entrySet().stream()
.collect(Collectors.filtering(x -> x.getKey().equalsIgnoreCase(inputAlphabet),
Collectors.flatMapping(x -> x.getValue().stream(),
Collectors.toList())));
As was previously told after collect you will get List<List<String>> with only one or zero value in it. You can use findFirst instead of collect it will return you Optional<List<String>>.
I've got a list List<String> (list1) and a function Integer foo(String s). Using the power of Java 8 I want to convert list1 to a List<Integer> by applying foo to each item of list1. The following code works but has a little problem:
List<Integer> list2 = list1.stream().mapToInt(s -> foo(s)).boxed().collect(Collectors.toList());
When fooreturns null for some element of list1 a NullPointerExceptionis thrown. Additionally my solution looks a little bit inconvenient. So is there a better one?
If you are mapping it to an Integer, I don't see the need to use mapToInt and then use boxed.
Instead, you could simple use map and then filter to exclude the nulls.
List<Integer> list2 =
list1.stream()
.map(s -> foo(s))
.filter(Objects::nonNull)
.collect(Collectors.toList());
If you want to keep the nulls in the list, simply remove the filter
List<Integer> list2 =
list1.stream()
.map(s -> foo(s))
.collect(Collectors.toList());
As an alternative to the answer already given, you can do this:
List<Integer> list2 = new ArrayList<>();
list1.forEach(s -> list2.add(foo(s)));