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 :)
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.
Actually i am going through one of the tutorial in which it mentioned that when we need to implement the Comparator interface we can override equals method. however it is not necessary to override.
So just to understand better, I override the method as follows:
Test.java
import java.util.TreeSet;
public class Test
{
public static void main(String[] args)
{
TreeSet t = new TreeSet(new MyComparator());
t.add(1);
t.add(1);
t.add(2);
System.out.println(t);
}
}
MyComparator.java
import java.util.Comparator;
public class MyComparator
implements Comparator
{
#Override
public int compare(Object o1, Object o2)
{
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i1.compareTo(i2);
}
#Override
public boolean equals(Object o1)
{
return false;
}
}
For other scenario
import java.util.Comparator;
public class MyComparator
implements Comparator
{
#Override
public int compare(Object o1, Object o2)
{
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i1.compareTo(i2);
}
#Override
public boolean equals(Object o1)
{
return true;
}
}
No matter what I return from equals method either true or false .. it is returning the same TreeSet value.
Can anyone can clear up the concept of functionality of equals method please?
Implementing the equals() method on a Comparator allows you to indicate that one comparator provides the same ordering as another comparator. It has nothing to do with how your elements are sorted. It is a very advanced and extremely rarely needed functionality. You are highly unlikely to ever encounter a situation where your comparator's equals() method will actually be invoked. I would suggest that you ignore it.
Edit:
As an example of the very rare kind of circumstances under which Comparator equals() might be used:
If you look at the source code of TreeMap in function PutAll(Map) they have an optimization which checks whether the given map is also a TreeMap, and if so, then they check whether that map's comparator is equal to this map's comparator, and if so, they perform an optimized insertion of data which is known to already be sorted in the right order; otherwise, they delegate to super.PutAll(Map) which adds the items one by one.
equals() method of the class, Comparator and Comparator require equals consistency because some Java collection classes may behave unpredictably if the compareTo() and equals() methods are not returning consistent results.
Java uses == operator to compare two primitives and/or to check if two variables refer to the same object.
Example :
String apple1 = new String("apple");
String apple2 = new String("apple");
System.out.println(apple1.equals(apple2)); // true
StringBuilder app1 = new StringBuilder("apple");
StringBuilder app2 = new StringBuilder("apple");
System.out.println(app1.equals(app2)); // false
As you can see we get different behavior. Why is that? This happens because String class implements an equals() method, which checks that the values are the same. On the other hand, StringBuilder does not implement equals() method, instead it uses the implementation of equals() provided by Object class. And implementation provided(inherited) by Object class does only simply checks if the two referred objects are the same.
So to check if two objects are equivalent Java uses the equals() method and whenever you introduce your own type you must overrides equals() method if you do not want to rely on Object class implementation of equals() method
Let's for example introduce our own type : simple class Apple
public class Apple {
private int weight;
private int cost;
private String color;
Now how do would you decide if two apples are equal? By color, by weight, by price or something else? This is why you need to supply explicitly your own equals method, so objects of your type can be compared for equality.
Example below compares two Apple object for equality, and says that two object are equal if they belong to the same 'Apple' class and if their weight and cost is the same. Notice we do not compare by color, we assume that color is irrelevant in our case, which means that we accept the fact that apples of different colors, but with the same weight and same cost are considered equals.
#Override
public boolean equals(Object obj) {
if ( !(obj instanceof Apple)) return false;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Apple other = (Apple) obj;
if (cost != other.cost and weight != other.weight )
return false;
return true;
}
You can implement any logic you like for your equals() method. Java equals() method is very important and it provides contract or important rules developer shall follow. I will not list them, you get them from here, they are logic and pretty straight forward.
There is another contract of equals() - whenever you override equals(), you are also expected to override hashCode() method. The reason behind that is because hashcode is used internally by some of Java collections, when object is stored as a key in the map.
Hashcode is a number that categorizes object into categories. Imagine you are given various sorts of apples(red, green, yellow) and asked to give them back when asked for particular sort. It would be much efficient and faster if you categorize them, put each in a different buckets accordingly and whenever asked for particular sort (for simplicity let's say red apple), and as you have already sorted and categorized them, you can retrieve them much faster. Hope it is clear now why do you need to implement hashcode(). hashcode() has it's own contract or rules same as equals() method. They pretty much make common sense :
First, result of hashcode() within same program shall not change. What it means that in your hascode() calculations you should not include variable that my change during execution of a program. For example if cost of Apple's is mutable, i.e. it may change, it is not good idea to include them into hashcode() calculation, otherwise results would be inconsistent.
Second rule says, that when called with two objects, if equals() returns true then calling hashCode() on each of those objects must retrieve the same result. But if equals() returns false when called with two objects, calling hashCode() on each of those objects does not necessary have to return a different result. Confusing? Why? Because hashCode() results are not required to be unique when called on unequal objects - you may put two unequal object in one bucket.
Now regarding Comparator - it is easier to understand it's logic when you introduce your own type, instead of using built-in types.
For example let's say we have class Apple with two attributes : weight and price and want to put out object this type into sorted collection TreeSet.
public class Apple {
private int weight;
private int cost;
Now how do you want this apples to be sorted inside collection - do you want to sort them by weight or by price? How shall compiler decide?
Supplying appropriate Comparatoror Comparable allows you to pass your intent.
Let's try to add object to collection.
public class Apple {
private int weight;
private int cost;
public static void main(String[] args) {
Apple redApple = new Apple();
redApple.setCost(10);
redApple.setWeight(2);
Apple greenApple = new Apple();
greenApple.setCost(12);
greenApple.setWeight(3);
Set<Apple> apples = new TreeSet<>();
apples.add(redApple);
apples.add(greenApple);
System.out.println(apples);
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getCost() {
return cost;
}
public void setCost(int cost) {
this.cost = cost;
}
#Override
public String toString() {
return "Apple [weight=" + weight + ", cost=" + cost + "]";
}
}
If you run above code you will get RuntimeError : Apple cannot be cast to java.lang.Comparable, because compiler has to way to figure out how do you want to compare your apples.
So let's fix it :
Let's implement Comparable interface
public class Apple implements Comparable<Apple> {
and override compareTo method
#Override
public int compareTo(Object obj) {
int cost = ((Apple) obj).getCost();
return this.getCost() - cost; // sorting in ascending order.
// change to this to sort in Descending order
// return cost - this.getCost();
}
Now with this changes, let's run our code :
[Apple [weight=2, cost=10], Apple [weight=3, cost=12]]
Our collection is sorted by cost in ascending order.
Now what if you don't have access to Apple class and you can't change source code to implement Comparable.
This is where Comparator helps.
Remove implements Comparable, as we assume we can't modify this class
public class Apple {
and remove method
#Override
public String toString() {
return "Apple [weight=" + weight + ", cost=" + cost + "]";
}
Add comparator implementation :
public class AppleComparator implements Comparator<Apple> {
#Override
public int compare(Apple app1, Apple app2) {
return app1.getCost() - app2.getCost();
}
}
Now I can just supply Comparator to collection to express my intend
Set<Apple> apples = new TreeSet<>(new AppleComparator());
Collection again will be sorted by cost, accordingly to supplied Comparator.
So we need to supply either Comparator or Comparable in order to get them stored in the collection, particularly in TreeSet.
Now regarding your question : There is no connection between Comparator and equals() method . There is only required consistency between Comparable (compareTo() method ) and equals() method.
Example - in above mentioned Apple class, in version that implements Comparable,
we introduce new logic for determining equality. The compareTo() method returns 0 if two objects are equal, while your equals() method returns true if two objects are equal.
A natural ordering that uses compareTo() required to be consistent with equals iff x.equals(y) is true whenever x.compareTo(y) equals 0. So you have to make your Comparable class consistent with equals because some Java collection classes may behave unpredictably if the compareTo() and equals() methods are not returning consistent results.
The following example shows compareTo() method that is not consistent with equals:
public class Apple implements Comparable<Apple> {
private int weight;
private int cost;
private String color;
public boolean equals(Object obj) {
if(!(obj instanceof Apple)) {
return false;
}
Apple other = (Apple) obj;
return this.weight == other.weight;
}
public int compareTo(Apple obj) {
return this.cost.compareTo(obj.cost); }
}
If we want to sort Apple objects by cost, but cost may not be unique. There could two objects with the same cost. Therefore, the return value of compareTo() might not be 0 when comparing two equal Apple objects, which mean that this compareTo() method is not consistent with equals().
The equals() method that you have override, is in the MyComparator class so it will be used if you need to compare 2 instance of Mycomparator ;)
And that's not what you're doing, you are comparing int so yes the compare() method is usefull because it's the one which will be used for sorting the Treeset but the equals method here won't be used
In 99% of the time, you don't need to override equals method in classes that implements Comparator, because they are here just to compare values not themself to another one, and in fact because most of the time they have to attributes, you won't have comparator equals.
I don't understand why you are linking the Comparator and the equals() method. The interfaces like Comparable and Comparatorare used for giving a comparison mechanism, like if you are putting data into a collection which is designed for sorting purpose, then it should have a comparison mechanism. Here is an example
package snippet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
class Person {
private int id;
private String name;
private String code;
private double salary;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public boolean equals(Object obj) {
if(obj != null && (obj instanceof Person)) {
Person other = (Person) obj;
return this.code.equals(other.getCode());
}
return false;
}
public Person(int id, String name, String code, double salary) {
super();
this.id = id;
this.name = name;
this.code = code;
this.salary = salary;
}
}
public class EqualsMethodImpl
{
public static void main(String[] args) {
Set<Person> set = new TreeSet<Person>();
Person p1 = new Person(1, "Sam", "M-1-SAM-50", 50000.00);
Person p2 = new Person(2, "Diaz", "M-1-SAM-35", 35000.00);
Person p3 = new Person(3, "Remy", "M-1-SAM-100", 100000.00);
Person p4 = new Person(4, "Cesar", "M-1-SAM-80", 80000.00);
Person p5 = new Person(5, "Rino", "M-1-SAM-5", 5000.00);
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
set.add(p5);
printPersons(set);
}
private static void printPersons(Set<Person> set) {
System.out.println("Id\tName\tCode\t\tSalary");
Iterator<Person> perItr = set.iterator();
while(perItr.hasNext()) {
Person p = perItr.next();
System.out.println(p.getId()+"\t"+p.getName()+"\t"+p.getCode()+"\t"+p.getSalary());
}
}
}
While you running this code you will get ClasCastException at line set.add(p1);, why because the collection TreeSet is confused about on which attribute of Peron class should it do the sorting. That's where the interfaces like Comparable and Comparator have their significance.
Add this code to your Person class
public int compareTo(Person other) {
Double person1Salary = this.getSalary();
Double person2Salary = other.getSalary();
return person1Salary.compareTo(person2Salary);
}
by making your class Comparable type like Person implements Comparable<Person>
So by this code you are explaining the collection on which attribute we need to do the sorting. So when you print the content of Set it will be ordered on the base salary (ascending by default).
The Wrapper classes like Byte, Short, Integer, Long, Float, Double and other predefied classes like String all are implementing Comaparable interface and that's why you can simply pass them to a Sortable collection.
Now coming to equals method, it is used to check the equality of two objects, the traditional equals method inherited from Object class will check equality based on address locations. So take a case in Person class where I haven't overrided equals method and I'm writing a code like this
Person p1 = new Person(1, "Sam", "M-1-SAM-50", 50000.00);
Person p2 = new Person(1, "Sam", "M-1-SAM-50", 50000.00);
System.out.println(p1.equals(p2));
It will return false because both are two different objects placed in two different address locations, but we know that the objects are having data of the same person. That's the place where we override equals() method like I did in my Person class based on Person code. In that case System.out.println(p1.equals(p2)); will print true because the codes are equal. equals() method helps us in finding the duplicates. I hope this helps.
The equals method is used to check the Equality of two comparators i.e in equals method you can specify what makes ComparatorA and ComparatorB Identical, it has nothing to do with the elements that are sorted using these comparators.
you don't need to sort the values of Treeset. TreesSet elements are automatically sorted in ascending order.
class TreeSet1{
public static void main(String args[]){
//Creating and adding elements
TreeSet<String> al=new TreeSet<String>();
al.add("Ravi");
al.add("Vijay");
al.add("Ravi");
al.add("Ajay");
//Traversing elements
Iterator<String> itr=al.iterator();
while(itr.hasNext()){
System.out.println(itr.next());
}
}
}
output=
Ajay
Ravi
Vijay
First of all, please use generics. With the added compile-time type-safety, your code will be more robust, which in turn will make your life (and that of those who read your code) much easier (if you know what you're doing, of course). Your class MyComparator implements the raw type Comparator, which means that by just looking at its type declaration, you have no way of knowing which types of objects it compares. You have to look at the source (or, alternatively, at the documentation) in order to find out that, if any object that is not a subtype of Integer is passed, a ClassCastException will be thrown at runtime. On the other hand, if it were to implement Comparator<Integer>, the compiler will never let it come to this, because then, the method signature of the compare method would read compare(Integer, Integer) instead of compare(Object, Object), meaning that, if an object is not an Integer, it can never be passed to the method in the first place, which is something that can already be ensured at compile time.
Having that out of the way, you seem to be confusing Comparator and Comparable. A Comparable means that something has a "natural ordering", as Java calls it. An integer is a simple example, because a number is inherently quantifiable, so comparing two numbers is unambiguous (Double.NaN is a special case, but that is another matter). Thus, Integer implements Comparable<Integer> (but not Comparable<Number>, interestingly). However, occasions might arise when you want to compare things that are not inherently quantifiable. Arun Sudhakaran used the idea of comparing persons as an example in his answer. He suggested making the Person class implement Comparable<Person> so that you can pass it to a TreeSet<Person>, but this seems counterintuitive to me, for the precise reason that a person is not inherently quantifiable. You might want to sort the persons by their salary, but this quantification is not inherent to a person, it is only specific to your particular intention in that situation. So this would be a use case for a Comparator<Person>. You can sort the persons by their age, salary, height, whatever, by providing a respective Comparator<Person>, but a Person is not by itself comparable.
Note that the two are not mutually exclusive – you can also compare objects that implement Comparable by using a Comparator whose ordering differs from the natural ordering of the Comparable objects.
Now when the javadocs talk about comparisons being "consistent with equals", then "equals" in this context always refers to the equals(Object) method of the object to be compared, may that object implement Comparable itself or just be compared to another object with a Comparator. In other words, if a comparison method (be that a Comparator or a natural ordering) deems two objects equal that are also equal according to their respective equals(Object) method, and vice versa, this comparison method is "consistent with equals" This has nothing to do with the equals(Object) method of a Comparator, which was already pointed out by Mike Nakis (unless you want to compare Comparators).
An example of a natural ordering that is inconsistent with equals is that of the class BigDecimal. Two BigDecimals representing 2.0 and 2.00 will not be equal according to BigDecimal.equals(Object), but they will be considered equal by their natural ordering (i.e. by compareTo(BigDecimal)).
I have such hashmap
HashMap<Man, Double> d = new HashMap<>();
Then I add new pair into it.
d.put(new Man("John"), 5.);
How can I retrieve this pair from this map?
I tried to do:
Man man = new Man("John");
System.out.println(d.get(man));
But as a result I have null while I expected 5.
This can only work if you override the methods equals(Object obj) and hashCode() in your class Man to allow your HashMap to understand that even if they are not the same instances, they have the same content and should be considered as the same key in your map.
So assuming that your class Man is something like:
public class Man {
private final String name;
public Man(final String name) {
this.name = name;
}
...
}
If you ask your IDE to generate the methods for you, you would get something like this:
#Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || this.getClass() != o.getClass()) return false;
final Man man = (Man) o;
return Objects.equals(this.name, man.name);
}
#Override
public int hashCode() {
return Objects.hash(this.name);
}
You need to have something unique that defines the object Man.
In your case, it appears to be -name.
So you override the equals() method and similarly the hashcode() methods in your Man class using name as the unique identifier.
Since name is a string you can delegate the task to similar methods in the String class.
The HashMap is working fine. Just because you make two new objects with the same name, doesn't mean the computer thinks they are the same object.
Try the following:
man1 = new Man("John");
man2 = new Man("John");
if (man1 == man2) {
System.out.println("Equal");
} else {
System.out.println("Not equal");
}
You'll get "not equal" because the computer is checking to see if they are exactly the same object, not just named the same.
Each time you're using the "new" keyword, you're declaring a new object and the computer gives it a unique address.
Try this:
man1 = new Man("John");
HashMap<Man, Double> myList = new HashMap<>();
myList.put(man1, "5.00");
System.out.print.ln(myList.get(man1));
you'll see you now get "5.00" back because you're actually giving it the exact object that is the key in the map.
You'll need to define manually how you decide that two "men" are equal if you want the behavior to work like this. You might be better off using a full name as a key, since that's usually going to be unique, and less work for you to implement.
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 want to check whether a List contains an object that has a field with a certain value. Now, I could use a loop to go through and check, but I was curious if there was anything more code efficient.
Something like;
if(list.contains(new Object().setName("John"))){
//Do some stuff
}
I know the above code doesn't do anything, it's just to demonstrate roughly what I am trying to achieve.
Also, just to clarify, the reason I don't want to use a simple loop is because this code will currently go inside a loop that is inside a loop which is inside a loop. For readability I don't want to keep adding loops to these loops. So I wondered if there were any simple(ish) alternatives.
Streams
If you are using Java 8, perhaps you could try something like this:
public boolean containsName(final List<MyObject> list, final String name){
return list.stream().filter(o -> o.getName().equals(name)).findFirst().isPresent();
}
Or alternatively, you could try something like this:
public boolean containsName(final List<MyObject> list, final String name){
return list.stream().map(MyObject::getName).filter(name::equals).findFirst().isPresent();
}
This method will return true if the List<MyObject> contains a MyObject with the name name. If you want to perform an operation on each of the MyObjects that getName().equals(name), then you could try something like this:
public void perform(final List<MyObject> list, final String name){
list.stream().filter(o -> o.getName().equals(name)).forEach(
o -> {
//...
}
);
}
Where o represents a MyObject instance.
Alternatively, as the comments suggest (Thanks MK10), you could use the Stream#anyMatch method:
public boolean containsName(final List<MyObject> list, final String name){
return list.stream().anyMatch(o -> name.equals(o.getName()));
}
You have two choices.
1. The first choice, which is preferable, is to override the `equals()` method in your Object class.
Let's say, for example, you have this Object class:
public class MyObject {
private String name;
private String location;
//getters and setters
}
Now let's say you only care about the MyObject's name, that it should be unique so if two `MyObject`s have the same name they should be considered equal. In that case, you would want to override the `equals()` method (and also the `hashcode()` method) so that it compares the names to determine equality.
Once you've done this, you can check to see if a Collection contains a MyObject with the name "foo" by like so:
MyObject object = new MyObject();
object.setName("foo");
collection.contains(object);
However, this might not be an option for you if:
You are using both the name and location to check for equality, but you only want to check if a Collection has any `MyObject`s with a certain location. In this case, you've already overridden `equals()`.
`MyObject` is part of an API that you don't have liberty to change.
If either of these are the case, you'll want option 2:
2. Write your own utility method:
public static boolean containsLocation(Collection<MyObject> c, String location) {
for(MyObject o : c) {
if(o != null && o.getLocation.equals(location)) {
return true;
}
}
return false;
}
Alternatively, you could extend ArrayList (or some other collection) and then add your own method to it:
public boolean containsLocation(String location) {
for(MyObject o : this) {
if(o != null && o.getLocation.equals(location)) {
return true;
}
}
return false;
}
Unfortunately there's not a better way around it.
This is how to do it using Java 8+ :
boolean isJohnAlive = list.stream().anyMatch(o -> "John".equals(o.getName());
Google Guava
If you're using Guava, you can take a functional approach and do the following
FluentIterable.from(list).find(new Predicate<MyObject>() {
public boolean apply(MyObject input) {
return "John".equals(input.getName());
}
}).Any();
which looks a little verbose. However the predicate is an object and you can provide different variants for different searches. Note how the library itself separates the iteration of the collection and the function you wish to apply. You don't have to override equals() for a particular behaviour.
As noted below, the java.util.Stream framework built into Java 8 and later provides something similar.
Collection.contains() is implemented by calling equals() on each object until one returns true.
So one way to implement this is to override equals() but of course, you can only have one equals.
Frameworks like Guava therefore use predicates for this. With Iterables.find(list, predicate), you can search for arbitrary fields by putting the test into the predicate.
Other languages built on top of the VM have this built in. In Groovy, for example, you simply write:
def result = list.find{ it.name == 'John' }
Java 8 made all our lives easier, too:
List<Foo> result = list.stream()
.filter(it -> "John".equals(it.getName())
.collect(Collectors.toList());
If you care about things like this, I suggest the book "Beyond Java". It contains many examples for the numerous shortcomings of Java and how other languages do better.
Binary Search
You can use Collections.binarySearch to search an element in your list (assuming the list is sorted):
Collections.binarySearch(list, new YourObject("a1", "b",
"c"), new Comparator<YourObject>() {
#Override
public int compare(YourObject o1, YourObject o2) {
return o1.getName().compareTo(o2.getName());
}
});
which will return a negative number if the object is not present in the collection or else it will return the index of the object. With this you can search for objects with different searching strategies.
Map
You could create a Hashmap<String, Object> using one of the values as a key, and then seeing if yourHashMap.keySet().contains(yourValue) returns true.
Eclipse Collections
If you're using Eclipse Collections, you can use the anySatisfy() method. Either adapt your List in a ListAdapter or change your List into a ListIterable if possible.
ListIterable<MyObject> list = ...;
boolean result =
list.anySatisfy(myObject -> myObject.getName().equals("John"));
If you'll do operations like this frequently, it's better to extract a method which answers whether the type has the attribute.
public class MyObject
{
private final String name;
public MyObject(String name)
{
this.name = name;
}
public boolean named(String name)
{
return Objects.equals(this.name, name);
}
}
You can use the alternate form anySatisfyWith() together with a method reference.
boolean result = list.anySatisfyWith(MyObject::named, "John");
If you cannot change your List into a ListIterable, here's how you'd use ListAdapter.
boolean result =
ListAdapter.adapt(list).anySatisfyWith(MyObject::named, "John");
Note: I am a committer for Eclipse ollections.
Predicate
If you dont use Java 8, or library which gives you more functionality for dealing with collections, you could implement something which can be more reusable than your solution.
interface Predicate<T>{
boolean contains(T item);
}
static class CollectionUtil{
public static <T> T find(final Collection<T> collection,final Predicate<T> predicate){
for (T item : collection){
if (predicate.contains(item)){
return item;
}
}
return null;
}
// and many more methods to deal with collection
}
i'm using something like that, i have predicate interface, and i'm passing it implementation to my util class.
What is advantage of doing this in my way? you have one method which deals with searching in any type collection. and you dont have to create separate methods if you want to search by different field. alll what you need to do is provide different predicate which can be destroyed as soon as it no longer usefull/
if you want to use it, all what you need to do is call method and define tyour predicate
CollectionUtil.find(list, new Predicate<MyObject>{
public boolean contains(T item){
return "John".equals(item.getName());
}
});
Here is a solution using Guava
private boolean checkUserListContainName(List<User> userList, final String targetName){
return FluentIterable.from(userList).anyMatch(new Predicate<User>() {
#Override
public boolean apply(#Nullable User input) {
return input.getName().equals(targetName);
}
});
}
contains method uses equals internally. So you need to override the equals method for your class as per your need.
Btw this does not look syntatically correct:
new Object().setName("John")
If you need to perform this List.contains(Object with field value equal to x) repeatedly, a simple and efficient workaround would be:
List<field obj type> fieldOfInterestValues = new ArrayList<field obj type>;
for(Object obj : List) {
fieldOfInterestValues.add(obj.getFieldOfInterest());
}
Then the List.contains(Object with field value equal to x) would be have the same result as fieldOfInterestValues.contains(x);
Despite JAVA 8 SDK there is a lot of collection tools libraries can help you to work with, for instance:
http://commons.apache.org/proper/commons-collections/
Predicate condition = new Predicate() {
boolean evaluate(Object obj) {
return ((Sample)obj).myField.equals("myVal");
}
};
List result = CollectionUtils.select( list, condition );