I'm new to Java and i'm stuck on something. Sorry for the long post and messy newbie programming.
I've searched over this forum and tried multiple soltuions to my problem. I'll try to explain: I have an object "Ticket" with multiple attributes. I have a list of Tickets.
List<Ticket> ticketList = new ArrayList<>();
I have another list, also populated with Tickets, that i mean to take them as a source to erase Tickets on the 1st list. But the only attribute that the Tickets on the second list have is the ticket's number - the other ones are null. Meaning - I need to erase tickets from the first list based on second list's ticket's number.
Here's what i've tried:
Gather all data in just one list, find the duplicates, and remove the duplicates:
List<Ticket> ticketList = new ArrayList<>();
Set<String> s = new HashSet<>();
for (Ticket t : ticketList) {
if (s.add(t.getNumber()) == false) {
System.out.println(t.getNumber() " is duplicated");
ticketList.remove(t)
Outcome: Unchanged list.
Run the two lists with for and use lambda expression: (Consider finalList the one with the tickets to be removed from ticketList)
for (Ticket t : finalList) {
for (Ticket u: ticketList) {
ticketList.removeIf(lt -> t.getNumber().equals(u.getNumber()));
}
}
Outcome: It successfully identifies the tickets on both lists by their number, but erase the whole list.
Run the two lists and identify the tickets by index, and removing them afterwards:
List<Integer> positions = new ArrayList<>();
for (Ticket t : finalList) {
for (Ticket a : ticketList) {
if (a.getNumber().equals(t.getNumber())) {
positions.add(ticketList.indexOf(a));
for (Integer z : positions) {
System.out.println("Removing index of " z " from ticketList");
int i = Integer.valueOf(z);
ticketList.remove(i);
}
Outcome: It erases the first entry successfully, but then the indexes get shuffled and i get IndexOutOfBounds error.
Does anyone have a light on this?
CodePudding user response:
You should avoid doing structural changes to a list while looping through it.
The following code removes all elements in firstList whose "number" property matches the number property of any element in the second list.
List<Ticket> firstList = ...;
List<Ticket> secondList = ...;
firstList.removeIf(
ticket -> secondList.stream().anyMatch(
ticket2 -> ticket2.getNumber().equals(ticket)
)
);
Or somewhat more readable
for(Ticket t : secondList){
firstList.removeIf(t -> t.getNumber().equals(t.getNumber));
}
CodePudding user response:
You haven't posted the code for the Ticket
class, but my guess is that you missed overriding the equals
and hashCode
methods. That is especially important when working with collections.
If the ticket number can be a unique identifier, you can define the equals and hashCode like this at your Ticket
class:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Ticket ticket = (Ticket) o;
return number != null ? number.equals(ticket.number) : ticket.number == null;
}
@Override
public int hashCode() {
return number != null ? number.hashCode() : 0;
}
Having this in place, just add all the elements to a Set make the trick, the Set removes the duplicates:
Set<Ticket> uniqueTickets = new HashSet<>(ticketList);
Or you can use the method removeAll
to remove all the elements present in the other list:
ticketList.removeAll(ticketsToRemoveList);
If you can't define the equals and hashCode, you can iterate the list of the tickets to remove and remove them from the list:
for (Ticket ticketToRemove : ticketsToRemove) {
ticketList.removeIf(ticket -> ticket.getNumber().equals(ticketToRemove.getNumber()));
}