I am maintaining one ArrayList of objects. And my object structure is Id, name, some other details. I need to remove one the object with some id value say(10) and I don't want to iterate over the list. Is there any solution for this?
Using Java-8 Collection#removeIf
myList.removeIf(obj -> obj.id == 10);
With Java-7 you'll have to use iterator:
for(Iterator<MyType> iterator = myList.iterator(); iterator.hasNext(); ) {
if(iterator.next().id == 10)
iterator.remove();
}
Note that list iteration is necessary in any case. In Java-8 removeIf method it's just performed internally.
Maybe I don't understand the question but why nobody suggested to use override equals and hashcode for that user class?
class MyObject {
final String id;
final String name;
MyObject(String id, String name) {
this.id = id;
this.name = name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return Objects.equals(id, ((MyObject) o).id);
}
#Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
#Override
public String toString() {
return "MyObject{id='" + id + "', name='" + name + "'}";
}
}
in this case you can easy remove any object from list
final ArrayList<MyObject> list = new ArrayList<>();
list.add(new MyObject("id1", "name1"));
list.add(new MyObject("id2", "name2"));
list.add(new MyObject("id3", "name3"));
MyObject removeCandidate = new MyObject("id2", "name2");
list.remove(removeCandidate);
System.out.println(list);
code above prints
[MyObject{id='id1', name='name1'}, MyObject{id='id3', name='name3'}]
If you really do not want to iterate over the list, you could use a stream but I personnally prefer Collection#removeIf like #TagirValeev suggested
myList = myList.stream()
.filter(x -> x.id() != 10)
.collect(Collectors.toList());
It is not possible1 to remove instances of an element from an ArrayList without iterating the list in some way2. The ArrayList is an array under the hood, and you need to examine each element in the array to see whether it matches the criteria for removal. At the fundamental level, that entails a loop ... to iterate over the elements.
Also note that when you remove a single element from an array, all elements with positions after the removed elements need to be moved. On average, that will be half of the array elements.
Now, you can code these operations in ways that avoid you using an explicit for loop, but the iteration will be happening behind the scenes, no matter how you code it.
1 - Not strictly true. Hypothetically, if you had a separate data structure that (for instance) mapped values to the indexes of elements in the ArrayList, then you could remove the elements without iterating. But I can't see how you could manage that data structure efficiently.
2 - Iteration doesn't just mean using an Iterator. For loops, Stream, Collections.removeIf and other solutions all entail iterating the elements of the list under the hood.
You could not do that without iterator, you should you Hashmap for this.
public class ObjectStructure{
private int Id;
private String name;
//and any data field you need
}
generate all setters and getters.
Use this class in
Hashmap<Integer, ObjectStructure> data = new HashMap<>();
you can add and delete data with only key which is Integer.
data.remove(10);
Related
I am maintaining one ArrayList of objects. And my object structure is Id, name, some other details. I need to remove one the object with some id value say(10) and I don't want to iterate over the list. Is there any solution for this?
Using Java-8 Collection#removeIf
myList.removeIf(obj -> obj.id == 10);
With Java-7 you'll have to use iterator:
for(Iterator<MyType> iterator = myList.iterator(); iterator.hasNext(); ) {
if(iterator.next().id == 10)
iterator.remove();
}
Note that list iteration is necessary in any case. In Java-8 removeIf method it's just performed internally.
Maybe I don't understand the question but why nobody suggested to use override equals and hashcode for that user class?
class MyObject {
final String id;
final String name;
MyObject(String id, String name) {
this.id = id;
this.name = name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return Objects.equals(id, ((MyObject) o).id);
}
#Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
#Override
public String toString() {
return "MyObject{id='" + id + "', name='" + name + "'}";
}
}
in this case you can easy remove any object from list
final ArrayList<MyObject> list = new ArrayList<>();
list.add(new MyObject("id1", "name1"));
list.add(new MyObject("id2", "name2"));
list.add(new MyObject("id3", "name3"));
MyObject removeCandidate = new MyObject("id2", "name2");
list.remove(removeCandidate);
System.out.println(list);
code above prints
[MyObject{id='id1', name='name1'}, MyObject{id='id3', name='name3'}]
If you really do not want to iterate over the list, you could use a stream but I personnally prefer Collection#removeIf like #TagirValeev suggested
myList = myList.stream()
.filter(x -> x.id() != 10)
.collect(Collectors.toList());
It is not possible1 to remove instances of an element from an ArrayList without iterating the list in some way2. The ArrayList is an array under the hood, and you need to examine each element in the array to see whether it matches the criteria for removal. At the fundamental level, that entails a loop ... to iterate over the elements.
Also note that when you remove a single element from an array, all elements with positions after the removed elements need to be moved. On average, that will be half of the array elements.
Now, you can code these operations in ways that avoid you using an explicit for loop, but the iteration will be happening behind the scenes, no matter how you code it.
1 - Not strictly true. Hypothetically, if you had a separate data structure that (for instance) mapped values to the indexes of elements in the ArrayList, then you could remove the elements without iterating. But I can't see how you could manage that data structure efficiently.
2 - Iteration doesn't just mean using an Iterator. For loops, Stream, Collections.removeIf and other solutions all entail iterating the elements of the list under the hood.
You could not do that without iterator, you should you Hashmap for this.
public class ObjectStructure{
private int Id;
private String name;
//and any data field you need
}
generate all setters and getters.
Use this class in
Hashmap<Integer, ObjectStructure> data = new HashMap<>();
you can add and delete data with only key which is Integer.
data.remove(10);
I have a POJO class SearchResults, that contains 4 Strings (title, number, date, status) and then all the getter and setter methods for it.
In another class I populate an ArrayList<SearchResults> results, is there a way I can go through that list results and erase any elements that have a duplicate number?
I've tried populating a new ArrayList by first passing results into a LinkedHashSet but that didn't work.
ArrayList<SearchResults> noDup;
noDup = new ArrayList<SearchResults>(new LinkedHashSet<SearchResults>(results));
I've also tried doing a .remove(indexof()) but that didn't work either.
if(noDup.contains(new SearchResults("-1","","",""))){noDup.remove(noDup.indexOf(new SearchResults("-1","","","")));}
Any suggestions?
Edit:
The equals() method in SearchResults (wonr refers to the number)
#Override
public boolean equals(Object object){
if(object == null){
return false;
}
if(getClass() != object.getClass()){
return false;
}
SearchResults result = (SearchResults) object;
if((this.wonr == null) ? (result.wonr == null): this.wonr.equals(result.wonr)){
return false;
}
return true;
}
The suggestions for implementing hashCode and equals are possible options, but does this single number value truly define what it means for these objects to be equivalent in the general case? If not, defining equals and hashCode that way seems to be a hack.
Without altering the definition of equivalence, if in just this case you want to elminiate values with the same number value, there are other approaches you can try. You didn't give us the API for your SearchResult class, so I'll assume there's an accessible field named number.
One quick way is to use a TreeSet which defines its idea of equivalence based on an underlying comparison operation. Write a custom Comparator that only looks at the number field and you're good to go:
Java 8
List<SearchResult> allResultsWithDuplicates = // ... populated list
Comparator<SearchResult> comparator =
(left, right) -> Integer.compare(left.number, right.number);
Set<SearchResult> uniqueNumbers = new TreeSet<>(comparator);
uniqueNumbers.addAll(allResultsWithDuplicates);
As JB Nizet mentioned, if your SearchResult class has a getNumber accessor method you can use a function reference and eliminate the lambda expression defining Comparator:
Comparator<SearchReult> comparator = Comparator.comparing(SearchResult::getNumber);
Java 5-7
In earlier versions of Java you must implement the Comparator class yourself. Then it plugs into the code given above in exactly the same way. This example assumes there is a int getNumber() accessor method on your SearchResult class:
Comparator<SearchResult> comparator =
new Comparator<SearchResult>() {
#Override
public int compare(SearchResult sr1, SearchResult sr2) {
// Optional support for null arguments is left as
// an exercise for the reader.
return Integer.compare(sr1.getNumber(), sr2.getNumber());
}
};
Another way you can do it with Java-8 is this way:
1) Create set of unique numbers,
2) Iterate over your list and filter by this set:
Set<Integer> numbers = new HashSet<>();
List<SearchResult> noDups = listWithDups.stream()
.filter(sr -> numbers.add(sr.getNumber()))
.collect(Collectors.toList());
If you implemented equals() and hashCode() so that they just look at Number property you could build a Set<SearchResult> instead of a ArrayList<SearchResult> and you will implicitly get no duplicates (this is one of the properties of sets - they don't contain duplicates). You can still iterate over the entries in the set so you should have all the functionality you need.
Do a stream of our list and use filter method and collect to an other list.
Is there a direct way to find out if the list contains duplicates?
Direct API method in some third party utils?
And if the list contains duplicates how many such duplicate elements exist in the list?
Code we can write but I want to know if any direct API exists?
You can count occurrences with
List<T> list =
Map<T, Integer> count = new HashMap<T,Integer>();
for(T t: list) {
Integer i = count.get(t);
if (i == null) i = 0;
count.put(t, i + 1);
}
No you'll have to do it by yourself. But you can use objects that won't allow to insert duplicate data (here)
It looks like you want something like this:
public int getNumOfElementInList(List<Object> myList, Object myElement){
int count = 0;
for(Object element: myList){
if(element.equals(myElement)) //or use instanceof instead, depending
count++;
}
return count;
}
This will give you the number of an element in a list. Alternatively, you could make a List instead of using count, and add the duplicate elements to the List, and return that.
Such as:
public List<DuplicateStats> getTotalNumOfElementInList(List<Object> myList){
List<DuplicateStats> dups = new ArrayList<DuplicateStats>();
int i;
for(Object element: myList){
if((i = dups.indexOf(element) != -1)
dups.get(i).addOne();
else
List.add(new DuplicateStats(element));
}
return count;
}
public class DuplicateStats {
private Object element;
private int count;
public DuplicateStats(Object o){
element = o;
}
public boolean equals(String compare){
return element.toString.equals(compare);
}
public void addOne(){
count++;
}
}
You can add getters, setters, etc. to the class DuplicateStats, but it will keep track of duplicates for you.
If you want an API you can find duplicates with Guava's Multiset.
Just add your list do the set and use the count method.
If you want to find out how many duplicates there are you could keep the list with duplicates, together with a set without duplicates. The number of duplicates is then just the size of the list minus the size of the set.
There are no built-in methods to do this. However you can use LinkedHashSet for example to solve this problem. It does not allow duplicates (as it acts like a set) but it preserves an order of elements (as it acts like a list). You can iterate over all of elements from your list and add them to LinkedHashSet, checking if add method returns true or false.
I have an ArrayList of custom objects. I want to remove duplicate entries.
The objects have three fields: title, subtitle, and id. If a subtitle occurs multiple times, I only need the first item with thats subtitle (ignore the remaining object with that subtitle).
You can put the content of the ArrayList in a TreeSet using a custom Comparator which should return 0 if the two subtitles are the same.
After that you can convert the Set in a List and have the List without "duplicates".
Here is an example for Object, of course you should use the correct class and logic.
public void removeDuplicates(List<Object> l) {
// ... the list is already populated
Set<Object> s = new TreeSet<Object>(new Comparator<Object>() {
#Override
public int compare(Object o1, Object o2) {
// ... compare the two object according to your requirements
return 0;
}
});
s.addAll(l);
List<Object> res = Arrays.asList(s.toArray());
}
List list = (...);
//list may contain duplicates.
//remove duplicates if any
Set setItems = new LinkedHashSet(list);
list.clear();
list.addAll(setItems);
You may need to override "equals()" so that 2 elements are considered equals if they have the same subtitle (or tite and subtitle maybe ?)
List<Item> result = new ArrayList<Item>();
Set<String> titles = new HashSet<String>();
for(Item item : originalList) {
if(titles.add(item.getTitle()) {
result.add(item);
}
}
add() of the Set returns false if the element already exists.
I would suggest using a Set
http://download.oracle.com/javase/6/docs/api/java/util/Set.html
Which by its nature cannot contain duplicate items. You can create a new set from your original ArrayList using
Set myset = new HashSet(myArrayList);
Alternatively, just use a Set from the start, and don't use an ArrayList as it is not performing the function that you require.
If I understand correctly you have an ArrayList<Custom>, let's call it list. Your Custom class has a subtitle field, let's say with a getSubtitle() method that returns String. You want to keep only the first unique subtitle and remove any remaining duplicates. Here's how you can do that:
Set<String> subtitles = new HashSet<String>();
for (Iterator<Custom> it = list.iterator(); it.hasNext(); ) {
if (!subtitles.add(it.next().getSubtitle())) {
it.remove();
}
}
You can use an O(n^2) solution: Use list.iterator() to iterate the list once, and on each iteration, iterate it again to check if there are duplicates. If there are - call iterator.remove(). A variation of this is to use guava's Iterables.filter(list, predicate) where your filtering logic is in the predicate.
Another way (perhaps better) would be to define the equals(..) and hashCode(..) methods to handle your custom equality logic, and then simply construct a new HashSet(list). This will clear duplicates.
Removes any duplicates in a collection, while preserving the order if it is an ordered collection. Efficient enough for most cases.
public static <I, T extends Collection<I>> T removeDuplicates(T collection)
{
Set<I> setItems = new LinkedHashSet<I>(collection);
collection.clear();
collection.addAll(setItems);
return collection;
}
Update for Java8:
Using Java8 streams you can also do pretty trivally.
ArrayList<String> deduped;
deduped = yourArrayList.stream()
.distinct()
.collect(Collectors.toCollection(ArrayList::new));
This also has the advantage over going ArrayList → Set → ArrayList of maintaining ordering.
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) continue;
previous = elem;
[... process unique element ...]
}
This presumes that you'll implement Comparable in your type A.
private static List<Integer> removeDuplicates(List<Integer> list) {
ArrayList<Integer> uniqueList = new ArrayList<Integer>();
for (Integer i : list) {
if (!inArray(i, uniqueList)) {
uniqueList.add(i);
}
}
return uniqueList;
}
private static boolean inArray(Integer i, List<Integer> list) {
for (Integer integer : list) {
if (integer == i) {
return true;
}
}
return false;
}
The solution depends on circumstances.
If you don't have much data then go with a Set Set<T> unique = new HashSet<>(yourList); (use LinkedHashSet if you care about the order. It creates a new collection, but usually it's not a problem.
When you want to modify existing list and don't want to/can't create a new collection, you can remove duplicates like here:
List<Integer> numbers =
new ArrayList<>(asList(1, 1, 2, 1, 2, 3, 5));
System.out.println("Numbers: " + numbers);
ListIterator<Integer> it = numbers.listIterator();
while (it.hasNext()) {
int i = it.nextIndex();
Integer current = it.next();
for (int j = 0; j < i; ++j) {
if (current.equals(numbers.get(j))) {
it.remove();
break;
}
}
}
System.out.println("Unique: " + numbers);
It works in O(n^2), but it works. Similar implementation, but simpler, is when the list is sorted - works in O(n) time. Both implementations are explained at Farenda: remove duplicates from list - various implementations.
In Java 8, you can also do something like this:
yourList.stream().collect(
Collectors.toMap(
obj -> obj.getSubtitle(),
Function.identity(),
(o1,o2) -> o1))
.values();
The trick is to collect stream to map and provide key collision resolver lambda ((o1,o2) -> o1) which always returns its first parameter.
The result is a Collection, not a List but you can easily convert it to a List:
new ArrayList(resultCollection);
List<YourObject> all = ******** // this is the object that you have already and filled it.
List<YourObject> noRepeat= new ArrayList<YourObject>();
for (YourObject al: all) {
boolean isPresent = false;
// check if the current objects subtitle already exists in noRepeat
for (YourObject nr : noRepeat) {
if (nr.getName().equals(al.getName()) {
isFound = true;//yes we have already
break;
}
}
if (!isPresent)
noRepeat.add(al); // we are adding if we don't have already
}
take one new ArrayList Object of same type
one by one add all the old arraylists elements into this new arraylist object
but before adding every object check in the new arraylist that if there is any object with the same subtitle.if new arraylist contains such subtitle don't add it. otherwise add it
Another method using Java 8 streams you can also do pretty cool:
List<Customer> CustomerLists;
List<Customer> unique = CustomerLists.stream().collect(collectingAndThen(
toCollection(() -> new TreeSet<>(comparingLong(Customer::getId))),
ArrayList::new));
I am making this class which is a custom Map based off a hash map. I have an add method where if you add an object the object will be the key, and its value will be 1 if the object is not currently in the list. However if you add object that is currently in the list its value will be bumped up by 1. So if I added 10 strings which were all the same, the key would be that string and the value will be 10. I understand in practice when I iterate through the map, there is actually only one object to iterate, however, I am trying to create a inner class that will define an iterator that will iterate the same object however many times its value is. I can do this by simply using for loops to construct an appropriate ArrayList and just create an iterator for that, but that is too inefficient. Is there an easy or more efficient way of doing this?
You can do it with two variables:
private T nextObj = null;
private int times = 0;
T next(){
if(times==0){
// get the next object and set the times variable to it's value in the hashmap
}
times--;
return nextObj;
}
You could use nCopies from the collections API. This will create a list with only one reference so it will be efficient. Then just return that Lists iterator. No need to create an inner class.
Assuming your Map<String, Integer> instance variable is called map, you could do the following:
Iterator<String> customIteratorForKey(String key) {
return Collections.nCopies(map.get(key), key).iterator();
}
It sounds like you're implementing a multiset or bag: a set that counts each unique element. Since this is a school project, I'll give some pointers on how to do it instead of providing code. Try your luck with it and refine your question if you get stuck.
When I create a new collection type that uses another collection behind it, I typically do the same thing when building the iterator.
Bag.Iterator's constructor would initialize itself with an iterator from the Map.
As dtech showed above, the iterator needs to track the current object it is counting and the number of times it should return it.
next() needs to get the next object at the start and once the current object runs out of counts.
hasNext() must do the same thing without actually decreasing the count or grabbing the next object.
I finally figured it out. Here is my solution. Thanks to everyone who responded and gave me pointers.
private int times = 0;
private boolean flag = true;
Iterator<Entry<T, Integer>> it = Bag.entrySet().iterator();
private Entry<T, Integer> t = it.next();
private int value = t.getValue();
private T nextObj = t.getKey();
public boolean hasNext() {
if (times > 0) {
return true;
}
return it.hasNext();
}
public T next() {
if (this.hasNext() == false) {
throw new NoSuchElementException();
}
if (times == 0 && flag == true) {
times = value;
flag = false;
}
if (times == 0 && flag == false) {
t = it.next();
value = t.getValue();
nextObj = t.getKey();
times = value;
}
times--;
return nextObj;
}