To Remove element from the list
objectA.removeIf(x -> conditions);
eg:
objectA.removeIf(x -> blockedWorkerIds.contains(x));
List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");
List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");
str1.removeIf(x -> str2.contains(x));
str1.forEach(System.out::println);
OUTPUT: A B C
Answer from uma shankar on Stack OverflowTo Remove element from the list
objectA.removeIf(x -> conditions);
eg:
objectA.removeIf(x -> blockedWorkerIds.contains(x));
List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");
List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");
str1.removeIf(x -> str2.contains(x));
str1.forEach(System.out::println);
OUTPUT: A B C
Although the thread is quite old, still thought to provide solution - using Java8.
Make the use of removeIf function. Time complexity is O(n)
producersProcedureActive.removeIf(producer -> producer.getPod().equals(pod));
API reference: removeIf docs
Assumption: producersProcedureActive is a List
NOTE: With this approach you won't be able to get the hold of the deleted item.
You don't need streams for this task, especially if you want to mutate the list of friends in-place:
students.forEach(s -> s.getFriends().removeAll(badKids));
And that's it. This uses the Collection.removeAll method.
Important: for this to work, the list of friends returned by the Student.getFriends() method must be mutable, such as ArrayList.
Despite the conciseness of the above solution, it breaks encapsulation, because we are mutating the list of friends of every student from outside the Student class. To fix this, you'd need to add a method to the Student class:
void removeBadKids(List<String> badKids) {
friends.removeAll(badKids);
}
Thus, the solution would now become:
students.forEach(s -> s.removeBadKids(badKids));
You can do this:
List<Students> good = st.stream()
.peek(s -> s.getFriends().removeAll(bad))
.collect(Collectors.toList());
But using peek this way (should be used to log) is an anti-pattern, you can use map instead.
List<Students> good = st.stream()
.map(s -> {
s.getFriends().removeAll(bad);
return s;
})
.collect(Collectors.toList());
The code that you have right now works perfectly, but is also O(n * m) since removeIf iterates through the List for every Children. One way to improve would be to store every child's personId in a Set<String> and remove every Person from the List<Person> if their personId is contained in the Set:
Set<String> childIds = childrenList.stream()
.map(Children::getPersonId)
.collect(Collectors.toSet());
personList.removeIf(person -> childIds.contains(person.getPersonId()));
Just another way of doing same but without mutating the original list:
Set<String> childIds = childrenList.stream()
.map(Children::getPersonId)
.collect(Collectors.toSet());
personList = personList.stream().filter(person ->
!childIds.contains(person.getPersonId())
).collect(Collectors.toList());
It should have a bit of increased space complexity but you could take advantage of parallelStream here.
The most efficient way is to extract the ID from the path, then attempt to find it in the Set, making each filter execute in constant time, ie O(1) giving an overall O(n), where n is the number of paths:
filePaths.stream()
.filter(p -> acceptedIds.contains(p.getParent().getFileName().toString()))
.collect(Collectors.toList());
If the reverse approach is done, where each acceptedIds is searched for in the path (as in other answers), each filter is O(m*k), where m is the number of acceptedIds and k is the average Path length, giving an overall O(n * m * k), which will perform very poorly for even moderate sizes of collections.
You could write:
filePaths.stream()
.filter(p -> acceptedIds.stream().anyMatch(id -> p.toString().contains(id)))
.collect(toList());
This filters each path such that at least one of the acceptedIds is contained in the string representation of the path. You might want to implement something better than contains here, depending on your use-case (matching the beginning of the filename for example).
anyMatch is an operation that determines if at least one element matches the given predicate.
Note that this answer does not make any assumption about the path to filter out elements. If you can safely say that in each path, the parent directory is named with the id, you should definitely go with @Bohemian answer, for performance reason.
If methods hashCode and equals are properly implemented in class Car, the stream-based solutions may look as follows:
- filter out the values, collect into a new list
// Predicate.not added in Java 11
List<Car> notJava11 = cars1.stream()
.filter(Predicate.not(cars2::contains))
.collect(Collectors.toList());
List<Car> notIn2 = cars1.stream()
.filter(car -> !cars2.contains(car))
.collect(Collectors.toList());
- Use
forEachforcars2(affectingcars1):
cars2.forEach(cars1::remove);
// no need to call cars2.stream().forEach(cars1::remove);
Here the first occurrence of Car instance is removed in cars1
removeIfshould work also
cars1.removeIf(cars2::contains);
If you due to some reason equals/hashCode are not overridden in class Car, the following solution may be offered:
List<Car> notIn2 = cars1
.stream()
.filter(c1 -> cars2
.stream()
.noneMatch(c2 ->
c1.getId() == c2.getId()
&& Objects.equals(c1.getName(), c2.getName())
)
)
.collect(Collectors.toList());
removeIf:
cars1.removeIf(c1 -> cars2
.stream()
.anyMatch(c2 -> c1.getId() == c2.getId()
&& Objects.equals(c1.getName(), c2.getName())
)
);
It can be done simply by using removeAll() method. You have to implement hashcode and equals in Car class.
Then you can cars1.removeAll(cars2);. this statement would leave only c1 in cars1 list.
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 invokeforEach()method on any type of collection without creating a stream, but keep in mind that using a multiline lambda insideforEach()doesn't bring you any advantage over a plainforloop. - it's highly advisable to give your variable clear self-explanatory names. Like
emplDeparmentX,empleOldDB, etc. Nameslist1makes 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
Setofidcontained in thelist2(remainder: the time complexity of the contains check is O(1)); - generate the list of
Employee(denoted asnonCommonIdin the code) from thelist1that have no commonidwithEmployeecontained in thelist2by checking everyidfrom thelist1against theSetobtained at the previous step. - Removing employees with different
idfrom a list separately causes additional performance overhead because each removal has a linear time complexity. A better option will be to useremoveAll(). - apply
removeAll()on the listiddiscard 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
You can use the removeAll method to remove the items of one list from another list.
To obtain the duplicates you can use the retainAll method, though your approach with the set is also good (and probably more efficient)
The collection facility has a convenient method for this purpose:
list1.removeAll(list2);
I think you can do it with stream by using removeIf and some condition :
list.removeIf(h -> h.getTokens().contains("INC") &&
Arrays.stream(h.getTokens().split(";"))
.filter(tag -> tag.contains("INC"))
.map(tag -> tag.substring(tag.length() - 1))
.filter("N"::equals)
.anyMatch(v ->
(flag && !h.getPropertyA().equals(anotherObject.getPropertyAValue())) ||
(!flag && !h.getPropertyB().equals(anotherObject.getPropertyBValue()))));
Or you can separated the condition in a separated method, for example :
static void doAction(List<Hunt> list) {
list.removeIf(MyClass::isCorrect);
}
private static boolean isCorrect(Hunt h) {
return h.getTokens().contains("INC") &&
Arrays.stream(h.getTokens().split(";"))
.filter(tag -> tag.contains("INC"))
.map(tag -> tag.substring(tag.length() - 1))
.filter("N"::equals)
.anyMatch(v ->
(flag && !h.getPropertyA().equals(anotherObject.getPropertyAValue())) ||
(!flag && !h.getPropertyB().equals(anotherObject.getPropertyBValue())));
}
Or as @Tom Hawtin - tackline suggest, you can use :
static void doAction(List<Hunt> list) {
list.removeIf(h -> h.getTokens().contains("INC") &&
Arrays.stream(h.getTokens().split(";"))
.anyMatch(tag -> tag.contains("INC") && tag.endsWith("N")
&& !(flag ? h.getPropertyA().equals(anotherObject.getPropertyAValue())
: h.getPropertyB().equals(anotherObject.getPropertyBValue()))));
}
IMO stream API wont help you in this case! And this style of code is more readable in compared with stream based version. however I think you can make improvement by changing a bit.
Remove temporary variables such as
String[] tags = h.getTokens().split(";");andString value = tag.substring(tag.length() - 1);Combine some conditions with together.
Use
tag.charAt(tag.length() - 1)instead oftag.substring(tag.length() - 1);public static void doAction(List<Hunt> list) { for (ListIterator<Hunt> iter = list.listIterator(); iter.hasNext(); ) { Hunt h = iter.next(); if (h.getTokens().contains("INC")) { for (String tag : h.getTokens().split(";")) { if (tag.contains("INC") && tag.charAt(tag.length() - 1) == 'N') { if (flag) { if (!h.getPropertyA().equals(anotherObject.getPropertyAValue())) { iter.remove(); } } else { if (!h.getPropertyB().equals(anotherObject.getPropertyBValue)) { iter.remove(); } } } } } } }