I need to compare 2 Lists of the same type of object. Can I get suggestion on which is the fastest method.
E.g
public class Cars {
String engine;
String brand;
String etc ..;
I got method compareCars(List a, List b) that returns true if there is a single occurrence in which the Cars in both are the same.
First of all, Class name should be singular - Car.
I suggest the following code.
You can override hashCode() and equals() to get compare two cars as you wanted.
boolean compareCars(List<Car> cars1, List<Car> cars2) {
boolean isSingleOccuranceMatch = false;
for (Car car : cars1) {
if (cars2.contains(car)) {
isSingleOccuranceMatch = true;
break;
}
}
return isSingleOccuranceMatch;
}
No need of variable isSingleoccuranceMatch.
This is just for readability.
You can directly return true instead of break and return false after completing all the iterations.
Using Java8, no need of any method
cars1.stream().filter(car1 -> cars2.contains(car1) ).count() > 0
Related
CompareList removedList = new CompareList();
CompareList addedList = new CompareList();
This is how I'm adding elements inside
addedList.add(new Objec("Var_a", "1"));
and class Objec has two strings.
How can I compare that? I can't use contains like I could with ArrayList of Strings?
In CompareList I have
public boolean equals(CompareList l) {
if (l.containsAll(this)) {
if (this.containsAll(l)) {
return true;
}
}
return false;
}
and in Objec
public Objec(String n, String s) {
this.name=n;
this.surname=s;
}
public String toString() {
return " Name: " + name + ", Surname: " + surname;
}
I see that many people are confused with my question. So what I want?
List 1:
Samy Joe
Emma Than
Julia Rob
List 2:
Samy Joe
Emma Than
Anna Sky
Removed Julia Rob and added Anna Sky. But I don't know how to do it when my lists contains of object that have two strings?
This piece of code compares if the lists are equal, that is, contains the same elements.
static boolean same(Collection<?> a, Collection<?> b) {
if (a.size() != b.size()) {
return false;
}
List<?> c = new ArrayList<>(a);
c.removeAll(b);
return c.isEmpty();
}
If the sizes are not equal, then the lists are never equal.
Else, if the sizes are equal, then we know that both lists contain one or more elements that are not present in the other list. So we make a new list from one of the lists (list a in my case), and then we remove the elements of b.
You don't need to use your own class CompareList, instead you could just use an ArrayList or something.
In order to compare your Objec to another one, you'll need to implement equals(Object) and hashCode() correctly.
If you want to know which elements are not contained in the other list, then you can use this:
static HashMap<Collection<?>, Collection<?>> disjoints(Collection<?> a, Collection<?> b) {
List<?> aa = new ArrayList<>(a);
aa.removeAll(b);
List<?> bb = new ArrayList<>(b);
bb.removeAll(a);
HashMap<Collection<?>, Collection<?>> map = new HashMap<>();
map.put(a, aa);
map.put(b, bb);
return map;
}
It returns a map with as keys the two collections and as values the elements of the collection specified by the key, which are not contained in the other collection. For example, if you want to know the elements of a not present in b, then call disjoints(a, b).get(a).
Note: I call the lists collections, because they are. In Java, a List is a subtype of Collection.
You need to override the equals method in your custom object like this:
public class MyObject {
private String name;
private String surname;
#Override
public boolean equals(MyObject myObject) {
// assert that name and surename can not be null or check for this
if (!this.name.equals(myObject.name)) {
return false;
}
if (!this.surname.equals(myObject.surname)) {
return false;
}
return true;
}
}
The contains-Method of a list will be use the equals method to check the equality of your objects.
But the link that #Prakash has posted is the better way do do that. I think no one understand what your attention is to do with yout CompareList and why you use a custom one ;)
EDIT:
Sorry, had mistake in the Signature.
#Override
public boolean equals(Object obj) {
if (!(obj instanceof MyObject) {
return false;
}
// else cast and use code above
}
You're question: "How can I compare that?"
The first thing to keep in mind is that you can compare in 2 ways.
so think about that first.
Equality:
When comparing for equality
use equals() and while you're at it implement hashCode().
Comparing / sorting:
When are 2 objects considered lower then, same, or higher then the other?
implement "Comparable" interface and override compareTo.
You're post contains the equals() so lets say that is what you want to do.
If you want to use containsAll(), keep in mind that it uses equals from the class the list contains to figure out to return true/false. So you should probably:
add equals() to your "Objec" class.
I am learning about arrays, and basically I have an array that collects a last name, first name, and score.
I need to write a compareTo method that will compare the last name and then the first name so the list could be sorted alphabetically starting with the last names, and then if two people have the same last name then it will sort the first name.
I'm confused, because all of the information in my book is comparing numbers, not objects and Strings.
Here is what I have coded so far. I know it's wrong but it at least explains what I think I'm doing:
public int compare(Object obj) // creating a method to compare
{
Student s = (Student) obj; // creating a student object
// I guess here I'm telling it to compare the last names?
int studentCompare = this.lastName.compareTo(s.getLastName());
if (studentCompare != 0)
return studentCompare;
else
{
if (this.getLastName() < s.getLastName())
return - 1;
if (this.getLastName() > s.getLastName())
return 1;
}
return 0;
}
I know the < and > symbols are wrong, but like I said my book only shows you how to use the compareTo.
This is the right way to compare strings:
int studentCompare = this.lastName.compareTo(s.getLastName());
This won't even compile:
if (this.getLastName() < s.getLastName())
Use
if (this.getLastName().compareTo(s.getLastName()) < 0) instead.
So to compare fist/last name order you need:
int d = getFirstName().compareTo(s.getFirstName());
if (d == 0)
d = getLastName().compareTo(s.getLastName());
return d;
The compareTo method is described as follows:
Compares this object with the specified object for order. Returns a
negative integer, zero, or a positive integer as this object is less
than, equal to, or greater than the specified object.
Let's say we would like to compare Jedis by their age:
class Jedi implements Comparable<Jedi> {
private final String name;
private final int age;
//...
}
Then if our Jedi is older than the provided one, you must return a positive, if they are the same age, you return 0, and if our Jedi is younger you return a negative.
public int compareTo(Jedi jedi){
return this.age > jedi.age ? 1 : this.age < jedi.age ? -1 : 0;
}
By implementing the compareTo method (coming from the Comparable interface) your are defining what is called a natural order. All sorting methods in JDK will use this ordering by default.
There are ocassions in which you may want to base your comparision in other objects, and not on a primitive type. For instance, copare Jedis based on their names. In this case, if the objects being compared already implement Comparable then you can do the comparison using its compareTo method.
public int compareTo(Jedi jedi){
return this.name.compareTo(jedi.getName());
}
It would be simpler in this case.
Now, if you inted to use both name and age as the comparison criteria then you have to decide your oder of comparison, what has precedence. For instance, if two Jedis are named the same, then you can use their age to decide which goes first and which goes second.
public int compareTo(Jedi jedi){
int result = this.name.compareTo(jedi.getName());
if(result == 0){
result = this.age > jedi.age ? 1 : this.age < jedi.age ? -1 : 0;
}
return result;
}
If you had an array of Jedis
Jedi[] jediAcademy = {new Jedi("Obiwan",80), new Jedi("Anakin", 30), ..}
All you have to do is to ask to the class java.util.Arrays to use its sort method.
Arrays.sort(jediAcademy);
This Arrays.sort method will use your compareTo method to sort the objects one by one.
Listen to #milkplusvellocet, I'd recommend you to implement the Comparable interface to your class as well.
Just contributing to the answers of others:
String.compareTo() will tell you how different a string is from another.
e.g. System.out.println( "Test".compareTo("Tesu") ); will print -1
and System.out.println( "Test".compareTo("Tesa") ); will print 19
and nerdy and geeky one-line solution to this task would be:
return this.lastName.equals(s.getLastName()) ? this.lastName.compareTo(s.getLastName()) : this.firstName.compareTo(s.getFirstName());
Explanation:
this.lastName.equals(s.getLastName()) checks whether lastnames are the same or not
this.lastName.compareTo(s.getLastName()) if yes, then returns comparison of last name.
this.firstName.compareTo(s.getFirstName()) if not, returns the comparison of first name.
You're almost all the way there.
Your first few lines, comparing the last name, are right on track. The compareTo() method on string will return a negative number for a string in alphabetical order before, and a positive number for one in alphabetical order after.
Now, you just need to do the same thing for your first name and score.
In other words, if Last Name 1 == Last Name 2, go on a check your first name next. If the first name is the same, check your score next. (Think about nesting your if/then blocks.)
Consider using the Comparator interface described here which uses generics so you can avoid casting Object to Student.
As Eugene Retunsky said, your first part is the correct way to compare Strings. Also if the lastNames are equal I think you meant to compare firstNames, in which case just use compareTo in the same way.
if (s.compareTo(t) > 0) will compare string s to string t and return the int value you want.
public int Compare(Object obj) // creating a method to compare {
Student s = (Student) obj; //creating a student object
// compare last names
return this.lastName.compareTo(s.getLastName());
}
Now just test for a positive negative return from the method as you would have normally.
Cheers
A String is an object in Java.
you could compare like so,
if(this.lastName.compareTo(s.getLastName() == 0)//last names are the same
I wouldn't have an Object type parameter, no point in casting it to Student if we know it will always be type Student.
As for an explanation, "result == 0" will only occur when the last names are identical, at which point we compare the first names and return that value instead.
public int Compare(Object obj)
{
Student student = (Student) obj;
int result = this.getLastName().compareTo( student.getLastName() );
if ( result == 0 )
{
result = this.getFirstName().compareTo( student.getFirstName() );
}
return result;
}
If you using compare To method of the Comparable interface in any class.
This can be used to arrange the string in Lexicographically.
public class Student() implements Comparable<Student>{
public int compareTo(Object obj){
if(this==obj){
return 0;
}
if(obj!=null){
String objName = ((Student)obj).getName();
return this.name.comapreTo.(objName);
}
}
I am not getting the right answer when I try to use indexOf() of an ArrayList made up of user defined objects. Here is the code that creates one of the objects:
State kansas = new State("KS", 5570.81, 2000)
So, the name of the object is "kansas"
Here is the code that creates the ArrayList and adds the object:
ArrayList<State> allStates = new ArrayList<State>();
allStates.add(kansas);
And here is the code that I try to use to find the index of this object:
System.out.println(allStates.indexOf(kansas));
This is the point at which my compiler (Eclipse) throws me a red X indicating that there is a problem with my code and the problem is that it does not recognize 'kansas'. So I tried this:
String s = "kansas";
System.out.println(allStates.indexOf(s));
and it will run but the result is -1.
I am calling a method from a different class to create the ArrayList as opposed to creating it in the same class as my main method but I'm new enough to coding that I"m not sure if that is where I am going wrong. However, in order for the program that I am writing to work, I need to have data about each of the State objects stored so that I can access it from the main method.
Any advice?
*This is my first time posting a questions and I wasn't sure how much detail to go into so if I'm missing relevant information please let me know :)
method indexOf uses equlas() method to compare objects.
That why you have to override equals method in your custom class (if you planning use class in Map override hashCode method as well).
most IDE can generate these methods (equals and hashCode).
here simple example.
public class State {
private String stateCode;
public State(String stateCode /* other parameters*/) {
this.stateCode = stateCode;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
State state = (State) o;
return stateCode.equals(state.stateCode);
}
#Override
public int hashCode() {
return stateCode.hashCode();
}
}
This is because, String is not your custom object State type. Your array list is a list of all 'State' types, which is why this -
String s = "kansas";
System.out.println(allStates.indexOf(s));
won't work.
What you can do is have a convenience method that iterates through the list and returns the index.
private int getIndexOfState(String stateName) {
for(State stateObject : allStates) {
if(stateObject.getName().equals(stateName))
return allStates.indexOf(stateObject);
}
return -1;
}
Now you can reuse this method to find index of any state name you pass, and whenever the method returns -1, it means the stateName(state) was not found in the list of states.You can pass in 'Kansas' or 'California' or anything as the parameter to the method.
In your method call you say
System.out.println(getIndexOfState("Kansas"));
System.out.println(getIndexOfState("Chicago"));
The return value is -1 because there is no String "kansas" in allStates, and ArrayList#indexOf returns -1 if the element is not present in the list. If you try to add s to allStates, the compiler won't even let you, because State is not a String.
I don't know why you instantiated a String with the value "kansas", but if you need to refer to the State from its name (maybe the name comes from a Scanner input), you will need a Map<String, State>, such as:
Map<String, State> map = new HashMap<>();
map.put("kansas", kansas) // a String and the object named kansas
Then, you can do:
System.out.println(allStates.indexOf(map.get("kansas")))
//or
String s = "kansas";
System.out.println(allStates.indexOf(map.get(s)))
I have a project in Java. First I need to create a collection with no duplicates in the name of object & its number, so I use the Set collection and this equals method:
public boolean equals(Object obj) {
Course<?> c=(Course<?>)obj;
return (c.number==number&& c.Name.equals(Name));
}
next ..i need to create collection of the same object but now i need to insure that only his name is not duplicate. so its a problem because i cant use two diffrents methods of equals
what can i do??
I'd use a TreeSet instead and specifying the comparator to use for that specific set instead of overriding equals.
https://docs.oracle.com/javase/8/docs/api/java/util/TreeSet.html#TreeSet-java.util.Comparator-
If you don't want them actually sorted, but just remove dupes, the comparator just has to return 0 when they are equal.
TreeSet<Course> tree1 = new TreeSet<Course>((c1, c2) -> c1.number==c2.number && c1.Name.equals(c2.Name) ? 0 : 1);
and
TreeSet<Course> tree2 = new TreeSet<Course>((c1, c2) -> c1.Name.equals(c2.Name) ? 0 : 1);
You can wrap your class in a wrapper class that will implement the hashcode and equals functions the way you want:
public NameWrapper {
private Course c;
public NameWrapper(Course c) {
this.c = c;
}
public void equals(Object other) {
// ...
return this.name.equals(other.name);
}
// + hashCode
// + getter
}
// Similarly with number and name wrapper
And then you can wrap, distinct and unwrap your elements:
Collection<Course> courses = // ...
Collection<Course> distincts =
courses.stream()
.map(NameWrapper::new) // wrap
.distinct()
.map(NameWrapper::getCourse) // unwrap
.map(NumberNameWrapper::new) // wrap
.distinct()
.map(NumberNameWrapper::getCourse) // unwrap
.collect(Collectors.toList())
One easy, but probably not really great solution would be to use two specific wrapper classes that each have different equals methods.
And instead of directly using your own class, you put objects of those "wrapper" classes into those collections.
Like:
class Course { ... your class
class CourseWrapperForName {
Course wrappedCourse;
...
Course getWrappedCourse() { return wrappedCourse; }
#Override
public boolean equals(Object other) {
... compares names
class CourseWrapperForNumber {
Course wrappedCourse;
...
#Override
public boolean equals(Object other) {
... compares numbers
Now, eliminating duplicates can be done by putting your Course objects into the corresponding wrapper; adding wrappers to Sets; and then retrieving the course.
But obviously, that is a lot of boilerplate; and more reasonable solutions could be
A) using a TreeSet with a different
B) the later can be enhanced with a lot of black lambda magic; there is a nice presentation how to do that (it is in German, but mainly code; the interesting part starts at page 40).
I would take the Set<Course> with this equals method implemented (that will give me the course which are unique in name & number both).
Further, I would make a subclass of Course 'SubCourse' and override the equals method:
class SubCourse extends Course{
public boolean equals(Object o){
if(o instanceof SubCourse){
return (this.Name.equals(((SubCourse)o).Name));
}else{
return false;
}
}
}
And then make a Set<SubCourse> that will give you a unique courses in terms of numbers (not in name as we excluded that condition). You need to make the instance variables of Course as protected.
I'm building simple phonebook. Thus a have created a class "Person":
public class Person implements Comparable<Person> {
String Name;
String number;
public Person(String name,String Num) {
Name=name;
number=Num;
}
public String getNumber() {
return number;
}
public String getName() {
return Name;
}
#Override
public int compareTo(Person another) {
return Name.compareTo(another.getName());
}
#Override
public String toString() {
return Name;
}
#Override
public boolean equals(Object obj) {
if(!(obj instanceof Person) && !(obj instanceof String))
{
return false;
}
else
{
if(obj instanceof Person)
return Name.toLowerCase().equals(((Person)obj).getName().toLowerCase());
else
return Name.toLowerCase().equals(((String)obj).toLowerCase());
}
}
#Override
public int hashCode() {
return Name.hashCode();
} }
In some other part of the program i'm creating a Vector, populate it with "Person" objects but when i try to search a person BY NAME using vctPerson.indexOf("John") I always get -1 as result (not found). What's wrong with my code? I have implemented custom "equals" that should work with strings, and according to docs, "indexOf" is using "equals" to compare objects...
EDIT: I KNOW, I SHOULD SEARCH AFTER PHONE NUMBER, NOT NAME BUT IT's IRRELEVANT FOR THIS EXAMPLE
What Vector does in indexOf:
if (o.equals(elementData[i]))
where o would be "John". So you would have to override Sting.equals to do the right comparison (just kidding). Or you could use
vector.indexOf(new Person("John", null));
which will call your equals. Strictly speaking that will solve your question.
But in the long run you should not use Vector for that, because every indexOf call will iterate through the list - this is not very efficient.
A better way is a Map like HashMap where you can store key-value pairs. Lookup using the key is much cheaper than Vector.indexOf if here are a couple of entries.
Map<String, Person> map = new HashMap<String, Person>();
Person p = new Person("John", "1234");
map.put(p.getName().toLowerCase(), p);
// loopup
Person john = map.get("John".toLowerCase());
you called vctPerson.indexOf("John") . In this case, Vector call "John".equals( vctPerson.get( indexValue ) . As equals of String is called, String's equals compare "John" and Person object.
But as String's equals() does not return true when target object is not an instance of String, "John".equals( vctPerson.get( indexValue ) always return false. So result is always -1.
So, you can't use vctPerson.indexOf("John"). If you want to use vector, you need to traverse vector manually.
Your equals is broken: You objects may equal to a String (and that's what you're trying to exploit), but no String may ever equal to you object. Breaking symmetry of equals breaks everything.
Well, to be on the safe side you can always use
1) Map<String,Person> to make the relation between a person and his name
2) Make your own class that extends java.util.Vector and overrides its indexOf method
3) place a breakpoint in your equals method and see what's going on when indexOf gets called. Whatever's going on, though, it's better that you don't rely on the current implementation of indexOf that's specified in the JDK documentation since it may get changed upon the release of a next version of the JDK :)