I'm creating a new list. I write objects to the new list from the list of entities. Then I will clean items from the list of entities appropriately, which also results in the removal of objects from this new list.
final ContributionEntity contributionEntity = this.findContribution(contributionId, DataStatus.WAITING, user, MovieField.PHOTO);
final Set<Long> idsToAddBeforeCleanUp = contributionEntity.getIdsToAdd();
this.cleanUpIdsToAdd(contributionEntity.getIdsToAdd(), contribution.getIdsToAdd(), contributionEntity.getMovie().getPhotos());
private void cleanUpIdsToAdd(final Set<Long> idsToAddFromEntity, final Set<Long> idsToAddFromDto,
final List<? extends MovieInfoEntity> entities) {
for (final Iterator<Long> it = idsToAddFromEntity.iterator(); it.hasNext(); ) {
final Long id = it.next();
if (!idsToAddFromDto.contains(id)) {
it.remove();
this.delete(entities, id);
}
}
}
This code removes the entity from the list of subject photos contributionEntity , but also removes objects from the list idsToAddBeforeCleanUp.
How do I copy a list from an entity and make it independent of this entity? I do not want to delete items from the list idsToAddBeforeCleanUp.
final Set<Long> idsToAddBeforeCleanUp = new HashSet<>();
contributionEntity.getIdsToAdd().foreach(item -> idsToAddBeforeCleanUp.add(item));
You didn't really create a new set, you just made another reference to the set. With the code above you make a new list and should it work.
Related
I am using JPA and I have an entity/class named Order. I have a rest GET endpoint to fetch an order by an id. It works perfectly fine. The order entity looks like below:
#Entity
public class Order {
#Id
private Long id;
#Column
private List<String> transactionRefs;
}
Now, in one particular scenario, I need to fetch the order from the database and add another item to the transactionRefs and save it. So I do as below:
Order order = orderRepository.findById(1).get();
List<String> transactionList = order.getTransactionRefs();
transactionList.add("transaction-ref");
I get the below error when I do that:
java.lang.UnsupportedOperationException: null\n\tat java.util.AbstractList.add(AbstractList.java:148)
If I do as below, that fixes the problem:
Order order = orderRepository.findById(1).get();
List<String> transactionList = order.getTransactionRefs();
transactionList = new ArrayList<>(transactionList);
transactionList.add("transaction-ref");
So, I need to know if I am in the right direction here and is this an expected error scenario.
Update:
Whenever we are adding an item to the list, we have the below condition :
if (transactionRefs == null) {
transactionRefs = new ArrayList<>();
}
So, whenever the transactionref is saved for the first time, we cast it to a ArrayList.
Update 2 :
Below is the getter for the transactionRef:
public List<String> getTransactionRefs(){
if (this.transactionRefs != null) {
return Arrays.asList(this.transactionRefs.split(","));
}
return null;
}
This is the cause of your exception
return Arrays.asList(this.transactionRefs.split(","));
Arrays.asList returns a collection backed by the array and it can't be modified with add or addAll. You need to create the List just like you are doing in the question:
List<String> transactionList = order.getTransactionRefs();
transactionList = new ArrayList<>(transactionList);
For more examples:
How to add elements in List when used Arrays.asList()
Regarding immutable List (created by Arrays.asList())
How can I remove the element from the list if some inner list attribute value fails to meet the condition.The trick here is that attribute is itself a list and comparison is based on some attribute of that inner list. Please refer the below sample and help out to fill the comment section in code:
Object :
Class product{
private String productId;
private String productName;
private List<Attribute> attributeList;
public static class Attribute{
private Long attributeId;
}
}
Driver class :
Class Driver{
List<product> productList = new ArrayList<product>();
/*
Remove the object from productList if attributeList doesn't contain attribute with attributeId = x;
*/
}
What you can do it to stream over original list, and leave only objects which satisfy the condition. It might look something like this:
List<Product> filtered = productList.stream()
.filter( p -> p.attributeList().stream().anyMatch( a -> a.attributeId.equals(x))
.collect(Collectors.toList())
in this live we are actually checking if nested list contains at least one object with attributeId = x
p.attributeList().stream().anyMatch( a -> a.attributeId.equals(x)
You can do a foreach loop and remove the unwanted elements. In "product" class you can insert a "FindInnerAtribute" function to search inside the Attributes list and return true if there is some.
List<product> productList = new ArrayList<product>();
for(product p : productList){
if ( p.FindInnerAttribute(x) ){
productList.remove(p);
}
}
How to remove from list
I have a list of entity. these are the response from db. I have another list of long. In the list of entity, each entity object has a filed called id. Those id will always be in ascending order.I need to traverse the entity list as per the order given through the list of long. Also I need to maintain another list of response object which will have few more fields than what we have in the entity list. I can not use transient also. The code below will give you an idea.
public List<ResponseObject> convert(List<EntityObject> entityList, List<Long> orderedIdList) {
List<ResponseObject> responseList = new ArrayList<>();
for (EntityObject object : entityList) {
ResponseObject responseObject = new ResponseObject();
responseObject.someSettermethod(object.someGettermethod());
/* other setters in responseObject which are not present in the entity object */
responseObject.otherSetters("some value");
responseList.add(responseObject);
};
return sortInOrder(responseList, orderedIdList);
}
private List<ResponseObject> sortInOrder(List<ResponseObject> responseList,List<Long> orderedIdList) {
List<ResponseObject> finalList = new ArrayList<>();
for(Long id : orderedIdList){
for(ResponseObject response : responseList){
if(response.getId().equals(id)){
finalList.add(response);
}
}
}
return finalList;
}
This is how it has been implemented for now. I would like to know if there is any better approach to enhance the performance to achieve the same output.
The sortInOrder method can be done faster than O(N^2):
Assuming, Ids are unique (let me know if its a wrong assumption):
Idea:
Create a map of Id to responseObject by iterating over the response list O(n).
Iterate over orderedIdList and check for id in map, if the id exists, add the value to response Object.
private List<ResponseObject> sortInOrder(List<ResponseObject> responseList,List<Long> orderedIdList) {
List<ResponseObject> finalList = new ArrayList<>();
Map<Long, ResponseObject> map = responseList.stream().collect(Collectors.toMap(ResponseObject::getId, respObj -> respObj));
for(Long id : orderedList) {
if(map.containsKey(id)) {
finalList.add(map.get(id));
}
}
return finalList;
}
If these lists aren't huge (like in many many thousands of entries), I wouldn't worry about performance. It's reasonable as it is and as long as you don't fail any specific performance requirements you shouldn't optimize your code for performance anyway.
You could on the other hand optimize your code for readability
by using a comparator to sort your list
by using the streams API to reduce the depth of your methods.
The comparator could be constructed using the ordering list and then comparing the indices of the ids from your resultList.
The comparator could look similar to this one:
private static class IndexComparator implements Comparator<Long> {
private final List<Long> order;
private IndexComparator(List<Long> order) {
this.order = order;
}
#Override
public int compare(Long id1, Long id2) {
return Comparator.comparingLong(order::indexOf).compare(id1, id2);
}
}
If you use map instead of a list like below, you can do it with complexity O(n) instead of O(n2)
public List<ResponseObject> convert(List<EntityObject> entityList, List<Long> orderedIdList) {
Map<Long, ResponseObject> responseMap = new HashMap<Long, ResponseObject>();
for (EntityObject object : entityList) {
ResponseObject responseObject = new ResponseObject();
responseObject.someSettermethod(object.someGettermethod());
/* other setters in responseObject which are not present in the entity object */
responseObject.otherSetters("some value");
responseMap.put(responseObject.getId(), responseObject);
};
return sortInOrder(responseMap, orderedIdList);
}
private List<ResponseObject> sortInOrder( Map<Long, ResponseObject> responseMap, List<Long> orderedIdList) {
List<ResponseObject> finalList = new ArrayList<>();
for(Long id : orderedIdList){
finalList.add(responseMap.get(id));
}
return finalList;
}
I have a problem to deep copy an ArrayList containing Attribute objects. After I have copied the ArrayList dataSet in a new one called trainingSet, I am trying to clear (of the trainingSet) all the content of the internal ArrayList of the Attribute called data. When I do so all the same content of the the ArrayList dataSet (data of dataSet) gets cleared, too. So in that case I have tried to deep copy all the content of the original list to the new one using the below tuts:
http://javarevisited.blogspot.gr/2014/03/how-to-clone-collection-in-java-deep-copy-vs-shallow.html#axzz4ybComIhC
https://beginnersbook.com/2013/12/how-to-clone-an-arraylist-to-another-arraylist/
How to make a deep copy of Java ArrayList
but I got the same behavior. So can someone please tell me how I can fix this problem and where the wrong thinking is?
Thank you for help.
ID3Algorithm.java
...
ArrayList<Attribute> dataSet = new ArrayList<dataSet>();
ArrayList<Attribute> trainingSet = new ArrayList<Attribute>(dataSet);
for(Attribute att : trainingSet) {
att.GetData().clear(); // At this point all the data in dataSet are cleared,too.
}
...
Attribute.java
public class Attribute
{
private String name;
private ArrayList<String> branchNames = new ArrayList<String>();
private ArrayList<String> data = new ArrayList<String>();
private ArrayList<Branch> branches = new ArrayList<Branch>();
private HashMap<String, Integer> classes = new HashMap<String, Integer>();
private ID3Algorithm id3;
private Leaf leaf = null;
public ArrayList<String> GetData() { return data; }
public Attribute(String attribName, ArrayList<String> attribBranchNames, ArrayList<String> attribData, ID3Algorithm algo) {
name = attribName;
branchNames = attribBranchNames;
data = attribData;
id3 = algo;
}
...
}
When you are assigning a value to trainingSet
ArrayList<Attribute> trainingSet = new ArrayList<Attribute>(dataSet);
You are only passing the references for the existing attributes into a new list. It is not a new list of different attribute objects. The first link you post, describes this process in detail. I would re-read it in depth.(The first example is a shallow copy)
http://javarevisited.blogspot.gr/2014/03/how-to-clone-collection-in-java-deep-copy-vs-shallow.html#axzz4ybComIhC
So when you call
att.GetData().clear();
You are clearing the orginal attribute objects data (which dataset also references)
Try creating new Attribute objects and assigning new data to each(copied from the orginal) then adding those to your trainingSet list.
I map business objects to entities and there are cases where the structure of an entity is different from the business objects.
I have userCategories which are stored in the business object RecipeBo as strings, because the BO does not have to know anything about the internal structure of the entities. These strings need to be mapped to a relation of Recipe and RecipeUserCategoryRel, in addition to it, another field, userId of RecipeBo needs to be mapped in this RecipeUserCategoryRel too.
My approach (which works) is to create a wrapper and manually create the relations by hand, but this looks like tinkering:
public class BoMapper
{
private final static ModelMapper modelMapper = new ModelMapper();
static
{
modelMapper.addMappings(new IngredientMap());
}
public static void map(Object from, Object to)
{
modelMapper.map(from, to);
if (from instanceof RecipeBo && to instanceof Recipe)
{
RecipeBo recipeBo = (RecipeBo)from;
List<String> userCategories = recipeBo.getUserCategories();
List<RecipeUserCategoryRel> recipeUserCategoryRels = new ArrayList<>();
for (String userCategory : userCategories)
{
recipeUserCategoryRels.add(new RecipeUserCategoryRel(userCategory, recipeBo.getUserId()));
}
Recipe recipe = (Recipe)to;
recipe.setRecipeUserCategoryRels(recipeUserCategoryRels);
}
}
}
Is there a better approach of that what I'm doing in BoMapper, e.g. using converters or something? The difficulty is to map each element of the list and add the userId field too.
ISSUE
This is a complex situation because you are getting userId from other hierarchy and not directly from the List. ModelMapper would map List to List but if you don't configure ModelMapper as LOOSE it will not be able to work.
modelMapper.getConfiguration()
.setMatchingStrategy(MatchingStrategies.LOOSE);
Anyway, In case you configure ModelMapper in that manner (LOOSE mode) it would map the List and put in String property of your Class RecipeUserCategoryRel (in this case for example userCategory if it is a String and considering userId is not a String) the others (I'm not pretty sure) I think it would be null.
SOLUTION
Well, I think the solution to your issue is to create a Converter and add it to your ModelMapper instance:
RecipeBO (Source) -> Recipe (Destination)
The code would be as bellow:
ModelMapper mapper = new ModelMapper();
Converter<RecipeBO, Recipe> converter = new Converter<RecipeBO,
Recipe>() {
#Override
public Recipe convert(MappingContext<RecipeBO, Recipe> context) {
RecipeBO source = context.getSource();
Recipe destination = new Recipe();
List<String> userCategoryValues = source.getUserCategories();
List<RecipeUserCategoryRel> userCategoryToMap = new ArrayList<RecipeUserCategoryRel>();
for(final String userCategory : userCategoryValues){
userCategoryToMap.add(new RecipeUserCategoryRel(userCategory,source.getUserId()));
}
destination.setRecipeUserCategoryRels(userCategoryToMap);
//... Map other properties if you need
return destination;
}
};
//Option 1
mapper.createTypeMap(RecipeBO.class, Recipe.class).setConverter(converter);
//If you add as a converter directly also works (I don't know which one is better,
//choose above option (createTypeMap + setConverter) or the next (addConverter)
//Option 2 -> mapper.addConverter(converter);
I've tested and It works!!
If I had a Recipe as next:
RecipeBO recipe = new RecipeBO();
recipe.setUserId("1");
String values[] = new String[] { "abc", "klm", "xyz", "pqr" };
List<String> list = Arrays.asList(values);
recipe.setUserCategories(list);
And a RecipeBO:
Recipe recipe = new Recipe();
List<RecipeUserCategoryRel> recipes = new ArrayList<>();
recipes.add(new RecipeUserCategoryRel("abc", "1"));
recipes.add(new RecipeUserCategoryRel("klm", "1"));
recipes.add(new RecipeUserCategoryRel("xyz", "1"));
recipes.add(new RecipeUserCategoryRel("pqr", "1"));
recipe.setRecipeUserCategoryRels(recipes);
When I map RecipeBO to Recipe:
Recipe actual = mapper.map(getRecipeBO(), Recipe.class);
I get the next Output:
OUTPUT:
- RecipeUserCategoryRel(userCategory=abc, userId=1)
- RecipeUserCategoryRel(userCategory=klm, userId=1)
- RecipeUserCategoryRel(userCategory=xyz, userId=1)
- RecipeUserCategoryRel(userCategory=pqr, userId=1)