Java init object and set property using Stream - java

I'm trying to clone a list to a new list and set a property in the new list.
I'm trying to use Java8 Stream as it makes cloning simple.
My code works but it gives this code smell from Sonar:
Local variables should not be declared and then immediately returned or thrown (squid:S1488)
Is there a way to do this without using a local variable?
code:
List<myObject> clonedList = listToClone.stream()
.map(item -> {
cloned = new myObject(item);
cloned.setLifeCycle("someLifeCycle");
return cloned;
})
.collect(Collectors.toList());
Thanks

It is a warning because you have used a new variable cloned unnecessarily instead of directly chaining functions like
List<myObject> clonedList = listToClone.stream()
.map(item -> {return (new myObject(item)).setLifeCycle("someLifeCycle");})
.collect(Collectors.toList());

You can try this:
List<myObject> clonedList = listToClone.stream()
.map(myObject::new)
.map(o -> {
o.setLifeCycle("someLifeCycle");
return o;
})
.collect(Collectors.toList());

public class MyObject{
private String someLifeCycle;
private Item item;
public MyObject(final String someLifeCycle,final Item item){
this.someLifeCycle = someLifeCycle;
this.item = item;
}
//getters and setters
}
And your code will be like this :
List<MyObject> clonedList = listToClone.stream()
.map(item -> new MyObject(item,"someLifeCycle")).collect(Collectors.toList());

Related

Return Element based on matching criteria from Stream

I have a list of custom object AllData. I would like to return one element from this list that matches a specific criteria (widgetId = 58). How would i use stream/filter/collections to return single AllData object matching my criteria. I have tried the below, however i get NoSuchElementException.
AppDatabase db = AppDatabase.getDbInstance(MyContext.getContext());
List<AllData> allDataList = db.allDataDao().getAllDataList();
AllData allData = allDataList.stream().findFirst().filter(e -> e.getMyTicker().getWidgetId() == 58).get();
You should filter the list first and the use findFirst
AllData allData = allDataList.stream()
.filter(e -> e.getMyTicker().getWidgetId() == 58)
.findFirst().get();
I would recommend to use orElse to avoid NoSuchElementException - if there is no value present in Optional
What happens if nothing is returned? You would want a default value to fall back on and also call findFirst() after filter(). Here you go:
public static void main(String[] args) {
List<MyObject> list = new ArrayList<>();
MyObject object = list.stream().filter(e -> e.getMyTicker().getWidgetId() == 58).findFirst().orElse(null);
}
public static class MyObject {
private Ticker myTicker;
public Ticker getMyTicker() {
return myTicker;
}
}
public static class Ticker {
private int widgetId;
public int getWidgetId() {
return this.widgetId;
}
}

How to convert Map to List of User Object?

I have a HashMap with the following structure:
Map<Object, List<OtherObject>>
And I want to convert it to:
List<FinalObject>
being FinalObject:
public class FinalObject {
private Object object;
private List<OtherObject> otherObject;
}
Assuming you have a constructor like:
public FinalObject(Object obj, List<OtherObject> list) {
this.object = obj;
this.otherObject = list;
}
then you can do:
List<FinalObject> newList = map.entrySet()
.stream().map(e -> new FinalObject(e.getKey(), e.getValue()))
.collect(Collectors.toList());
A corresponding forEach approach would be:
List<FinalObject> finalList = new ArrayList<>();
map.forEach((k,v) -> finalList.add(new FinalObject(k, v)));

Flattening a collection within collection to get a single List<>

I'm quite new to using Java and was trying to flatten collections within collections using map to try and get a single List. However I don't seem to be able to get this working. In order to reproduce this I've created quite a simple example of what I'm trying to do. Here is what I have so far:
ClassA
import java.util.List;
public class ClassA {
private List<ClassB> listOfClassB;
public ClassA(List<ClassB> listOfClassB) {
this.listOfClassB = listOfClassB;
}
public List<ClassB> getListOfClassB() {
return this.listOfClassB;
}
}
ClassB
public class ClassB {
private String item;
public ClassB(String item) {
this.item = item;
}
public String getItem() {
return item;
}
}
Main
public class Main {
public static void main(String[] args) {
ClassB firstClass = new ClassB("A");
ClassB secondClass = new ClassB("B");
ClassB thirdClass = new ClassB("C");
ClassB fourthClass = new ClassB("D");
ArrayList<ClassB> firstClassList = new ArrayList<>();
ArrayList<ClassB> secondClassList = new ArrayList<>();
firstClassList.add(firstClass);
firstClassList.add(secondClass);
secondClassList.add(thirdClass);
secondClassList.add(fourthClass);
ArrayList<ClassA> classes = new ArrayList<>();
classes.add(new ClassA(firstClassList));
classes.add(new ClassA(secondClassList));
List<List<String>> collect = classes.stream().map(c -> c.getListOfClassB().stream().map(ClassB::getItem).collect(Collectors.toList())).collect(Collectors.toList());
}
}
As you can see on the bottom I am able to get List<List<String>> but what I'm looking to get is a List<String> of the items within ClassB. I've tried using a flatmap for this but I couldn't get it working and was looking for some guidance.
Thanks in advance.
Here is the flatmap example which works fine:
classes.stream().flatMap(aclass -> aclass.getListOfClassB().stream())
.forEach(b -> System.out.println("Class B Item Name : "+b.getItem()));
It gives the following output:
Class B Item Name : A
Class B Item Name : B
Class B Item Name : C
Class B Item Name : D
and to get the exact answer:
List<String> collect2 = classes.stream().flatMap(aclass -> aclass.getListOfClassB().stream())
.map(b -> b.getItem())
.collect(Collectors.toList());
it gives me a list as follows:
collect2 : [A, B, C, D]
This will return a list of Strings of ClassB :
List<String> listB = classes.stream().flatMap(a-> a.getListOfClassB().stream())
.map(ClassB::getItem)
.collect(Collectors.toList());
Here, we use a flatMap and then map the getItem of ClassB and finally use .collect(...) to return the list.

Java8 Stream API: Group a list into a custom class

I'm trying to construct a custom class instance by Java8's stream API.
public class Foo {
Group group;
// other properties
public Group getGroup() { return this.group; }
public enum Group { /* ... */ };
}
public class FooModel {
private Foo.Group group;
private List<Foo> foos;
// Getter/Setter
}
...
List<Foo> inputList = getFromSomewhere();
List<FooModel> outputList = inputList
.stream()
.collect(Collectors.groupingBy(Foo::getGroup,
???));
But I don't know how the Collector downstream must be.
Do I have to implement a Collector myself (don't think so) or can this be accomplished by a combination of Collectors. calls?
You are looking for something like this:
List<FooModel> outputList = inputList
.stream()
.collect(Collectors.groupingBy(Foo::getGroup))// create Map<Foo.Group,List<Foo>>
.entrySet().stream() // go through entry set to create FooModel
.map(
entry-> new FooModel (
entry.getKey(),
entry.getValue()
)
).collect(Collectors.toList());

map and apply in java stream

I currently have something like below
List<String> myNewList = myList
.stream()
.map(item->{
return mappedItem
})
.collect(Collectors.toList());
repository.save(myNewList);
In Optional, I can perform operations on the mapped item by using ifPresent method like below
myOptional
.map(item -> {
return mappedItem
})
.ifPresent(newItem -> {
repository.save(newItem);
});
I was wondering if I can do something like the above on stream. Rather than declaring myNewList, is there a way I can collect the new List and apply my function on the new list?
Update: Based on the answer from #tagir-valeev, I modified my code as below
myList
.stream()
.map(item->{
return mappedItem
})
.collect(Collectors.collectingAndThen(Collectors.toList(),
list -> {
repository.save(list);
return list;
}
));
You can create your custom collector like this:
myList.stream().map(..)
.collect(Collectors.collectingAndThen(Collectors.toList(), repository::save));
If save return type is void, it would be more ugly as you need to return something from collect:
myList.stream().map(..)
.collect(Collectors.collectingAndThen(Collectors.toList(),
list -> {repository.save(list);return list;}));
You may declare special method in your Repository class:
class Repository {
Collector<MyItemType, ?, List<MyItemType>> saving() {
return Collectors.collectingAndThen(Collectors.toList(),
list -> {this.save(list);return list;});
}
void save(List<MyItemType> list) { ... }
}
And use it:
myList.stream().map(..).collect(repository.saving());

Categories