I try to get my head around java streams.
Atm I have this construct which does not work:
List<int[]> whiteLists = processes.stream()
.map(Process::getEventList)
.forEach(eventList -> eventList.stream()
.map(event -> event.getPropertie("whitelist"))
.map(propertie -> propertie.getIntArray())
.collect(Collectors.toList()));
}
The hierarchy is:
Process
Event
Property
Process::getEventList returns a list of Event objects
event.getPropertie("whitelist") returns a Property objects which hast the method getIntArray()
event.getPropertie() gives me an int-array.
How do I collect these array into a List of arrays?
Thanks!
You can't use forEach() as it takes a Consumer, meaning it will consume the stream, but can't return anything (so nothing to collect).
You need flatMap to stream the internal eventList as follows
List<int[]> whiteLists = processes.stream()
.flatMap(p -> p.getEventList().stream())
.map(event -> event.getPropertie("whitelist"))
.map(propertie -> propertie.getIntArray())
.collect(Collectors.toList());
Related
I'm using Java8 Streams to iterate through a list and for each of the element I invoke map and then I need to aggregate the results. My problem is that when I call groupingBy I also need to access the original object before calling map. Here is a snippet:
list.stream() //
.filter(item -> item.getType() == HUMAN) //
.map(item -> manager.itemToHuman(item.getId())) //
.filter(Objects::nonNull) //
.collect(Collectors.groupingBy(Human::getAge, Collectors.summarizingLong(item.getCount())));
The problem is with the call to Collectors.summarizingLong(item.getCount()) since item at this point is NOT accessible. Is there an elegant way to overcome this?
After doing map() stream transformed into Stream<Human> so you can't use item object in the collector.
You can transform item into a pair of Human object and count using SimpleEntry then use it on the collector.
list.stream()
.filter(item -> item.getType() == HUMAN)
.map(item ->
new AbstractMap.SimpleEntry<>(manager.itemToHuman(item.getId()), item.getCount()))
.filter(entry -> Objects.nonNull(entry.getKey()))
.collect(Collectors.groupingBy(entry -> entry.getKey().getAge(),
Collectors.summarizingLong(Map.Entry::getValue)));
The following lambda expressions take the values of a HashMap, gets a List of Objects in its ArrayList, adds all such Objects to another ArrayList, and then prints each attribute if a condition is met.
This works, but I am a bit frustrated I couldn't figure out how to do this in one step, as in, not using two lambda expressions.
Map<Integer, Person> people = new HashMap<Integer, Person>();
...
List<Object> objects = new ArrayList<Object>();
people.values().stream()
.map(p->p.getObjects())
.forEach(p->objects.addAll(p)); //note: can be multiple
objects.stream()
.filter(p->p.getClass().toString().contains("Keyword"))
.forEach(p->System.out.println(p.display()));
So is there a way I can go from line 2 to line 5 directly, which would in effect convert a stream of List of Objects to a stream of all of the Objects themselves?
You could merge your operations to a single stream pipeline as
List<Pet> cats = people.values().stream()
.flatMap(p -> p.getPets().stream())
.filter(p -> p.getClass().toString().contains("Cat")) // or Cat.class::isInstance
.collect(Collectors.toList());
and then perform operations on them as in your code such as
cats.forEach(cat -> System.out.println(cat.getName()));
An overall transformation of your code would look like:
Map<Integer, Person> people = ...;
people.values().stream()
.flatMap(p -> p.getPets().stream())
.filter(p -> p.getClass().toString().contains("Cat"))
.forEach(cat -> System.out.println(cat.getName()));
Is there a way to use the stream result (List<Integer> in my case) to perform an operation inside the function on that list (all at once)
So instead of this:
var cardTypesToRemove = existingIds.stream()
.filter(c -> !cardTypeIds.contains(c))
.collect(Collectors.toList());
repository.deleteBy(cardTypesToRemove);
Something like this (excuse me for pseudo code)
var cardTypesToRemove = existingIds.stream()
.filter(c -> !cardTypeIds.contains(c))
.collect(Collectors.collectingAndThen(repository.saveAll(resultList)));
No. Just pass the result of the collect as a regular method parameter.
var cardTypesToRemove = repository.saveAll(
existingIds.stream()
.filter(c -> !cardTypeIds.contains(c))
.collect(Collectors.toList());
I have a set of value objects:
Set<EntityKey> clientAssignedPlaceholderEntityKeys
Where the EntityKey class has the following properties:
private Integer objectRef;
private String entityType;
What is the most efficient way to extract the distinct objectRef values into a sorted list using streams?
I have the following but the fact that it calls stream() twice seems like a bad smell:
// Extract into a sorted list all the distinct placeholder objectRefs (regardless of type).
List<Integer> sortedPlaceholderObjectRefs = clientAssignedPlaceholderEntityKeys.stream()
.map(entityKey -> entityKey.getObjectRef())
.collect(Collectors.toSet())
.stream() // having to call stream() a 2nd time here feels sub-optimal
.sorted()
.collect(Collectors.toList());
Maybe:
sortedPlaceholderObjectRefs = clientAssignedPlaceholderEntityKeys.stream()
.map(entityKey -> entityKey.getObjectRef())
.sorted()
.distinct()
.collect(Collectors.toList());
EDIT:
calling .distinct() before .sorted() might be more optimal
clientAssignedPlaceholderEntityKeys.stream()
.map(ek -> ek.getObjectRef())
.sorted()
.distinct()
.collect(Collectors.toList());
Regarding your doubt about the order of sorted() and distinct()
Quoting the very first answer from here:
Chaining distinct() operation after sorted(), the implementation will utilize the sorted nature of the data and avoid building an internal HashSet.
List<Integer> sortedRefs = clientAssignedPlaceholderEntityKeys
.stream()
.map(EntityKey::getObjectRef)
.distinct()
.sorted()
.collect(Collectors.toList());
I have list of lists. i need to extract items from these lists based on index and make it individual arraylist. I tried doing it by adding
List<List<String>> multilist = new ArrayList<>();
List<List<String>> totalRecords= totalRecordsList;
List<String> targetList = totalRecords.stream().filter(e ->
e.get(index)!=null).flatMap(List::stream) .collect(Collectors.toCollection(ArrayList::new));
multilist.add(targetList);
But still inside list of lists rather than storing as individual arraylist objects, it is combining all the items. Can you please correct wherever i am wrong.
Thanks
This operation:
.flatMap(List::stream)
flattens everything in the input lists into a stream.
If you just want to take the index-th element of each list, replace this with:
.map(e -> e.get(index))
Overall:
totalRecords.stream()
.filter(e -> e.get(index)!=null)
.map(e -> e.get(index))
.collect(Collectors.toCollection(ArrayList::new))
You can avoid repeating the get by reversing the filter and map:
totalRecords.stream()
.map(e -> e.get(index))
.filter(Object::nonNull)
.collect(Collectors.toCollection(ArrayList::new))