Manipulation list and removed data from list - java

List<Object1> list1 = userVo.getMstCardTypeVOs();
Object1 object1 = new Object1();
object1.setId(1);
object1.setName("Test");
-- More fields which are not matched with object2
list1.add(object1);
object1 = new Object1();
object1.setId(2);
object1.setName("Test1");
-- More fields which are not matched with object1
list1.add(object1)
List<Object2> list2 = mapMenuProgramRepo.findAll();
Object2 object2 = new Object2();
object2.setId(1);
object2.setName("Test");
list1.add(object2);
object2 = new Object2();
object2.setId(2);
object2.setName("Test1");
list1.add(object2);
What I need to check same id exist in list1 with reference from list2?
I have used below code:
for (Object1 object1 : obj1) {
for (Object2 obj2: obj2) {
if (object2.getId().equals(object1.getId())) {
// removed entry from the list1
// We can removed by using iterator instead of list
}
}
}
What are the better way and optimized way using jdk8 or 7?

// removed entry from the list1
If you need to remove from one of lists than you can use following code
list1.removeIf(val1 -> list2.stream()
.filter(val2 -> val2.getId().equals(val1.getId()).findFirst().orElse(null) != null)

Although using stream like other answers looks cool, imho it may not be optimal, especially when there is a lot of values in list2.
Instead of using stream to verify if a value exists in list2, which gives complexity of O(M*N) (M,N for number of values in list1 and list2), I would recommend doing things like:
Set<> excludeIds = list2.stream().map(Object2::getId).collect(Collectors.toSet());
list1.removeIf(v -> excludeIds.contains(v.getId()));
Easier to understand, and run faster.
With a HashSet, the complexity of the logic reduce to O(M+N)

You can do this easily with JDK8 streams as shown below by using noneMatch() method:
list1.removeIf(value1 ->
list2.stream().anyMatch(value2 ->
(value2.getId().equals(value1.getId))));//equal ids then remove it from list1
If you have to solve the problem without using streams, then you need to use Iterator (look here for example on remove) and then loop through the list1 and list2 and remove the matched elements from list1
Also, I suggest never create any class names (even example classes) with Object1 or Object2, etc.. as it is very confusing for other developers(i.e., it is not easy to understand the code with class names as Object1, Object2).

Related

Remove Objects from a List based on their ID and collect Objects that have been removed to another List

I want to remove from a list of Employee (list1) objects that are not present in another list of Employee (list2) by their id and add removed objects from list1 into another list (list3) using Java 8.
Example :
List<Employee> list1 = Stream.of(
new Employee("100","Boston","Massachusetts"),
new Employee("400","Atlanta","Georgia"),
new Employee("300","pleasanton","California"),
new Employee("200","Decatur","Texas"),
new Employee("500","Cumming","Atlanta"),
new Employee("98","sula","Maine"),
new Employee("156","Duluth","Ohio"))
.collect(Collectors.toList());
From the above list need to remove Employee object based on id of below list.
List<Employee> list2 = Stream.of(
new Employee("100","Boston","Massachusetts"),
new Employee("800","pleasanton","California"),
new Employee("400","Atlanta","Georgia"),
new Employee("10","Decatur","Texas"),
new Employee("500","Cumming","Atlanta"),
new Employee("50","sula","Maine"),
new Employee("156","Duluth","Ohio"))
.collect(Collectors.toList());
Expected Output :
list1 and list3
List<Employee> list1 = Stream.of(
new Employee("100","Boston","Massachusetts"),
new Employee("400","Atlanta","Georgia"),
new Employee("500","Cumming","Atlanta"),
new Employee("156","Duluth","Ohio"))
.collect(Collectors.toList());
List<Employee> list3 = Stream.of(
new Employee("300","pleasanton","California"),
new Employee("200","Decatur","Texas"),
new Employee("98","sula","Maine")
)
.collect(Collectors.toList());
Tried below way but not working as expected
List<Employee> list3 = new ArrayList<>();
if(CollectionUtils.isNotEmpty(list1) && CollectionUtils.isNotEmpty(list2)){
list2.stream().forEachOrdered( l2 -> {
Optional<Employee> nonMatch = list1.stream().filter(l1 -> !l1.getId().equalsIgnoreCase(l2.getId())).findAny();
if(nonMatch.isPresent()){
list3.add(nonMatch.get());
list1.removeIf(l1 -> l1.getId().equalsIgnoreCase(nonMatch.get().getId()));
}
});
}
System.out.println(list1);
System.out.println(list3);
Here come two possible solutions.
This one is short and concise, but does in fact not remove elements from list1 but utilizes a partitioning collector to create the two lists. Think of the partitioning collector as kind of a two-way filter: if your predicate is fulfilled, collect to one list, if it's not, collect to the other list. The predicate in our case actually is "does list2 contain an employee with the same ID as the stream element from list1?". In order to lower the actual overhead, the code prepares a list of IDs from list2 up-front.
final List<String> list2Ids = list2.stream()
.map(Employee::getId)
.collect(Collectors.toList());
Map<Boolean, List<Employee>> partitioned = list1.stream()
.collect(Collectors.partitioningBy(e -> list2Ids.contains(e.getId())));
list1 = partitioned.get(true);
List<Employee> list3 = partitioned.get(false);
If you need to keep list1 - e.g. for memory foot-print reasons - and really have to remove the elements from it, I'd say you will have to stick to the really old-fashioned iterator. The reason for that is that iterators allow you to iterate some collection and remove elements while doing so. The next sample does exactly this. Note, that I prepared a list of IDs of list2 up-front again.
final List<String> list2Ids = list2.stream()
.map(Employee::getId)
.collect(Collectors.toList());
final List<Employee> list3 = new LinkedList<>();
for (Iterator<Employee> iterator = list1.iterator(); iterator.hasNext();) {
Employee next = iterator.next();
if (!list2Ids.contains(next.getId())) {
list3.add(next);
iterator.remove();
}
}
Issues with your current code:
Remember as the rule of thumb: every time you're finding yourself changing something outside the stream using forEach, most likely something is going wrong (*have a look at the API documentation. You can invoke forEach() method on any type of collection without creating a stream, but keep in mind that using a multiline lambda inside forEach() doesn't bring you any advantage over a plain for loop.
it's highly advisable to give your variable clear self-explanatory names. Like emplDeparmentX, empleOldDB, etc. Names list1 makes more difficult to reason about the code.
You can address this problem in the following steps (time complexity of each step is linear):
create a Set of id contained in the list2 (remainder: the time complexity of the contains check is O(1));
generate the list of Employee (denoted as nonCommonId in the code) from the list1 that have no common id with Employee contained in the list2 by checking every id from the list1 against the Set obtained at the previous step.
Removing employees with different id from a list separately causes additional performance overhead because each removal has a linear time complexity. A better option will be to use removeAll().
apply removeAll() on the list id discard all the employee that are present in the list obtained at the previous step.
The overall time complexity O(n + m) (n and m - are numbers of elements in the list1 and list2).
Set<String> idSet = list2.stream()
.map(Employee::id)
.collect(Collectors.toSet());
List<Employee> nonCommonId = list1.stream()
.filter(emp -> !idSet.contains(emp.id()))
.collect(Collectors.toList());
list1.removeAll(new HashSet<>(nonCommonId)); // list `nonCommonId` is wrapped by the HashSet to improve performance, because `removeAll()` relies on the `contains()` checks
// diplaying results
System.out.println("List1:");
list1.forEach(System.out::println);
System.out.println("nonCommonId:");
nonCommonId.forEach(System.out::println);
Output:
List1:
Employee[id=100, city=Boston, state=Massachusetts]
Employee[id=400, city=Atlanta, state=Georgia]
Employee[id=500, city=Cumming, state=Atlanta]
Employee[id=156, city=Duluth, state=Ohio]
nonCommonId:
Employee[id=300, city=pleasanton, state=California]
Employee[id=200, city=Decatur, state=Texas]
Employee[id=98, city=sula, state=Maine]
A link to Online Demo
First get the distinct id list from list2
idList = list2.stream().map(Employee::getID).distinct().collect(Collectors.toList())
Remove the employees having common id from list1 and get list3.
ArrayList<Employee> list3 = list1.stream().filter(e->!idList .contains(e.getID())).collect(Collectors.toList());
list1 = list1.stream().filter(e->idList.contains(e.getID())).collect(Collectors.toList());

Difference between copying a list to another with or without 'new' - JAVA

1) What is the difference between
for (MyObject myObject : sessionBean.firstList()) {
secondList.add(new MyObject(myObject.getSeverity(), myObject.getSummary(), myObject.getDetail()));
}
and
for (MyObject myObject : sessionBean.firstList()) {
secondList.add(facesMessage);
}
considering that i later want to clear sessionBean.firstList but want to retain the values in secondList.
2) Is there a more readable way of doing it in java 8?
You can do it in single statement in a readable way:
secondList.addAll(sessionBean.firstList())
When you use new you will allocate additional memory, otherwise you are mapping the reference of the existing objects. Even if you want to clear the sessionBean.firstList later point of time, if you have a valid reference for that objects in another collection,it will not be collected by garbage collector. So I could not see any valid scenario to compel to use the new operator.
1) The difference is that in the first case the secondList will contain references to new objects with same fields as in the firstList. And in the second case secondList will contain references to the same objects as in the firstList. So if you modify one of your firstList items, the corresponding item in the secondList will be modified too.
2) You can use the following example to create a list with copies:
List<MyObject> secondList = sessionBean.firstList()
.stream()
.map(first -> new MyObject(first.getSeverity(), first.getSummary(), first.getDetail()))
.collect(Collectors.toList());
Or, if you implement a clone method in your MyObject class
public MyObject clone() {
return new MyObject(severity, summary, detail);
}
you can simply write:
List<MyObject> secondList = sessionBean.firstList()
.stream()
.map(MyObject::clone)
.collect(Collectors.toList());
Or if you just want to copy references, use
secondList.addAll(sessionBean.firstList())

any alternative to overriding "equals()" method in java?

I have several very large ArrayLists of objects which i would like to find their Symmetric Differences ( or disjunction). To do so i have decided to use Sets and their "contain()" methods. However, this method uses the equals() method to evaluate said objects.
Problem is, i cannot make any changes in my class. So, i cannot override any method. (my code is just a small part of a very bigger project)
so this leaves me here, is there any other alternative to altering the classes themselves ? or any other way that would not require me to make any changes to my classes ?
I've recently found out about this so I have an alternate solution (only for Java 8):
// Being T the class of the objects in the list
ArrayList<T> list1 = ...;
ArrayList<T> list2 = ...;
// A function to compare two elements
BiFunction<T, T, Boolean> funcEquals = (a,b) -> yourEquals(a,b);
// A function that given a List returns a predicate that states if an element is on that list
Function<List<T>, Predicate<T>> notIn = (s) -> (e) -> s.stream().filter((y) -> funcEquals.apply(e, y)).count() == 0;
// Get the elements in list1 that are not in list2
Stream<String> list1Filtered = list1.stream().filter(notIn.apply(list2));
// Get the elements in list2 that are not in list1
Stream<String> list2Filtered = list2.stream().filter(notIn.apply(list1));
/*
If you have more than two lists, comparisons can be concatenated:
Stream<String> list1Filtered = list1.stream().filter(notIn.apply(list2)).filter(notIn.apply(list3));
Stream<String> list2Filtered = list2.stream().filter(notIn.apply(list1)).filter(notIn.apply(list3));
Stream<String> list3Filtered = list3.stream().filter(notIn.apply(list1)).filter(notIn.apply(list2));
*/
// Add them all together
ArrayList<T> result = new ArrayList<T>();
result.addAll(list1Filtered.collect(Collectors.toList()));
result.addAll(list2Filtered.collect(Collectors.toList()));
It's a little confusing at first, but you don't have to create any more classes.
I ended up using a wrapper class, originally suggested by "Oliver Charlesworth" and other people in the comments.

Less verbose way to remove objects from the same class in an array of multiple classes

Suppose I have an array with the following elements:
List<Object> objects = new ArrayList<>();
objects.add(1);
objects.add("one");
objects.add("two");
objects.add(new Object());
objects.add(2);
Is there a reduced way to remove certain objects of the same category?
For example, if I want to remove only the strings, I know I can do something like this:
for (Iterator<Object> it = objects.iterator(); it.hasNext();){
if(it.next() instanceof String) {
it.remove();
}
}
But is this the minimal way to do it? I guess I can do it with java-8 but I'm not too sure. Thanks!
In Java 8 you can use Collection.removeIf():
objects.removeIf(obj -> obj instanceof String);
It's still O(n), but it's a little more readable.

Get collection of object.getSomething() from this object collection

Let's say I have a Collection<MyObject> and I want to get a new collection of something like Collection<Integer> with myObjects.getIntValues(). Can I make this fast and clear instead of making new Collection and iterate through all the collection members and add MyObject.getIntValue() to the new collection?
List<Int> list = new ArrayList<Int>;
for(MyObject obj : myCollection) {
list.add(obj.getIntValue());
}
Example with Java 8 streams:
final List<Object> objects = Arrays.asList(1, 2, 3, 4);
final List<Integer> integers = objects.stream()
.map(o -> (Integer) o)
.collect(Collectors.toList());
You can use the copy Method of the Collection class if you just want a new collection of the same values as in the original one.
If you want to create a new collection with different values than in your original collection though (MyObject.getIntValue()), then I don't see a workaround for iterating through all your objects and programatically put the value into your new collection.
Be careful with the first variant though, as it only creates a shallow copy (the objects will be the same, only the references will be copied).

Categories