This question already has answers here:
How do I remove an object from an ArrayList in Java?
(9 answers)
Closed 5 years ago.
I have a List object which holds 10 elements, i have to do conditional check and remove some of the elements from the list. I have done as below, but it is not working as expected, any inputs?
List<MyDTO> myList = new ArrayList<MyDTO>(); //myist.size () is 10
I want to check as below:
for(MyDTO res : myList){
if(res.getResult().equals("cancel")){
myList.remove()
}
}
As shown in above code, if res.getResult() is "cancel" i want to remove that particular object from the list(myList). Is it the correct way to remove an element completely from list based on conditional check?
Simply use removeIf on your list, for example if you had list of Integers ranging from 1 to 10 and you wanted to remove Integers larger than 4, you would do:
yourListHere.removeIf(x -> x > 4);
Resulting list would contain: 1, 2, 3 and 4
Read here about removeIf:
https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate-
BONUS
Additional resources if you are unfamiliar with Java 8 features:
Lambdas - https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
Functional Interfaces - https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
Use removeIf:
default boolean removeIf(Predicate<? super E> filter)
Removes all of the elements of this collection that satisfy the given
predicate. Errors or runtime exceptions thrown during iteration or by
the predicate are relayed to the caller.
In your example:
myList.removeIf(res -> res.getResult().equals("cancel"));
As an alternative to removeIf you can use the Java stream solution:
List<MyDTO> newList = myList.stream()
.filter(s -> !s.getResult().equals("cancel"))
.collect(Collectors.toList());
This might be more convenient when the length of the input list and the number of elements to remove is relatively large.
If use Java 8:
myList.removeIf(x -> x.getResult().equals("cancel"));
If older (6,7):
public class MyDTO {
private String result;
(...)
public MyDTO(String result) {
this.result = result;
}
public String getResult() {
return result;
}
(...)
#Override
public int hashCode() {
return 31;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final MyDTO other = (MyDTO) obj;
return !((this.result == null) ? (other.result != null) : !this.result.equals(other.result));
}
(...)
}
then:
MyDTO toRemove = new MyDTO("cancel");
while(myList.contains(toRemove))
myList.remove(toRemove);
or:
List<MyDTO> toRemove = new ArrayList<MyDTO>();
for(MyDTO res : myList){
if(res.getResult().equals("cancel")){
toRemove.add(res);
}
}
myList.removeAll(toRemove);
Without overriding the equals method:
for(int i = 0; i < myList.size() ; i++)
if(myList.get(i).getResult().equals("cancel"))
myList.remove(i);
or:
Iterator<MyDTO> i = myList.iterator();
while (i.hasNext())
if(i.next().getResult().equals("cancel"))
i.remove();
Related
I am facing a situation similar to described below in my project, of which I am unable to implement the code.
I have a POJO Class
public class TranObject {
public String loadId;
public String vDate;
public String dDate;
public String pDate;
public TranObject(String loadId, String vDate, String dDate, String pDate) {
super();
this.loadId = loadId;
this.vDate = vDate;
this.dDate = dDate;
this.pDate = pDate;
}
//Getter and Setters
//toString()
}
Now I have another processor class where I want to implement some comparison between tranload objects that I am receiving through a data service call and collect them into another collection.
The implementation logic is given in the comments below. Please read the below comments
import java.util.Arrays;
import java.util.List;
public class DemoClass {
public static void main(String[] args) {
List<TranObject> listObj = Arrays.asList(
new TranObject("LOAD1", "20180102", "20180202", null),
new TranObject("LOAD2", "20180402", "20180403", null),
new TranObject("LOAD3", "20180102", "20180202", "20190302"),
new TranObject("LOAD4", "20180402", "20180403", null),
new TranObject("LOAD5", "20200202", "20200203", null)
);
/*
IF (obj1, obj3 vdate and dDate are equal)
IF(pDate == null for obj1 or obj3)
THEN obj1 and obj3 are equal/duplicates, and we collect them.
ELSE IF(pDate != null for obj1 and obj3)
IF(pDate is same for obj1 and obj3)
THEN obj1 and obj3 are duplicates, and we collect them.
ELSE
THEN obj1 and obj3 are unique.
*/
}
}
My End result should be a collection like List containing duplicate Tran objects for further update.
I searched internet in order to how to solve it using Lambda API.
-> Tried using groupingBy first with vDate and then dDate, but then I could not compare them for pDate equality.
Can anyone help me solve this issue. A little help will be very helpful for me. I am stuck here
UPDATE:
After some reading I am trying to implement the same by over-riding equals method in POJO class as shown below:
#Override
public boolean equals(Object obj) {
boolean isEqual=false;
if(obj!=null) {
TranObject tran = (TranObject) obj;
isEqual=(this.vDate.equals(tran.getvDate()) && this.dDate.equals(tran.getdDate()));
if(isEqual && this.pDate != null && tran.getpDate()!= null) {
isEqual = (this.pDate.equals(tran.getpDate()));
}
}
return isEqual;
}
Still it's not working as expected... Can anyone please help me why??
The closest to your requirement would be grouping in a nested manner and then filtering the inner Map for varied conditions while being interested only in values eventually.
Stream<Map<String, List<TranObject>>> groupedNestedStream = listObj.stream()
.collect(Collectors.groupingBy(a -> Arrays.asList(a.vDate, a.dDate)
, Collectors.groupingBy(t -> t.pDate == null ? "default" : t.pDate)))
.values().stream();
from these groupings further the conditions for the values (from map) to be eligible are
they all have same pDate in this case the innerMap would have just one entry with the common pDate (m.size() == 1)
one of the values after grouping has exactly one pDate as null (meaning m.containsKey("default") && m.get("default").size() == 1)
List<TranObject> tranObjects = groupedNestedStream
.filter(m -> m.size() == 1 || (m.containsKey("default") && m.get("default").size() == 1))
.flatMap(m -> m.values().stream().flatMap(List::stream))
.collect(Collectors.toList());
Note, the use of "default" string constant to avoid failures(or poor practice) in collecting a Map with null keys or values.
Sounds like TranObject needs an equals and hashCode method.
#Override
public boolean equals(Object obj) {
//check instanceof and self comparison
TranObject other = (TranObject) obj;
if(this.vDate.equals(other.vDate) && this.dDate.equals(other.dDate)) {
//if pDate is not given then consider them duplicate
if(this.pDate == null || other.pDate == null)
return true;
//if pDate are the same then they are duplicate, otherwise they are unique
return this.pDate.equals(other.pDate);
}
return false;
}
//auto generated by Eclipse
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dDate == null) ? 0 : dDate.hashCode());
result = prime * result + ((pDate == null) ? 0 : pDate.hashCode());
result = prime * result + ((vDate == null) ? 0 : vDate.hashCode());
return result;
}
Now that you have an equals method to determine if two TranObjects are considered equal (based on the rules you specified), just collect the elements that occur in the list more than once:
private static List<TranObject> collectDuplicates(List<TranObject> list) {
List<TranObject> result = new ArrayList<TranObject>();
for(TranObject element : list) {
if(Collections.frequency(list, element) > 1)
result.add(element);
}
return result;
}
This will return all elements that have a duplicate.
Note: collectDuplicates does not return a unique list of the elements that are duplicated. Instead, it returns a list of each duplicated element (as required by OP's question).
I have 2 lists. The requirement is to filter out elements in list1 that are not in list2 based on condition.
Class Fighter
{
String name;
String address;
}
List<Fighter> pairs1 = new ArrayList();
pairs1.add(new Fighter("a", "a"));
pairs1.add(new Fighter("b", "a"));
List<Fighter> pairs2 = new ArrayList();
pairs2.add(new Fighter("a", "c"));
pairs2.add(new Fighter("a", "d"));
Set<Fighter> finalValues = new HashSet<>();
finalValues = pairs1.stream().filter(firstList ->
pairs2.stream().noneMatch(secondList ->
firstList.getName().equals(secondList.getName())
&& firstList.getName().equals(secondList.getName()))).collect(Collectors.toSet());
System.out.println(finalValues);
Expected Output : a=a, b=a
Explanation: Elements in list1 that are not in list2
The above code is not giving the expected output. Please let me know how the above stream code can be corrected to get the output
First override the equals and hashcode methods in Fighter class.
#Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Fighter))
return false;
Fighter f = (Fighter) o;
return f.name.equals(name) && f.address.equals(address);
}
#Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + address.hashCode();
return result;
}
Then create a Set from pairs2. Finally use it's contains method to get the set difference. Here's how it looks,
Set<Fighter> pairs2Set = new HashSet<>(pairs2);
Set<Fighter> setDiff = pairs1.stream()
.filter(f -> !pairs2Set.contains(f))
.collect(Collectors.toSet());
You do not use address when matching inside the filter.
List<Fighter> res = pairs1.stream()
.filter(f -> !pairs2.stream()
.anyMatch(s -> f.getName().equals(s.getName())
&& f.getAddress().equals(s.getAddress())))
.collect(Collectors.toList());
Here filter the element of first list if contains in second list based on condition.
Better approach:
Using List's contains method
List<Fighter> res = pairs1.stream()
.filter(e -> !pairs2.contains(e))
.collect(Collectors.toList());
You need to override equals() method in Fighter class. contains() method you will use the equals() method to evaluate if two objects are the same.
#Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Fighter))
return false;
Fighter f = (Fighter) o;
return f.name.equals(name) && f.address.equals(address);
}
But using Set it will be more faster. See similar problem solution using Set
I have something like List<List<UsersDetails>> userList. If I debug to see its value it's giving [[]] i.e., List<UsersDetails> is empty and List<List<UsersDetails>> is also empty. Is there a way to check if List<UsersDetails> is empty without iteration?
I tried userList.sizeOf, userList.empty() functions and userList==null operator but all are giving false.
If you want to check each element in the "outer" list you have to iterate over it somehow. Java 8's streams would hide this from you, though, and provide a slightly cleaner syntax:
boolean allEmpty = userList.stream().allMatch(l -> l == null || l.empty());
There is not. There is:
if (userList.isEmpty() || userList.get(0).isEmpty()) { ... }
But mostly if the notion: "This a list of lists where the list of lists contains 1 list, but that list is empty" is something you should consider as 'empty', you're using the wrong datastructure. You haven't explained what you are modelling with this List<List<UsersDetails>> but perhaps if you elaborate on that, some other data type in java.* or perhaps guava would be far more suitable. For example, maybe a Map<Integer, UsersDetail> is a better match here (mapping a user's ID to their details).
You could create your own List that simply delegates to e.g. ArrayList but prevents null or empty lists from being added:
public class NonEmptyUserList implements List<List<UserDetails>>{
private ArrayList<List<String>> mDelegate = new ArrayList<>();
public void add(int index, List<UserDetails> element) {
if (element == null || element.isEmpty()) {
return;
}
mDelegate.add(index, element);
}
public boolean add(List<UserDetails> element) {
if (element == null || element.isEmpty()) {
return false;
}
return mDelegate.add(e);
}
public List<UserDetails> set(int index, List<UserDetails> element) {
if (element == null || element.isEmpty()) {
return null;
}
return mDelegate.set(index, element);
}
public boolean addAll(Collection<? extends List<UserDetails>> c) {
boolean changed = false;
for (final List<String> list : c) {
changed = changed || add(list);
}
return changed;
}
public boolean addAll(int index, Collection<? extends List<UserDetails>> c) {
boolean changed = false;
int startIndex = index;
for (final List<String> list : c) {
add(startIndex, list);
changed = changed || (list != null) && !list.isEmpty();
startIndex++;
}
return changed;
}
// delegate all other methods required by `List` to mDelegate
}
Using this list you can be sure no null or empty values will be present and thus you can use:
NonEmptyUserList userList = new NonEmptyUserList();
userList.add(null);
userList.add(Collections.emptyList());
userList.isEmpty(); // returns true
List<UserDetails> subList = new ArrayList<>();
subList.add(null);
userList.add(subList);
userList.isEmpty(); // returns false
If you want to handle sub lists with only null elements as empty as well you will need to extend the above implementation. This is however the only solution I can currently imagine that doesn't involve iterating over the list's elements. But I'd not really recommend this solution. I just wrote it down to show you what might be possible.
I personally think the answer provided by #Mureinik using streams is most favorable.
This question already has answers here:
Limit a stream by a predicate
(19 answers)
Closed 6 years ago.
I don't know how to describe the goal I want to achieve with a good title. I have a list of objects where each have a boolean member. I want to filter this list to get at the end the first objects from the list which all have this boolean member set to true.
This is how I would do it without streams, add every object to a new list for which the getter method returns true and stop when the first element returns false.
int i = 0;
while(i < list.size()){
if(!list.get(i).isMemberTrue()){
break;
}
else{
newList.add(list.get(i));
}
i++;
}
Is this somehow possible with a sequential stream?
Maybe something like this would work:
ArrayList<YourClass> newList = new ArrayList<>(
list.stream()
.filter(obj -> obj.isMemberTrue())
.collect(Collectors.toList())
);
remember to import java.util.stream.Collectors if you want to return it as a list.
you can use this code to get list
list.subList(0,list.indexOf(list.stream()
.filter(member->!member::isMemberTrue)
.findFirst()
.orElse(list.size()))
or you can use it as
int endIndex=list.indexOf(list.stream()
.filter(member->!member::isMemberTrue)
.findFirst()
.orElse(list.size());
then
list.sublist(0,endIndex);
In this I am getting new list by making use of List::sublist(..)and then I am usinglambda` to fetch all members before the condition goes false.
After being reviewed by Flown, I updated the answer
Hope this helps! :)
You can achieve the solution with streams. Just remember the predecessor in the list.
public class ListTest
{
#Test
public void test()
{
List<Foo> list = new ArrayList<>();
list.add(new Foo(true));
list.add(new Foo(true));
list.add(new Foo(true));
list.add(new Foo(false));
list.add(new Foo(false));
list.add(new Foo(true));
list.add(new Foo(true));
List<Foo> firstSequence = list.stream().filter(new Predicate<Foo>() {
private boolean inFirstSequence = true;
#Override
public boolean test(Foo foo)
{
// turns into false, if first false value is found
inFirstSequence = inFirstSequence && foo.b;
return inFirstSequence;
}
}).collect(Collectors.toList());
Assert.assertEquals(3, firstSequence.size());
}
private class Foo
{
public Foo(boolean b)
{
super();
this.b = b;
}
boolean b = true;
}
}
Is there any Utility function in Java to convert List<Object[]> to List<Object>
No, there's no method that does this directly for you. You can write a nested for loop or use the flatMap of the stream API as follows:
List<Object> flat = objArrs.stream()
.flatMap(Stream::of)
.collect(Collectors.toList());
You can easily write one yourself:
public static<T> List<T> append (List<T[]> input) {
List<T> res = new ArrayList<T>();
for(T[] subarr : input) {
if(subarr != null) {
int n = subarr.length;
for(int i = 0; i < n; i++) {
res.add(subarr[i]);
}
}
}
return res;
}
The function appends the different arrays and null arrays are ignored, null elements are however not. Thus if the input is [null,[a,b],[null,null],[c,null,d],null]. The output is [a,b,null,null,c,null,d].
No.
Why don't you just write the function yourself? It would probably be faster than asking this question and waiting for an answer.
In Java 8 you can do it with Streams :
List<Object[]> list = ...
List<Object> l = list.stream()
.flatMap(arr -> Stream.of(arr))
.collect(Collectors.toList());
As others have already said, there is no utility and creating one yourself wouldn't be hard, for example using old school for loops:
public List<Object> flatten( List<Object[]> source )
{
// if ( source == null ) return null; // which check you use it up to you
List<Object> result = new ArrayList<Object>();
if ( source == null ) return result; // Personally I like this check
for ( Object[] array: source )
{
if ( array == null ) continue; // skip nulls
for ( Object object: array )
{
result.add(object);
}
}
return result;
}