I have two lists with different objects in them.
List<Object1> list1;
List<Object2> list2;
I want to check if element from list1 exists in list2, based on specific attribute (Object1 and Object2 have (among others), one mutual attribute (with type Long), named attributeSame).
right now, I do it like this:
boolean found = false;
for(Object1 object1 : list1){
for(Object2 object2: list2){
if(object1.getAttributeSame() == object2.getAttributeSame()){
found = true;
//also do something
}
}
if(!found){
//do something
}
found = false;
}
But I think there is a better and faster way to do this :)
Can someone propose it?
Thanks!
If you just need to test basic equality, this can be done with the basic JDK without modifying the input lists in the one line
!Collections.disjoint(list1, list2);
If you need to test a specific property, that's harder. I would recommend, by default,
list1.stream()
.map(Object1::getProperty)
.anyMatch(
list2.stream()
.map(Object2::getProperty)
.collect(toSet())
::contains)
...which collects the distinct values in list2 and tests each value in list1 for presence.
To shorten Narendra's logic, you can use this:
boolean var = lis1.stream().anyMatch(element -> list2.contains(element));
You can use Apache Commons CollectionUtils:
if(CollectionUtils.containsAny(list1,list2)) {
// do whatever you want
} else {
// do other thing
}
This assumes that you have properly overloaded the equals functionality for your custom objects.
There is one method of Collection named retainAll but having some side effects for you reference
Retains only the elements in this list that are contained in the
specified collection (optional operation). In other words, removes
from this list all of its elements that are not contained in the
specified collection.
true if this list changed as a result of the call
Its like
boolean b = list1.retainAll(list2);
Loius answer is correct, I just want to add an example:
listOne.add("A");
listOne.add("B");
listOne.add("C");
listTwo.add("D");
listTwo.add("E");
listTwo.add("F");
boolean noElementsInCommon = Collections.disjoint(listOne, listTwo); // true
faster way will require additional space .
For example:
put all items in one list into a HashSet ( you have to implement the hash function by yourself to use object.getAttributeSame() )
Go through the other list and check if any item is in the HashSet.
In this way each object is visited at most once. and HashSet is fast enough to check or insert any object in O(1).
According to the JavaDoc for the .contains(Object obj):
Returns true if this list contains the specified element. More
formally, returns true if and only if this list contains at least one
element e such that (o==null ? e==null : o.equals(e)).
So if you override your .equals() method for your given object, you should be able to do: if(list1.contains(object2))...
If the elements will be unique (ie. have different attributes) you could override the .equals() and .hashcode() and store everything in HashSets. This will allow you to check if one contains another element in constant time.
to make it faster, you can add a break; that way the loop will stop if found is set to true:
boolean found = false;
for(Object1 object1 : list1){
for(Object2 object2: list2){
if(object1.getAttributeSame() == object2.getAttributeSame()){
found = true;
//also do something
break;
}
}
if(!found){
//do something
}
found = false;
}
If you would have maps in stead of lists with as keys the attributeSame, you could check faster for a value in one map if there is a corresponding value in the second map or not.
Can you define the type of data you hold ? is it big data ? is it sorted ?
I think that you need to consider different efficiency approaches depending on the data.
For example, if your data is big and unsorted you could try and iterate the two lists together by index and store each list attribute in another list helper.
then you could cross check by the current attributes in the helper lists.
good luck
edited : and I wouldn't recommend overloading equals. its dangerous and probably against your object oop meaning.
org.springframework.util.CollectionUtils
boolean containsAny(java.util.Collection<?> source, java.util.Collection<?> candidates)
Return true if any element in 'candidates' is contained in 'source'; otherwise returns false
With java 8, we can do like below to check if one list contains any element of other list
boolean var = lis1.stream().filter(element -> list2.contains(element)).findFirst().isPresent();
Related
I have 2 lists of data:
data class PatientData(var patientID: String, var patientName:String, var patientPhone:String)
var list1: List<PatientData>?=null
var list2: List<PatientData>?=null
I need to determine if list1 is a duplicate of list2. One way of determining this is matching on patientID.
I have tried different ways such as zip and contentEquals but it did not work.
Your data class is doing most of the comparison work for you out of the box, so it's just a case of doing some collection comparisons.
Like Animesh says, you can do a size check and a containsAll (if you know they're the same size you only need to do the containsAll check one way)
if (list1.size == list2.size && list1.containsAll(list2))
You can sort the lists and then directly compare them (since your classes don't implement Comparable we'll have to do it ourselves, patientId is a good choice assuming it's a unique value)
if (list1.sortedBy { it.patientID } == list2.sortedBy { patientID })
Or you might actually want to use a Set for this, if you don't want duplicate PatientData objects anyway - that will let you do nice set properties like unions and intersections, but for comparison purposes two sets are equal if they contain the same collection of items
if (set1 == set2)
I have a function in java like
void remove(List<Var> vars, List<List<Value>> vals) {
int index = calculateIndex();
vars.removeAll(vars.subList(index, vars.size()));
vals.removeAll(vals.subList(index, vals.size()));
}
always both lists have the same number of elements before enter the method, but, after removeAll vars have one element more than vals, index is between zero and the size of the lists, why could this be happening?
If I understand correctly what you're trying to do, the code to remove the sublists should look like
int index = calculateIndex();
vars.subList(index, vars.size()).clear();
vals.subList(index, vals.size()).clear();
removeAll isn't the right tool for the job. The purpose of removeAll is to look at all the elements in collection A, and remove elements in collection B that are equal to any element in collection A. I believe it uses .equals to determine which elements are equal, not reference equality, which means that you could be removing some elements you don't intend to remove. Furthermore, since the collection A in this case would be a sublist of collection B, so that they overlap, I wouldn't count on removeAll to function correctly anyway, although it might; using overlapping lists in this situation could lead to havoc.
As an alternative design and not necessarily on track, I think it would be a nicer method if you actually constructed a new List containing the difference and returned it preserving both the original lists, otherwise its a slight code smell.
i.e.
List<Var> difference(List<Var> vars, List<List<Value>> vals) {
List<Var> results = new ArrayList<Var>();
// Loop through Vars and Vals appropriately adding Var to results based on some criteria
// ....
return results;
}
This way you preserve List vars from appearing to magically change when passed in as a input parameter to a method.
I have java object Map<Integer, SortedMap<Integer, Pair<Integer, String>>> data. I am passing this object to recursive method to build objects of each key in the above map. Please see the example below.
For ex,
<1 - <1, Pair<1, 'A'>>
<2 - <1, Pair<1, 'A'>>
<2, Pair<2, 'B'>>
I want to check the circular dependency on the above map. Please see the example below.
<9994 - <1, Pair<9995, 'X'>>
<2, Pair<2, 'B'>>
<9995 - <1, Pair<9994, 'Y'>>
<2, Pair<2, 'B'>>
Above 9994 contains 9995 and 9995 contains 9994. This is invalid and should error out the programme.
Can anyone suggest me the best way to perform this validation?
Thanks.
private static Boolean validate(
Map> input,
Set object) throws Exception {
Boolean result = true;
for (Pair cal : input.values()) {
if (!object.add(cal.left))
return false;
result = validate(get(cal.left), object);
}
return result;
}
The check for circular dependency could be implemented via Depth-first-search. Fo this, either the objects to be checked would have to be changed to contain a flag (which would indicate whether they have already been visited) or some auxiliary data structure like a list or hashmap would contain the visited nodes.
I would recommend, you use the recursive function to put the keys in a HashSet. The HashSet "add" method returns true if the insertion was successful and false otherwise. So if there was a duplicate the add method will return false. You can use that as your validation.
If you can post some initial code snippet, we can try to help with this solution.
http://docs.oracle.com/javase/7/docs/api/java/util/Set.html
You can track the visited nodes in a collection in all levels of recursion. Check before processing if the node is already visited.
I have a Vehicle class with fields: baseVehicleId, subModelId, notes and partNo
If is the same baseVehicleId, subModelId and partNo , i want the object with the longest notes to remain( and delete the others)
I have this:
Comparator<Vehicle> comparator = new Comparator<Vehicle>() {
#Override
public int compare(Vehicle a, Vehicle b) {
if(a.baseVehicleId().equals(b.baseVehicleId()) && String.valueOf(a.subModelId ()).equals(String.valueOf(b.subModelId ()))
&& String.valueOf(a.partNo ()).equals(String.valueOf(b.partNo ()))
))
return a.getOurnotes().compareTo(b.getOurnotes());
// not good I know
return 1;
}
};
TreeSet<Vehicle> treeSet = new TreeSet<Vehicle>(comparator);
How can i modify this code :/?
So for example if i have all the fields equal and the length of notes are greater then delete the other objects.
The comparator's job is simply to say which of two objects comes first - there's no mechanism to manipulate the underlying collection during it's comparisons. I think you would want to think about this in a slightly different way: perform your filtering logic prior to adding an object to your collection.
I'd recommend just using a standard list, say ArrayList for sake of simplicity. Also you'd need to override the equals() method in your Vehicle class which returns true if two Vehicles share the same IDs and part numbers.
When it comes to adding new vehicles you can do something like this:
int vehicleIndex = myvehicles.indexOf(vehicle) // -1 if missing, otherwise 0 or greater to represent the index position
if (vehicleIndex == -1) {
// No item in the list shares the ids/model/part numbers so can add
myvehicles.add(vehicle)
} else {
// There's another similar vehicle, better compare their notes:
Vehicle existingVehicle = myvehicles.get(vehicleIndex); // retrieve the object from list so that we can compare it
// check if the new vehicle has longer notes
if (vehicle.getOurNotes().length > existingVehicle.getOurNotes().length) {
// if it does, remove the old one from the list
myvehicles.remove(vehicleIndex);
// and add the new one. Effectively we've done a replacement
myvehicles.add(vehicle)
}
}
Obviously it's not automatically sorted but you can do that after adding a batch of items by running the Collections.sort() method which also accepts a Comparator, but this time your comparator can focus on the specific task of detailing the ordering now that you know the list is pre-filtered so you are not concerned with trying to filter items out.
I think you should use the method
maximum = Collections.max(collection, comparator);
for searching the maximum of the elements and then just delete the others with
collection.remove();
Inside the comparator you can't delete from the collection.
Just loop through the collection and remove everything that doesn't meet your requirements using it.remove().
You may also want to look at the Java 8 collections functionality that will allow you to do things like filter a collection as that will also do what you are looking for.
I have an ArrayList<SomeObject> in java which contains some <SomeObject> multiple times.
I also have a Set<SomeObject>, which contains some elements one time only. The elements are only uniquely distinguishable only by their name (String SomeObject.Name).
How am I possible to see if the list has exactly the same elements as the set, but maybe multiple times?
Thanks
There are several collections libraries to do this. For example commons-collection: https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html#isEqualCollection-java.util.Collection-java.util.Collection-
eg. CollectionUtils.isEqualCollection(myList, mySet)
If you have to write it yourself, no libraries, then just check that each contains all the elements of the other:
`mySet.containsAll(myList) && myList.containsAll(mySet)`
It seems like the simplest one-line solution to this is probably
return new HashSet<SomeObject>(list).equals(set);
...which just identifies the unique elements of list and verifies that that matches set exactly.
You could convert the ArrayList to a set by using HashSet:
HashSet listSet = new HashSet(arrayList);
To check whether the ArrayList initially has more elements, just compare the listSet and arrayList size() results:
boolean sameSize = listSet.size() == arrayList.size()
Then you can get the intersection of the two sets (the elements they have in common):
listSet.retainAll(set1)
If listSet.size() == set1.size() now, then they had the same elements, as all elements in the two lists were shared in common. To check whether the arrayList had repeating elements initially, check the value of the boolean from before: if sameSize is true, then they did, false means that they didn't.
You have a couple of options:
add all you List elements to Set and check if the size is the same;
create a new List based on your Set and check is they are equals.
All Java collections have method boolean containsAll(collection<>), so if we want to check if two collections contain same elements, we can write collection1.containsAll(collection2) && collection2.containsAll(collection1) which will return true if collection1 and collection2 contain same elements.
Create a Hash that is a String and an Integer of the count of how many time. Interate thought the list create a Hash entry and setting to one if element exists already add one to the count.
Hash<String, Integer> hash = new HashMap<String, Integer>();
for (String s : list) {
if (hash.containsKey(s))
hash.put(s, hash.get(s)++);
else
hash.put(s,1);
}