How to convert Map to List of User Object? - java

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)));

Related

How to implement forEach loop of list using completableFuture of java8

List<String> list1 = new ArrayList<String>();
list1.add("Class1");
list1.add("Class2");
List<String> list2 = new ArrayList<String>();
list2.add("Book1");
list2.add("Book2");
List<List<String>> combine = new ArrayList<List<String>>();
List<List<Object>> objList = new ArrayList<List<Object>>();
combine.add(list1);
combine.add(list2);
List<List<Object>> finalResponse = getObjList(combine, objList);
}
private static List<List<Object>> getObjList(List<List<String>> combine, List<List<Object>> objList) {
Student std = new Student();
AtomicInteger counter = new AtomicInteger(0);
combine.forEach((items)->{
std.setList(items);
std.setPageId(counter.incrementAndGet());
// rest call
List<Object> response = null; // we are doing rest call here
objList.add(response);
});
return objList;
}
Please help me.We are preparing the Student object and sending it to rest api. We need to do multiple time depending on List<List> size.Need to use CompletableFuture
Not sure about how your API looks like but below is an example that gives you a fair idea of how you can call your API in parallel.
#Service
public class RestService1 {
private final String YOUR_API = "your-api";
#Autowired
private RestTemplate restTemplate;
Executor executor = Executors.newFixedThreadPool(5);
public void callApi() {
List<Object> list = List.of(new Object(), new Object(), new Object()); // your list of object.
Map<String, String> map = new HashMap<>();
List<CompletableFuture<Student>> completableFutureList = list.stream().map(object -> {
return CompletableFuture.supplyAsync(() -> callRestApi(map), executor).thenApply(jsonNode -> callRestApi(jsonNode));
}).collect(Collectors.toList());
List<Student> result = completableFutureList.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
private JsonNode callRestApi(final Map<String, String> params) {
return restTemplate.getForObject(YOUR_API, JsonNode.class, params);
}
private Student callRestApi(final JsonNode jsonNode) {
// deserialize it and return your Currency object
return new Student(); // just for example
}
// Keep it somewhere in Model package (here just for example)
private class Student {
// define your arguments
}
}

java 8 stream groupingBy into collection of custom object

I have the following class structure
public class Store {
private Long storeId;
private Long masterStoreId;
private String operatorIdentifier;
}
public class StoreInfo {
private String operatorIdentifier;
private Set<Long> slaveStoreIds;
public StoreInfo(String operatorIdentifier, Set<Long> slaveStoreIds) {
super();
this.operatorIdentifier = operatorIdentifier;
this.slaveStoreIds = slaveStoreIds;
}
}
I want to collect the "List<Store" into a "Map<Long, StoreInfo>". Is it possible to do so in a single operation/iteration?
List<Store> stores;
Map<Long, Set<Long>> slaveStoresAgainstMasterStore = stores.stream().collect(Collectors
.groupingBy(Store::getMasterStoreId, Collectors.mapping(Store::getStoreId, Collectors.toSet())));
Map<Long, StoreInfo> storeInfoAgainstMasterStore = stores.stream()
.collect(
Collectors
.toMap(Store::getMasterStoreId,
val -> new StoreInfo(val.getOperatorIdentifier(),
slaveStoresAgainstMasterStore.get(val.getMasterStoreId())),
(a1, a2) -> a1));
As masterStoreId and operatorIdentifier are same same in group(comfirmed in comment) you can groupingBy both creating pair of them using AbstractMap.SimpleEntry. Then using Collectors.toMap create map.
Map<Long, StoreInfo> storeInfoMap =
stores.stream()
.collect(Collectors.groupingBy(
e -> new AbstractMap.SimpleEntry<>(e.getMasterStoreId(),
e.getOperatorIdentifier()),
Collectors.mapping(Store::getStoreId, Collectors.toSet())))
.entrySet()
.stream()
.collect(Collectors.toMap(e -> e.getKey().getKey(),
e -> new StoreInfo(e.getKey().getValue(), e.getValue())));
To complete the implementation, you were attempting. You need to ensure merging capability within StoreInfo such as :
public StoreInfo(String operatorIdentifier, Long slaveStoreId) {
this.operatorIdentifier = operatorIdentifier;
this.slaveStoreIds = new HashSet<>();
this.slaveStoreIds.add(slaveStoreId);
}
public static StoreInfo mergeStoreInfo(StoreInfo storeInfo1, StoreInfo storeInfo2) {
Set<Long> slaveIds = storeInfo1.getSlaveStoreIds();
slaveIds.addAll(storeInfo2.getSlaveStoreIds());
return new StoreInfo(storeInfo1.getOperatorIdentifier(), slaveIds);
}
this would simplify the implementation of collector and you an invoke these correspondingly:
Map<Long, StoreInfo> storeInfoAgainstMasterStore = stores.stream()
.collect(Collectors.toMap(Store::getMasterStoreId,
store -> new StoreInfo(store.getOperatorIdentifier(), store.getStoreId()),
StoreInfo::mergeStoreInfo));

Java 8 List to Map conversion

I have a problem with conversion List Object to Map String, List Object. I'm looking for Map with a keys name of all components in cars, and a value is represented by cars with this component
public class Car {
private String model;
private List<String> components;
// getters and setters
}
I write a solution but looking for a better stream solution.
public Map<String, List<Car>> componentsInCar() {
HashSet<String> components = new HashSet<>();
cars.stream().forEach(x -> x.getComponents().stream().forEachOrdered(components::add));
Map<String, List<Car>> mapCarsComponents = new HashMap<>();
for (String keys : components) {
mapCarsComponents.put(keys,
cars.stream().filter(c -> c.getComponents().contains(keys)).collect(Collectors.toList()));
}
return mapCarsComponents;
}
You could do it with streams too, but I find this a bit more readable:
public static Map<String, List<Car>> componentsInCar(List<Car> cars) {
Map<String, List<Car>> result = new HashMap<>();
cars.forEach(car -> {
car.getComponents().forEach(comp -> {
result.computeIfAbsent(comp, ignoreMe -> new ArrayList<>()).add(car);
});
});
return result;
}
Or using stream:
public static Map<String, List<Car>> componentsInCar(List<Car> cars) {
return cars.stream()
.flatMap(car -> car.getComponents().stream().distinct().map(comp -> new SimpleEntry<>(comp, car)))
.collect(Collectors.groupingBy(
Entry::getKey,
Collectors.mapping(Entry::getValue, Collectors.toList())
));
}
I know this is a Java question, and there is already a Java answer. However, I would like to add that Kotlin, which is a JVM language and perfectly interoperable with Java, you can do things like this very easily and cleanly:
val carsByComponent = cars
.flatMap { it.components }
.distinct()
.map { component -> component to cars.filter { car -> component in car.components } }
.toMap()
or even more concise, allthough less readable:
val carsByComponent = cars
.flatMap { car -> car.components.map { it to car } }
.groupBy { it.first }
.mapValues {it.value.map { it.second }}

Problem with collecting to stream after filter to map

I have two lists and I have to create a map from them. One, I am iterating in for loop and second I wanted to go threw by a stream and than collect to map, but I have no idea how to use Collectors.toMap in that specific case. Is it possible?
I already made a solution, but not using stream, however I am very curious is it possible to do it and if yes, how to do it?
public void findMatch(List<ObjectA> objectAList, List<ObjectB> objectBList) {
Map<ObjectB, ObjectA> objectBObjectAMap = new HashMap<>();
for (ObjectB objectB : objectBList) {
if (isNull(objectB.getHandoverTime())) {
objectBObjectAMap.putAll(
objectAList
.stream()
.filter(objectA -> {
ObjectC objectC = objectB.getObjectC();
return objectA.getNumber().equals(objectC.getNumber())
&& objectA.getQuality().equals(objectC.getQuality());
})
.collect(Collectors.toMap(???)));
}
}
}
You can try with flatMap:
Map<ObjectB, ObjectA> objectBObjectAMap =
objectBList.stream()
.filter(b -> isNull(b.getHandoverTime()))
.flatMap(b -> objectAList.stream()
.filter(a -> {
ObjectC c = b.getObjectC();
return a.getNumber().equals(c.getNumber()) &&
a.getQuality().equals(c.getQuality());
})
.map(a -> new SimpleEntry<>(b,a)))
.collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue));
This is assuming each ObjectB instance will not be associated with more than one ObjectA instance.
I tried to restore your problem and I create three simple clasess
public class ObjectA {
Long number;
String quality;
...
}
public class ObjectB {
Long number;
String quality;
Date handoverTime;
ObjectC objectC;
...
}
public class ObjectC {
Long number;
String quality;
...
}
and main function with I hope your processing.
ObjectA a1 = new ObjectA();
a1.setNumber(1L);
a1.setQuality("aaa1");
ObjectA a2 = new ObjectA();
a2.setNumber(2L);
a2.setQuality("aaa2");
List<ObjectA> aList = new ArrayList<>();
aList.add(a1);
aList.add(a2);
ObjectB b1 = new ObjectB();
b1.setNumber(3L);
b1.setQuality("bbb1");
//b1.setHandoverTime(new Date());
ObjectC c1 = new ObjectC();
c1.setNumber(1L);
c1.setQuality("aaa1");
b1.setObjectC(c1);
ObjectB b2 = new ObjectB();
b2.setNumber(4L);
b2.setQuality("bbb2");
//b2.setHandoverTime(new Date());
ObjectC c2 = new ObjectC();
c2.setNumber(2L);
c2.setQuality("aaa2");
b2.setObjectC(c2);
List<ObjectB> bList = new ArrayList<>();
bList.add(b1);
bList.add(b2);
Map<ObjectB, ObjectA> mapzz = findMatch(aList, bList);
System.out.println(mapzz);
and following method
public static Map<ObjectB, ObjectA> findMatch(List<ObjectA> objectAList, List<ObjectB> objectBList) {
List<ObjectA> checkPoint1 = new ArrayList<>();
Map<ObjectB, ObjectA> mapzz = new HashMap<>();
mapzz.putAll(
objectBList.stream()
.filter(objB -> isNull(objB.getHandoverTime()))
.collect(Collectors.toMap(Function.identity(),
objB -> objectAList
.stream()
.filter((a) -> a.getNumber().equals(objB.getObjectC().getNumber()) && a.getQuality().equals(objB.getObjectC().getQuality()))
.peek(checkPoint1::add)
.findAny().get()
))
);
return mapzz;
}
Result is as follow:
{ObjectB{number=3, quality=bbb1, handoverTime=null, objectC=ObjectC{number=1, quality=aaa1}}=ObjectA{number=1, quality=aaa1}, ObjectB{number=4, quality=bbb2, handoverTime=null, objectC=ObjectC{number=2, quality=aaa2}}=ObjectA{number=2, quality=aaa2}}
I hope that it helps.

Java init object and set property using Stream

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());

Categories