Remove all repeated list members - java

I have an Array full of Objects, and if two Objects are the same, I want to delete both of them.
Here is my current approach, which returns a
java.util.ConcurrentModificationException
public void deleteDuplicates(ArrayList<Object> objectArrayList){
Iterator<Object> objectIterator = objectArrayList.iterator();
Iterator<Object> objectIterator2 = objectArrayList.iterator();
while(objectIterator.hasNext()){
Object object = objectIterator.next();
while(objectIterator2.hasNext()){
if(object.equals(objectIterator2.next())){
objectIterator2.remove();
objectIterator.remove();
}
}
}
}

This code will remove all list members that are present more than once.
This approach handles an odd number of repeats. So it does not detect and remove repeat pairs, because that could leave an orphan repeat. Instead, it counts first, and then removes repeats.
public static void deleteDuplicates( List<Object> objectList) {
HashMap<Object,Integer> counts = new HashMap<>();
for ( Object o : objectList ) {
int oldCount = counts.containsKey( o )
? counts.get( o ).intValue()
: 0;
counts.put( o, oldCount + 1 );
}
for ( Iterator<Object> it = objectList.iterator(); it.hasNext(); ) {
Object o = it.next();
if ( 1 < counts.get( o )) {
it.remove();
}
}
}
Note that this approach requires that the hashCode() method of the list members satisfy the contract specified by Object.hashCode(), including:
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

Related

How to retrieve non-matched result from a list/array

I would like to compare the Objects of two Arrays and obtain a new Array with the Objects that did not match. This is:
Array1
Array2
both contain Object User with methods getId and getUsername
for (int fw = 0; fw < tempOldArray.size(); fw++) {
for (int fi = 0; fi < tempArray.size(); fi++) {
if (tempOldArray.get(fw).getId() == tempArray.get(fi).getId()) {
match++;
break;
}
if(fi == (tempArray.size()-1)) {
nomatchfound++;
break;
}
}
}
Array1: {[1231, Peter], [2562, Jackson], [38987, Robert], [4765, William]}
Array2: {[2562, Jackson], [7584, Alfred], [38987, Robert], [8123, Mozart]}
Array3 should output {[1231, Peter], [4765, William]}
and Array4 should output {[7584, Alfred], [8123, Mozart]}
Also questioned about how to retrieve a result from list
{"peter", "trump", "donald", "jerry"}
{"peter", "donald", "lucas", "jerry"}
and output non matching ones
You could use something like this. With this function it doesn't matter if you compare collection1 with collection2 or collection2 with collection1 if they have different sizes. The diff should be always equal.
private static <E> List<E> getDiff(Collection<E> collection1, Collection<E> collection2) {
Collection<E> largerOne = collection1.size() >= collection2.size() ? collection1 : collection2;
Collection<E> smallerOne = largerOne == collection1 ? collection2 : collection1;
return largerOne.stream().filter(i -> !smallerOne.contains(i)).collect(Collectors.toList());
}
If you are already using lists, just use something on the lines of
list1.removeAll(list2)
To further optimize, if you use a hashset, your remove operations become O(1) so it is even more efficient
This is simply a matter of discrete math. Check out the implementation of removeAll():
public static void main(String[] args) {
List<String> first = Arrays.asList("peter", "trump", "donald", "jerry");
List<String> second = Arrays.asList("peter", "donald", "lucas", "jerry");
List<String> results = new ArrayList<>(first);
results.removeAll(second);
System.out.println(results.toString());
}
Prints:
[trump]
This fulfills your requirement for leaving the first and second Lists intact, and creating a 3rd List to contain the result.
The other Answers showing List::removeAll such as the one from Cuga are correct and best for the simple case.
Java streams
I'll show the fancier approach using Java streams. You may want to use this approach if your situation gets more complicated.
Define your two lists. Using List.of to instantiate an unmodifiable list was added to Java 9.
List < String > namesA = List.of ( "Peter" , "Paul" , "Mary" , "Wendy" , "Lisa" );
List < String > namesB = List.of ( "Peter" , "Paul" , "Jesse" , "Wendy" , "Lisa" );
Create a stream from one list. For each element in the stream, see if that element can be found in the other list.
To find all the items that match, use.filter ( namesB :: contains ).
To find all the items that do not match (the distinct elements), use:.filter ( Predicate.not ( namesB :: contains ) )
That Predicate.not trick is new to Java 11, as shown here.
Collect the results into a new List.
List < String > distinct =
namesA.stream ()
.filter ( Predicate.not ( namesB :: contains ) )
.collect ( Collectors.toList () );
Dump to console.
System.out.println ( "namesA: " + namesA );
System.out.println ( "namesB: " + namesB );
System.out.println ( "distinct: " + distinct );
Results.
namesA: [Peter, Paul, Mary, Wendy, Lisa]
namesB: [Peter, Paul, Jesse, Wendy, Lisa]
distinct: [Mary]
Assuming the class you are using is called Person, you have to add the following equals method to the class definition
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!Person.class.isAssignableFrom(obj.getClass())) {
return false;
}
final Person other = (Person) obj;
if (this.id != other.id) {
return false;
}
return true;
}
In this case you can simply use the removeAll method that's specified in the other answers like this.
list1.removeAll(list2);

Filtering objects with circular reference to itself (ObjectA has property List<ObjectA>)

I have a large json structure with nested values that I have converted into a list of objects to work with. I'd like to filter out all objects that don't contain a specific property value. Problem is though, that so far all I've come up with is a for loop within a for loop within a for loop (and that's given we know the json structure is only three nested levels). I only want to filter out the objects that do contain an integer (if it's null, it could be a parent containing something valid) or parents that are empty). If I try to stream with flattened - I can filter out all my objects and nested objects but won't I lose my structure?
quick eg.
public class ObjectA {
Integer id;
List<ObjectA> sublist;
}
List<ObjectA> fullList;
Set<Integer> keeptheseIntegers;
for (ObjectA obj : fullList) {
if (obj.getId() != null && !keeptheseIntegers.contains(obj.getId()){
fullList.remove(obj);
} else if (obj.getId() == null && obj.getSubList().size() > 0) {
for (ObjectA subObj : obj.getSubList()){
(same thing as above)
}
}
edit - I did realize later that the remove was not working properly and used iterator.remove. still same logical issue though
First: instead of manipulating your original structure (remove unwanted) I would collect the wanted items into an own list during the algorithm.
Second: Iterating through nested structures is a good candidate for the recursive pattern.
Third: Using java 8 I would implement it using streams and lambdas.
Something like this:
public class ObjectA
{
Integer id;
List<ObjectA> sublist;
}
private static final Set<Integer> keeptheseIntegers = new HashSet<>();
static
{
keeptheseIntegers.add( 1 );
}
private List<ObjectA> filter( List<ObjectA> list)
{
List<List<ObjectA>> subLists = new ArrayList<>();
List<ObjectA> result = list.stream()
// get all objects with sublist and add the sublist to interim subLists:
.peek( obj -> {
if ( obj.sublist == null )
{
// assert your assumption that Integer is assigned
if ( obj.id == null )
{
throw new IllegalArgumentException();
}
}
else
{
subLists.add( obj.sublist );
}
} )
// filter for the objects you want in result:
.filter( (obj -> obj.id != null && keeptheseIntegers.contains(obj.id)))
// and convert the resulting stream to a list:
.collect( Collectors.toList());
// call this method recusively for all found sublists:
subLists.forEach( i -> result.addAll(filter( i)) );
return result;
}
and somewher in your main program flow you call it:
...
List<ObjectA> fullList = new ArrayList<>();
List<ObjectA> objWithInt = filter( fullList);
// process the received list. Your original fullList is unchanged.

Remove the reversing pairs

I would like to ask question which I thought it was simple.
However, it might be not at least to me.
Here are lots of string pairs.
My question would be how to remove the reversing pairs. It's because the (A-B) and (B-A) are the same to me. I would like to keep only one.
Input:
A B
A C
A D
B A
C A
D A
B D
D B
Expect output
A B
A C
A D
B D
I try ArrayListMultimap to get (key, List) pairs.
(A, B; C; D)
(B, A; D)
....
However, I still have (A,B) and (B,A)
Any comments are welcome. Thank you.
Create a List of strings, which will contain "AB", "BC", etc..
List<String> = new ArrayList<String>() strings;
Here, you should loop on your left and right strings, concatenate and populate the List, avoiding what you consider as duplicates :
for(<your loop condition here, to iterate over left and right strings>) {
StringBuilder sb = new StringBuilder();
sb.append(leftString); // say "A"
sb.append(rightString); // say "B"
// now sb contains "AB"
// We only append the String if the reverse doesn't already exists in the List
if(!strings.contains(sb.reverse().toString())) // reverse yields "BA"
strings.add(sb.toString());
}
You're done, you have a list of strings without duplicates.
If you want to retrieve left and right parts, just use substring(0,1) and substring (1,2).
I guess I figured this out. Here is the code
Scanner pairs = new Scanner(new File("TestSet.txt"));
ArrayListMultimap<String,String> dataset = ArrayListMultimap.create();
while (pairs.hasNextLine()){
String data0 = pairs.nextLine();
String [] array0 = data0.split("\t", 3);
String itemA = array0[0];
String itemB = array0[1];
if(dataset.containsKey(itemB) && dataset.containsEntry(itemB, itemA)){
}
else{
dataset.put(itemA, itemB);
}
}
Another approach (more instructive I think) is to consider a pair of string as the following class:
class StringPair{
String s1,s2;
public StringPair(String _s1, String _s2) {
this.s1=_s1;
this.s2=_s2;
}
#Override
public boolean equals(Object other) {
if (other == null) return false;
if (other == this) return true;
if (!(other instanceof StringPair))return false;
StringPair p = (StringPair)other;
return (s1.equals(p.s1) && s2.equals(p.s2))
|| ( s1.equals(p.s2) && s2.equals(p.s1));
}
#Override
public int hashCode() {
String s;
if(s1.compareTo(s2) <= 0)
s=s1+s2;
else
s=s2+s1;
return s.hashCode();
}
}
and then use a SET implementation to store the ALL String pairs (this will take care of removing the duplicates). Set does not allows to contain duplicates.
Quoting the javadoc:
Adds the specified element to this set if it is not already present (optional operation). More formally, adds the specified element e to this set if the set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, the call leaves the set unchanged and returns false. In combination with the restriction on constructors, this ensures that sets never contain duplicate elements.
public static void main(String[] args) {
HashSet<StringPair> pairs = new HashSet<StringPair>();
pairs.add(new StringPair("A", "B"));
pairs.add(new StringPair("B", "A"));
pairs.add(new StringPair("B", "C"));
pairs.add(new StringPair("B", "C"));
pairs.add(new StringPair("B", "B"));
for (Iterator<StringPair> i = pairs.iterator(); i.hasNext();) {
StringPair stringPair = (StringPair) i.next();
System.out.println(stringPair.s1+" "+stringPair.s2);
}
}
which outputs:
B B
A B
B C

Java iteration for possible matches in ArrayList

My question is about iteration and performance. Let's think of following case:
public class Car {
private String name;
private int type;
private int horsePower;
String getKey() {
return type + "_" + horsePower;
}
private final int NUM_OF_CARS = 50000;
public void test() {
List<Car> cars = new ArrayList<Car>(NUM_OF_CARS);
for (int i = 0; i < NUM_OF_CARS; i++) {
Car c = new Car();
if (i == 0 || i == 176 || i == 895 || i == 1500 || i == 4600) {
c.name = "Audi A4 " + i;
c.type = 1;
c.horsePower = 200;
} else {
c.name = "Default";
c.type = 2 + i;
c.horsePower = 201;
}
cars.add(c);
}
// Matches should contain all Audi's since they have same type and horse
// power
long time = SystemClock.currentThreadTimeMillis();
HashMap<String, List<Car>> map = new HashMap<String, List<Car>>();
for (Car c : cars) {
if (map.get(c.getKey()) != null) {
map.get(c.getKey()).add(c);
} else {
List<Car> list = new ArrayList<Car>();
list.add(c);
map.put(c.getKey(), list);
}
}
Iterator<Entry<String, List<Car>>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
if (iterator.next().getValue().size() == 1) {
iterator.remove();
}
}
Log.d("test", String.valueOf((SystemClock.currentThreadTimeMillis() - time)));
}
}
Is this the most efficient way of finding all Audi's here?
This took me 1700 ms
Thanks.
It depends why you're iterating. If you really do need to visit every bottom-level Car, then you don't really have a choice. However, if you're looking for specific matches to the String, then you might consider using a Map.
Why don't you try (Map):
http://docs.oracle.com/javase/7/docs/api/java/util/Map.html
Basically it's a collection of Hashmaps:
http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html
Here's an example:
Map<String, List<Car>> map = new HashMap<String, List<Car>>();
If you want to find a String you should use HashMap. Otherwise you cannot avoid that type of iterations as far as i have in mind.
Use Hashing collections: HashSet that uses Object.hashCode() and Object.equals() to optimize search. How?
You must define your implementation of MyClass.hashCode() and equals(). hashCode() gives an integer representation of your object (you can do what you want here, but do it in a way where two different objects have different values)
HashSet will then do a modulo on your result ex: if the HashSet size is 5000 it will do a modulo 5000 and it will find the index where to put your object ex: if your hashCode() returns 10252, then 10252 % 5000 = 252. And your object will be put in an array with the index 252.
Finally, when you will ask (do I have an instance of "BMW x6", the object you ask for will have its hashCode() method called, which will return 10252 again. And HashSet will only search if it has an object in the 252 index.
If ever two objects give the same hashCode, then they will be compared through the equals() method.
I hope my explanations were clear. In short implement hashCode and equals() and try making the implementation of hashCode() optimized so you will gain time when filling your HashSet
You will probably also be interested in HashMap which stores keys and values where keys use the hashing mechanism: so you can find an object by its key

Finding custom objects in array with particular variable

Suppose I have a custom object set up in a class similar to this.
public class anObject {
public String id, otherProperty;
public anObject(){
this.id = "1";
this.otherProperty = "cat";
}
}
Then I create an array of these objects in another class
anObject[] objects = new anObject[40];
for(int i=0; i < 40; i++){
objects[i] = new anObject();
}
What can I do then to find the first object in the array that has an id of 2 (for example)?
anObject found = null;
for(int i=0; i < 40; i++){
if ("2".equals(object[i].id)) {
// found it
found = object[i];
break; // exit the loop
}
}
Or am I missing something?
EDIT: added the break. Also, there is a convention that class names begin with an uppercase letter, such as AnObject.
There are multiple ways of going about this. First, you could do a simple for loop, iterating over all of the objects until one with a specific id is found. Your search complexity would be O(N)
anObject obj = null;
dance: for( int i = 0; i < objects.length; i++ )
{
if( object[i].id == 2 )
{
obj = object[i];
break dance;
}
}
if you know that you're always going to be searching by id, you could implement Comparable. Then you can use java.util.Arrays to sort and search the array for you. This reduces your search to O(log n)
public class anObject implements Comparable {
public String id, otherProperty;
public anObject(){
this.id = "1";
this.otherProperty = "cat";
}
public int compareTo( Object o )
{
if( o instanceof anObject )
{
return this.id.compareTo( ( (anObject) other).id );
}
return -1;
}
}
Last option, you can store the results in a Map<String, anObject>. If you're doing a lot of searching, this is the best method as it gives your search O(1), at the cost of additional memory.
There's no other way besides iterating through them and checking manually, as Matthew showed you. You can store them in the order of the id and do something like binary search to cut down time to O(log(n)) instead of O(n), but that might be too much overhead.
You can try storing them in a Map<String, YourObject> and just do map.get(id). This has O(1) access time.
Map<String, YourObject> map = new HashMap<String, YourObject>();
for (int i=0; i < 40; i++) {
YourObject obj = new YourObject(); // couldn't put anObject, it bugged me :)
map.put(obj.id, obj);
}
// get object with id = 2
YourObject secondOne = map.get("2");
if (secondOne != null) {
...
}
The best way to do this depends your main use-cases, really, and on how many elements you plan on supporting.
public static anObject findById(anObject[] arr,String str) {
for (anObject obj:arr) if (obj.id.equals(str)) return obj;
return null;
}
And then call anObject.findById(objects,"2")
Use Commons Collections:
http://commons.apache.org/collections/apidocs/org/apache/commons/collections/CollectionUtils.html#find(java.util.Collection, org.apache.commons.collections.Predicate)

Categories

Resources