I am using Spring-WS to populate a List<SomeBean> w.r.t. a web-service. The final list that I get after consuming the service contains null entries for some of its properties. For example, consider the following list of MangaBean which has title, author and genre as properties.
MangaBean [title=Bleach, author=Kubo Tite, genre=fantasy]
MangaBean [title=Naruto, author=Masashi Kishimoto, genre=action]
MangaBean [title=One Piece, author=Eiichiro Oda, genre=null]
MangaBean [title=Fairy Tail, author=Mashima Hiro, genre=null]
MangaBean [title=Rurouni Kenshin, author=Watsuki Nobuhiro, genre=Shounen]
MangaBean [title=Takamagahara, author=KAWAI Juuzou, genre=Supernatural]
MangaBean [title=Historys Strongest Disciple Kenichi, author=Matsuena Syun, genre=Martial arts]
MangaBean [title=Hajime no Ippo, author=Jyoji Morikawa, genre=null]
The genre entry for some beans are null. I am trying to remove the objects with null properties in the list.
My questions are:
Is it proper/good practice to alter the original list by removing specific entries? Or should I populate another list w.r.t. the previous list?
If I should alter the list and remove those null entries, how should I do it?
I tried this but it fails as the object-refs themselves aren't null. I tried the traditional for loop but it fails to remove adjucent objects with null properties. Cannot use the enhanced-for-loop for removing items while accessing the list. Kindly render me some brain. :)
You can use an Iterator and remove the one having a null genre manually:
for (Iterator<MangaBean> it = mangas.iterator(); it.hasNext();) {
if (it.next().getGenre() == null) {
it.remove();
}
}
Or you could filter the list using some common libraries like Apache Commons CollectionUtils, or Guava Iterables.filter():
// apache commons
mangas = CollectionUtils.filter(mangas, new Predicate() {
#Override public boolean evaluate(Object object) {
return ((Manga) manga).getGenre() != null;
}
});
// Guava
Iterables.filter(mangas, new Predicate<Manga>() {
#Override public boolean apply(Manga m) {
return manga.getGenre() != null;
}
});
There is nothing about removing from a List that is bad practice, unless you cannot trust the source of the List to give you a mutable reference.
If it is mutable (not read-only), then you can use an Iterator:
List<MangaBean> list = ...;
Iterator<MangaBean> iterator = list.iterator();
while (iterator.hasNext())
{
MangaBean bean = iterator.next();
if ( ! isValidMangaBean(bean))
{
iterator.remove();
}
}
The limitation presents itself with removing from the List if it is a read-only List. By creating a copy that only contains the non-null elements, you can guarantee that you will not hit any roadblocks, but using the above Iterator will work if the List is not read-only.
List<MangaBean> list = ...;
List<MangaBean> cleaned = new ArrayList<MangaBean>();
for (MangaBean bean : list)
{
if (isValidMangaBean(bean))
{
cleaned.add(bean);
}
}
Reference function that could be expanded upon:
boolean isValidMangaBean(MangaBean bean)
{
return bean.getGenre() != null;
}
If you continuously run into the above situation, then consider writing a Filter interface that would allow you to perform the above filtering much more generically.
Related
I have a hashmap with key and object like
HashMap<String,List<Object,> > profileMap= new HashMap<>();
ArrayList eventList = new ArrayList();
for(Profile profile:Plist) {
> profileMap.putIfAbsent(profile.getprofileID(),eventList );
cpToEvent.get(event.getContact_profile()).add(event);
}
Profile object contains information about different events, event date, and profileID associated with that event.
I need to delete the events of the profile where the gap between two events in a profile is more than 1 yrs.
For that, I need to sort the list so that I can calculate the gap between them before deleting them.
How do achieve this?
If you are trying to have the elements in your List sorted, I recommend using a natively existing type such as a "SortedSet" implementation. E.g. a TreeSet
Map<String, TreeSet<Object>> profileMap = new HashMap<>();
This will have you implementing the Comparator Interface in which you can define to sort by Date.
public class Objekt implements Comparator<Objekt> {
#Override
public int compare(Objekt o1, Objekt o2) {
if (o1.getDate().before(o2.getDate())) {
return -1;
} else if (o1.getDate().after(o2.getDate())) {
return 1;
} else {
return 0;
}
}
More on how to implement that here: Compare Object by dates ( implements Comparator)
You can try to iterate over the HashMap item and filter the element with Date that is older than 1 year.
Given the Profile class as below
public class Profile {
private Date createdAt;
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
}
And our List is
HashMap<String, Profile> profiles = new HashMap<>();
Then we can simply do as below to get the list of Map.Entry that matches your requirement.
List<Map.Entry<String, Profile>> matchProfile = profiles.entrySet().stream().filter(item -> item.getValue().getCreatedAt().getYear() > 2015)
.collect(Collectors.toList());
There are several constraints you should have in mind, mostly regarding modifying your existing objects.
The simplest code that processes your items is this:
Map<String, List<Profile>> profileMap= ...;
profileMap.forEach((k, v) -> {
v.sort(Comparator.comparing(Profile::getDate));
// additional processing on "v" here (v is the value in the Map.Entry, i.e. the list of profiles)
});
But the code above modifies the List which exists in your map.
If you need to preserve the existing List as-is, then instead of sorting v, you should create a new List and then process that.
profileMap.entrySet().forEach(e -> {
List<Profile> profiles = new ArrayList<>(e.getValue());
profiles.sort(Comparator.comparing(Profile::getDate));
e.setValue(profiles);
});
The code above modifies the profileMap, it now maps the original keys to new values.
Again, if that is not ok, and you want to preserve the original profileMap entirely, then in the forEach above you need to fill a new Map instead of setValue-ing the existing entries.
Make sure to focus on solving or improving the overall product, not just on a small piece of the processing. Sometimes, the best way to improving a process is to eliminate some parts of it entirely and adjust the remaining pieces.
Why do you have a huge list of events that is both unsorted and containing obsolete entries? Can you sort the events when receiving them? Or when reading them from the database?
class ParentItem {
String itemName; //I want to get this property for all the objects in hierarchy
Integer itemCode;
List<ParentItem> childItem;
}
I want to get the names of the All Items(ParentItem name, ChildItem name, GrandChildItemName) using streams, how to achieve this? Assume the ChildItem also has a Child which means the ParentItem has a GrandChild! So there are 3 levels of Nesting. How to achieve this?
Try the following approach to recursively flatmap the child streams:
Stream<ParentItem> flatMapChildren(ParentItem item ) {
return Stream.concat( //flatMap replaces the item in the stream so we need concat() to keep it
Stream.of(item), //create a 1-element stream for the item that gets replaced
item.childItem.stream() //create a stream for the children
.flatMap(YourClass::flatMapChildren) //recursively add their children
);
}
Then use that on your top level stream:
List<ParentItem> topLevel = ....;
Stream<String> streamOfAllNames =
topLevel.flatMap(YourClass::flatMapChildren)
.map(ParentItem::getName);
Note: the implementation doesn't contain null checks etc. for simplicity reasons. Add those in your actual code.
public static void main(String[] args) {
ParentItem grandpa = new ParentItem();
List<String> listOfItemNames = Stream.of(grandpa)
.flatMap(Main::loadChildRecursively)
.map(ParentItem::getItemName)
.collect(Collectors.toList());
listOfItemNames.forEach(System.out::println);
}
private static Stream<ParentItem> loadChildRecursively(ParentItem parent) {
if (parent.getChildItem() != null && parent.getChildItem().size() > 0) {
return Stream.concat(parent.getChildItem().stream().flatMap(Main::loadChildRecursively), Stream.of(parent));
} else {
return Stream.of(parent);
}
}
An important thing to keep in mind is, when calling the recursive function always add / include your parent object, otherwise you'll end up only having the lowest level of children and no parents.
I am trying to filter out a list based on values. I have two List. One is a list of names which i want to remove i.e present in animalList. And another is the main primary list AnimalPrimaryDataPojoFilterList from where i have to remove the object which matches the names from animalList. Now i do have the solution but i think it takes lot of time. Below is the code. I am using Java 8. Can it be optimised?
if(animalList!=null && animalList.size()>0)
{
for(AnimalFilterPojo dtoObject:animalList)
{
if(!dtoObject.getApproved())
{
for(AnimalPrimaryDataPojo mainDtoObject: AnimalPrimaryDataPojoFilterList)
{
if(mainDtoObject.getAnimalName().equalsIgnoreCase(dtoObject.getValue()))
{
AnimalPrimaryDataPojoFilterList.remove(mainDtoObject);
}
}
}
}
Use removeAll() method.
AnimalPrimaryDataPojoFilterList.removeAll(animalList);
It will remove the objects of animalList from AnimalPrimaryDataPojoFilterList
N.B: You need to implement hashCode() and equals() method in AnimalFilterPojo
You can use Java 8 streams to filter the list. In below example Parent is the object which has abc property of type String. We are filtering List<Parent> objs using List<String> names
public class ListFilterDemo {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
List<Parent> objs = new ArrayList<>();
List<Parent> filtersObjs = objs.parallelStream().filter((obj) -> names.contains(obj.getAbc())).collect(Collectors.toList());
}
}
class Parent {
private String abc;
public Parent(String abc) {
this.abc = abc;
}
public String getAbc() {
return this.abc;
}
}
You can try this:
if(animalList!=null && animalList.size()>0)
animalList.removeIf(animal ->
AnimalPrimaryDataPojoFilterList.stream()
.filter(filter -> !filter.getApproved())
.map(AnimalFilter::getValue)
.collect(Collectors.toList()).contains(animal.getAnimalName()));
to explain the code: here we use removeIf() on the List to remove the objects using a Predicate that is a lambda that receives the animal and filters the list by removing the elements by name where name is taken from a list generated as a selection of the AnimalPrimaryDataPojoFilterList of the elments that have the approved flag (the second filter), extracting the value (using the map) and constructing a list out of it using a Collector.
The portion:
AnimalPrimaryDataPojoFilterList.stream()
.filter(filter -> !filter.getApproved())
.map(AnimalFilter::getValue)
.collect(Collectors.toList())
generates the list to be used as a filter
animalList.removeIf(animal ->
<generated list>.contains(animal.getAnimalName()));
uses list generated in place to apply the filter.
Beware that this of course modifies the list you have
Besides, you should not start a variable with a capital letter like you did for AnimalPrimaryDataPojoFilterList.
you can use removeIf then use AnimalPrimaryDataPojoFilterList as the source in which case you'll need to invert your logic within the if block i.e:
if(animalList != null && animalList.size() > 0){
AnimalPrimaryDataPojoFilterList.removeIf(x ->
animalList.parallelStream()
.filter(e -> !e.getApproved() && x.getAnimalName().equalsIgnoreCase(e.getValue())).findAny().orElse(null) != null);
}
I am using myBatis 3.2.x and have run into a scenario where I need to do multiple table inserts in one database trip,
I was wondering if I can create a master INSERT sql mapper file which would call these multi table inserts and save me network trips
I am consuming JSON objects from a EMS server and my Turn around time is a bit higher then required.
All suggestions and hints are welcome.
Thanks
VR
Use Collections.sort() to sort and use a simple for cycle to catch doubles, e.g.:
Collections.sort(myList);
A previous = null;
for (A elem: myList) {
if (elem.compareTo(previous) == 0) {
System.err.println("Duplicate: "+elem);
}
previous = elem;
}
Assuming that the Comparable is consistent with the equals implementation, you can use a Set. You can add each element to a Set using Set.add(..) and use the return value of add to determine if the value was already present in the Set and create either a Set or a List to return.
Note: If you need each duplicate returned only once, you can change the return list to a set.
List<A> duplicates(List<A> myList) {
Set<A> s = new HashSet<A>();
List<A> duplicates = new ArrayList<A>(); // change to using a Set if you want to report each duplicate item only once.
for (A item: myList) {
if (!s.add(item)) {
duplicates.add(item);
}
}
return duplicates;
}
An improved version using sorting (to report duplicate elements only once, I assume there are no null values in the list):
Collections.sort(myList);
A previous = null, elem = null;
for (java.util.Iterator<A> it = myList.iterator; it.hasNext(); elem = it.next()) {
if (elem.compareTo(previous) == 0) {
System.err.println("Duplicate: "+elem);
while (it.hasNext() && (elem = it.next()).compareTo(previous)) {
//loop through other values
}
}
previous = elem;
}
A version using SortedSet (probably this is faster a bit): and corrected the same
SortedSet<A> set = new TreeSet<>(), duplicates = new TreeSet<>();
for (A a: myList) {
if (!set.add(a)) {
duplicates.add(a);
}
}
return duplicates;//or for (A a: duplicates) System.println("Duplicate: " + a);
I have the following code:
Widget[] widgetArray = widgetService.getAllWidgets();
List<Widget> widgets = Arrays.asList(widgetArray);
// Prune out any Widgets named "Melvin".
Iterator<Widget> iter = widgets.iterator();
while(iter.hasNext()) {
Widget w = iter.next();
if("Melvin".equals(w.getName()))
iter.remove();
}
When I run this code I get a runtime java.lang.UnsupportedOperationExceptionError with a vague exception message of null that gets thrown on the iter.remove() line. It seems that some Java Iterators don't support the remove method and will throw this exception.
I can't change the widgetService.getAllWidgets() method to return a List<Widget> and am stuck with the Widget[] array return value.
So I ask: what can I do to loop through my widgets array and dynamically prune out ones that are named "Melvin"?
If you can afford it, just make a mutable copy of the list. Replace
List<Widget> widgets = Arrays.asList(widgetArray);
with
List<Widget> widgets = new ArrayList<Widget>(Arrays.asList(widgetArray));
Just defer removal until the iterator is done:
Widget[] widgetArray = widgetService.getAllWidgets();
List<Widget> widgets = Arrays.asList(widgetArray);
// Prune out any Widgets named "Melvin".
List<Widget> toRemove = new ArrayList<Widget>();
Iterator<Widget> iter = widgets.iterator();
while(iter.hasNext()) {
Widget w = iter.next();
if("Melvin".equals(w.getName()))
toRemove.add(w);
}
widgets.removeAll(toRemove);
Alternatively, just build the list from eligible widgets using the inverse logic:
List<Widget> widgets = new ArrayList<Widget>();
// Add all Widgets not named "Melvin"
for (Widget w : widgetService.getAllWidgets()) {
if(!"Melvin".equals(w.getName()))
widgets.add(w);
}
The asList() list is still backed by the array.
You may want to loop through each element of the array and add it to a brand new list. This would take two loops.
Or better yet, compare the string value and then add to the list. This way, you have one loop and a brand new list.
With guava you can get rid of all that code and let a fast, well tested library take care of it for you.
Collection<Widget> noMelvins = Collections2.filter( Arrays.asList(widgetArray), new Predicate<Widget>() {
#Override public boolean apply( final Widget arg) {
return !"Melvin".equals(arg.getName());
}
});