I am somewhat new to java and very new to the Collection framework. I know that this refers to the current object
public class Student implements Comparable <Student> {
String name;
int grade;
public Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
public int compareTo(Student s) {
return this.name.compareTo(s.name);
}
public String toString() {
return this.name + ", " + this.grade;
}
}
Here this.name is null and s.name does have a value, so what are we trying to do by comparing this.name.compareTo(s.name);
Also what really happens when we do Collections.sort(studentList); ?
The code snippet is just for demo purposes
You are asking two different questions, so I will answer them seperately
First one which is what are we trying to by comparing this.name.compareTo(s.name);
When the compareTo method is called on an object of class Student, the this becomes the calling object. Since the calling object (hopefully) has been initialized properly this.name will be the name of the calling object.
s.name is the name of the Student object passed in to the compareTo method which is again (hopefully) initialized properly and has a name.
What is boils down to is a String variable calling compareTo passing in a String variable to compare with
Second is what really happens when we do Collections.sort(studentList);
Here is the JavaDocs on the Collections.Sort method but you are likely asking about what it does relative to your implementation of Comparable. In short it uses your compareTo method when doing the comparisons for the sort
Since it seems that the name is an important property of a Student the first thing you should ask yourself is "What is a valid name for a student?" Is null valid? Is an empty string valid? If not, you need to prevent the initialization of a Student with an invalid name by using a setter:
public void setName(String name) {
if (name is invalid) {
throw error;
}
this.name = name; // name is valid, so this is safe now
}
Now, call the setter in your constructor and you'll be sure that if you have a Student, he'll have a valid name.
One problem with having an invalid name is that if you don't prevent null values you'll get a NullPointerException every time you call the compareTo() method. You'll essentially be calling null.compareTo() and obviously null doesn't have such a method, it doesn't have any methods.
Now, to the sort() method. How do you sort Students? If you don't tell Java how to compare one Student to another Student, how should it order them? It can handle numbers (2 is less than 3) and strings ("a" is before "b"), but it can't know how to compare a custom class you created. Therefore, you need to let it know - using the compareTo() method. Calling sort() on a collection of objects which cannot be compared will cause an exception. Calling sort() on a collection of Student will order them using the rules you provided in compareTo() which is by name (regular string comparison).
As for the sorting mechanism itself, it's implementation specific, but it's usually a combination of several algorithms, a hybrid. In Java I believe it's Timsort, in C# it's introspective sort. In any case, in order to sort a collection like this, you need to compare elements, two by two (hence the requirement to implement Comparable). The trick is how to do this so as to reduce the number of comparisons performed. There are a lot of good sources out there that explain different sorting methods, but it all boils down to being able to compare elements and tell which should come before which.
Related
I encountered this question during OCPJP.
Given the code:
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public boolean equals(Object o) {
if (!(o instanceof Person)) return false;
Person p = (Person)o;
return p.name.equals(this.name);
}
}
And the following statements (only one of which is true):
A. Compilation fails because the hashCode method is not overridden
B. A HashSet could contain multiple Person objects with the same name.
C. All Person objects will have the same hash code because the hashCode method is not overridden
D. If a HashSet contains more than one Person object with name="Fred" then removing another Person, also with name="Fred", will remove them all.
The answer is (B), but I don't understand why. Can somebody please explain this?
As a general rule: always make sure that if obj1.equals(obj2), then obj1.hashCode() == obj2.hashCode()
In your case, since hashCode isn't being overridden, it's using the default one defined in Object, which does not take into account any properties of the object - you'll generally find that new Object().hashCode() != new Object().hashCode().
With this case, if you have two Person objects with the same name, they will most likely end up with different hashCode values, allowing them to coexist in the set: your HashSet can contain multiple Person objects with the same name, which is answer (B).
This is why it's important to override both hashCode and equals - if your objects are equal, but don't generate the same hashCode, they won't behave properly when using them in hash-based collections. This related question has a bit more details on what happens in different cases.
This question already has answers here:
Can someone explain a void return type in Java?
(5 answers)
Closed 6 years ago.
I'm confused about "void",
as it pertains to methods.
I don't know what the distinction between two methods is when one has "void" and another doesn't.
For example, if I do:
Public meth (int amount)
{
amount = initial * interest;
return amount;
}
( not sure if it was right, or even valid, to take the name "amount" and name it the same thing as my formal parameter, but what makes sense here is that you're performing a calculation and returning the result)
Then, if I did something like:
Public void Testing (int array[])
{
//code that would modify the internals of an array
}
Would the second one have no "return" because it's more of a general method, that can be applied to any integer array, while the first one is about doing work on specific variables?
Would also appreciate one or two more examples of when I would or wouldn't be using "void" and "return".
One other thing that seems to confuse me is calling methods.
I know sometimes I'll do something like, for example, using the Testing method above,
Testing(ArrayName);
Other times, it will be like:
NameOfWhateverImApplyingMethodTo.MethodName();
And then there are times when things will be done properly by:
Thing1.MethodName(Thing2);
Which circumstances would I switch the syntax for method calls like this?
Java is case sensitive, so the modifier Public is invalid, use public
You can't define a method as public methodName(int a), only a constructor has this signature, a method must be public void methodName(<signature>) for methods that don't return anything or public <return type> methodName(<signature>) for methods that do.
Void basically means that the method will not return anything.
If you did
String name= "tim";
public void getName(){
return name;
}
This would result in an error, because the getName method is returning a string object called name, but the method declaration is saying I am returning nothing - because it is void.
Instead the method should be :
String name = "tim";
public String getName(){
return name;
}
Now when the method getName() is called it will return a string object "name" with "tim" inside of it :)
You might have void for a set method. So for example
String name = "tim";
public void setName(String newName){
this.name = newName;
}
When this method is called you would use setName("Andy"); and it would set the value of the name variable to be "Andy". Nothing is returned in this method, because it is setting something, but there is no need to send anything back, so we use void on the method declaration.
Hope this helps.
The method that has void as return type does not return anything. For example you want to set a field firstName in your class. You will write a setting method like
public void setFirstName(String n) {
this.firstName = n;
}
As you can see you are just setting a class variable and does not require to return anything.
If you dont use void then you have to provide a return type for method. Like if you wish to write a getter for above variable as:
public String getFirstName() {
return this.firstName;
}
Once you provide a return type, you will have to return a value of that type otherwise your code will not compile.
Calling a method can be done based on where you are calling it from and what modifier is used:
If you are calling the method from the same class then you can simply write firstName = getFirstName()
If you are calling the method from another class then you require object of method's class as qualifier like personObject.getFirstName()
If you are calling a static method then you require class name as qualifier like Person.getFirstName();
Return type is what you get out of it. When you call it, what are you hoping to get back? For instance, if the method gets the average of two numbers, then you're expecting a number back, so the return type will be a number type, like "int" (integer).
You can see what it should be using that logic or by looking in the method for the word return - what comes after return is what is returned, and its type should be declared in the method (e.g. if it says "return 4;" it's returning an int, and should be e.g. public int getFour()
You also asked about e.g. testing() vs testing(word)
I remember having the same difficulty. The distinction between the two also relates to the method declaration line. I'll illustrate.
public String testing(){
return "a word";
}
Calling this method by doing "System.out.println(testing());" should print "a word". Calling this method by doing "System.out.println(testing("a word"));" will give you an issue - this is because when you call testing, it looks at the appropriate method: one in the right class, with the right return type and with the right arguments/parameters. If you're calling testing("a word"), that means you're using a String as an argument (because "a word" is a string), and so it tries to use the testing(String aString) method - which doesn't exist.
So you use empty brackets when the method takes no input, and you put stuff in brackets when the method expects stuff. This should be less confusing than it sounds, because it's usually logical - if you want to call a method that returns an average, you need to ask yourself "Average of what?" You'd probably need to supply it with the values you want the average of.
Moving on: (a) testing() versus(b) AClass.testing() versus(c) aclass.testing() -
In (a), there's no class specified. Therefore, if you call it from that class, Java can guess which class: this one, and it'll work. From any other class, it won't know what you're talking about, and might even insult you.
In (b), you're specifying a class in general - therefore it'll know what class to find it in - and it'll work if it's a "static method". *[see bottom]
In (c), you're specifying an instance of AClass you want to run "testing()" on*.
For instance, imagine you've created a class called Business. You make a hundred Business objects by specifying for each a name, number, address.
e.g.
Business b = new Business(name, number, address);
Then in the Business class you have a method "getName()". This method takes no argument - you could see that the brackets are empty - so if, from another class, you call "Business.getName()", how could it know which name you want? You've just made a hundred businesses!
It simply can't. Therefore, for such a method, you'd call "b.getName()" (b being the Business we created above) and it would get the name for this instance of a Business - namely, b.
I'm happy to help, so if you're confused about any particular parts of what I just wrote please let me know and I'll try to elaborate!
edit: A bit on static methods:
Static methods don't belong to an instance of the class. getName(), for example, would get the name of this Business - ie, this instance of the Business class. But let's say that in the Business class you made a method that took the first letter of each word in a String and transformed it to uppercase - like if you wanted to make the business names look more professional when you printed them out.
public static String stringToUpperCase(String aString){
aString = aString.substring(0, 1).toUpperCase() + aString.substring(1);
return aString;
}
And to use that, you change the getName() method from:
public String getName(){
return name;
}
to
public String getName(){
return stringToUpperCase(name);
}
The new method is used here to make the name have an uppercase first letter - but that is the extent of its involvement with the Business class. You notice it doesn't ask for information about the name, address, or number for a particular business. It just takes a string you give it, does something to it, and gives it back. It doesn't matter whether you have no Businesses or a hundred.
To call this method, you'd use:
System.out.println(Business.stringToUpperCase("hello"));
This would print Hello.
If it were not a static method, you'd have to make a new Business first:
Business b = new Business("aName", "aNumber", "anAddress");
System.out.println(b.stringToUpperCase("hello"));
And if the method did need access to more Business-instance information (like a business's name number or address) it wouldn't be able to be an instance variable.
The first example, a method without a return type at all, is a constructor; used when an instance is created with new. However, you can't return a value from a constructor. Something like,
this.amount = initial * interest; // return amount;
Sets the field amount to initial * interest.
Let's assume I have an Employee base class and Manager subclass which extends Employee.Now let's say I create an object x of type Employee and object y of type Manager and call x.compareTo(y) no exception is triggered and x and y is compared as Employees namely y is cast to an Employee but when I call y.compareTo(x) I get a classCastException.I need to know why this happens and how to prevent x.compareTo(y) to execute as x and y are from different classes.My idea is to use getclass() method in Reflection class like this:
if (getClass() != other.getClass())
throw new ClassCastException();
I also want to know is there any other way to implement this.
You should implement compareTo() in the class Employee and start it with:
Employee o = (Employee)other;
Then continue with comparing this to o - this will ensure you're comparing two Employees (which is the lowest common denominator).
Because your Manager is an Employee but Employee is not a Manager See below
http://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html
instance of can be usefull in such cases
here Manager is a Employee.
but Employee is not Manager.
Quote from Effective Java, Item 12:
Let’s go over the provisions of the compareTo contract. The first provision says that if you reverse the direction of a comparison between two object refer- ences, the expected thing happens: if the first object is less than the second, then the second must be greater than the first; if the first object is equal to the second, then the second must be equal to the first; and if the first object is greater than the second, then the second must be less than the first. The second provision says that if one object is greater than a second, and the second is greater than a third, then the first must be greater than the third. The final provision says that all objects that compare as equal must yield the same results when compared to any other object.
One consequence of these three provisions is that the equality test imposed by acompareTo method must obey the same restrictions imposed by the equals con- tract: reflexivity, symmetry, and transitivity. Therefore the same caveat applies: there is no way to extend an instantiable class with a new value component while preserving the compareTo contract, unless you are willing to forgo the benefits of object-oriented abstraction (Item 8). The same workaround applies, too. If you want to add a value component to a class that implements Comparable, don’t extend it; write an unrelated class containing an instance of the first class. Then provide a “view” method that returns this instance. This frees you to implement whatever compareTo method you like on the second class, while allowing its cli- ent to view an instance of the second class as an instance of the first class when needed.
All Manager are Employee but not all Employee are Managers. Since all the attributes of Employee are available in Manager,Manager can be casted to Employee. But attributes of Manager is unavailable to Employee, so cast is not possible.
My suggestion is to override compareTo() method in your classes and cast the object Employee.
If your are using compareTo method then i am excepting you have implemented Comparable interface in your class and provide a implementation of the method compareTo. let me know how you are comparing object on what logic ,based on that only you get the solution.
I have little bit confuse on this
if (getClass() != other.getClass())
throw new ClassCastException();
if it is the code in your compareTo method then rather then doing this create one more interface say "XYZ" and implement that Interface to both the class
check the logic
public int compareTo(T obj){
if(this instanceof XYZ && obj instanceof XYZ)){
return 0;
}else{
throw new ClassCastException();
}
}
You could perhaps use isAssignableFrom which will return true or false and then use it for doing further comparison or equals etc. Not sure why you would need this in compareTo; however.
Anyways assuming name , salary for an employee and set of reportees for manager and then for example further just comparing salaries as part of compareTo.
public class Test{
public static void main(String[] args) {
class Employee implements Comparable<Employee> {
public Employee(String string, int salary) {
this.name = string;
this.salary = salary;
}
public Employee() {
name = "";
salary = 0;
}
String name;
Integer salary;
public int compareTo(Employee o) {
return o!=null && getClass().isAssignableFrom(Employee.class)
? salary.compareTo(o.salary) : Integer.MIN_VALUE;
}
}
class Manager extends Employee {
public Manager(String name, String[] subordinates) {
super(name, 1000000);
reportees = subordinates;
}
String[] reportees;
}
Employee e = new Employee("me", 1000);
Employee e1 = new Employee("mycolleague", 2000);
Manager m = new Manager("myboss", "me mycolleague".split(" "));
System.out.println(e1.compareTo(e));
System.out.println(e.compareTo(m));
System.out.println(m.compareTo(e)); // this gives INT.MIN as you cannot compare manager to employee
}
}
I am quite new to the concepts and pretty naive user so please excuse me for the following question,but
I am trying to understand the basic concepts of collection in java
I have made the following class
package com.vish;
public class HashSetDemo {
private int age;
public HashSetDemo(int age) {
this.age = age;
}
}
Now here I am having set collection framework described in my following class
package com.vish;
import java.util.HashSet;
public class HashSetDemo1 {
public static void main(String args[]) {
HashSetDemo hsd = new HashSetDemo(23);
HashSetDemo hsd1 = new HashSetDemo(24);
HashSet<HashSetDemo> hashset = new HashSet<HashSetDemo>();
hashset.add(hsd);
hashset.add(hsd1);
System.out.println(hashset.size());
System.out.println(hashset.contains(hsd));
System.out.println(hashset.contains(new HashSetDemo(23)));
}
}
Now the outut of this is following
2
true
false
Why is the last one false,when it has the same object reference
Thanks
Why is the last one false,when it has the same object reference
It doesn't. You've created a new object which happens to have the same value for age.
It's like asking a builder to build you two houses with 5 bedrooms. Yes, they look the same - but they're different houses, with different addresses.
Now HashSet actually doesn't for equal references - it checks for equal objects - where equality is determined via the hashCode and equals methods. By default, this checks for reference identity, but it doesn't have to. So if you override equals and hashCode to determine equality just your age value, then it would consider your new object equal to the old one.
public final class HashSetDemo {
private final int age;
public HashSetDemo(int age) {
this.age = age;
}
#Override public int hashCode() {
return age;
}
#Override public boolean equals(Object other) {
if (!(other instanceof HashSetDemo)) {
return false;
}
HashSetDemo otherDemo = (HashSetDemo) other;
return age == otherDemo.age;
}
}
Because you have not implemented equals() in your HashSetDemo class. If you don't do that, then java can't figure out how to tell if two objects are equal. It does have a default implementation though, and that default implementation is to ask, "Are these two objects the same reference?"
Since you are explicitly creating a new HashSetDemo, Java uses the default equals() and says, "no, these are not the same instance of HashSetDemo"
Because you have not provided a custom equals and hashCode method for your class. Your class uses the implementations provided by Object.
If you overrode equals to be return this.age == ((HashSetDemo)other).age and overrode hashCode to return a hash value derived from age, then your last call to hashset.contains would return True.
Its not the same reference. In second case, you are creating new instance of HashSetDemo, and that has different address in memory.
Your HashSet is using default comparator for searching, and that compares instances of objects, not their content.
Why is the last one false,when it has the same object reference
Even though the objects are exactly the same, they aren't the same objects. It's like putting one brand new bicycle in a garage, getting another brand new bicycle exactly the same as the other, and asking the garage if it contains the second bike. Sure, the bikes may be equal, but they're not the same.
Good question, by the way.
You need to override equals() and hashCode() in HashSetDemo. This tells your program how to determine whether 2 separate instances are equal. If you don't, your program will fall back to the default implementation, which only checks the object reference. In your third line, the object is equivalent, but Java doesn't know that - it only knows that its a different reference.
In the last case you have created new Object new HashSetDemo(23) It will store in different location in the java heap memory. It is different compare to the other two objects hsd and hsd1.
Hello
if you search in an HashMap<String,String> for a specific value of a key-value-pair, you can write the following:
myHashMap.containsKey(myString);
But how can I manage it if the key is not a string? I have a class which looks like this:
public class Kategorie implements Comparable {
private String name;
public Kategorie() {
super();
}
public Kategorie(String name) {
setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public int compareTo(Object o) {
if (!(o instanceof Kategorie))
throw new ClassCastException();
Kategorie k = (Kategorie)o;
String name = k.getName();
return this.getName().compareTo(name);
}
}
In a map I saved keys and values of this type "Kategorie".
mapKategorieDEundEN.put(new Kategorie(strName_de), new Kategorie(strName_en));
Later in the code, I want to check if there is a key with a specific string.
if (mapKategorieDEundEN.containsKey(searchString)) {
...doesn't work, because the key is not a string but a "Kategorie", that's clear.
Then I tried something like this:
if (mapKategorieDEundEN.containsKey(new Kategorie(searchString))) {
...doesn't work too. I assume that it doesn't find anything because the object is not the "original" object but a new one.
In this case, can I use containsKey at all or do I have to use a loop over the HashMap?
You class should override equals and hashCode, it will work after that.
The HashMap/Hashtable puts the items in "buckets" by using the hashCode of the key, so a new object that represents the same value as another object, and which should be considered as the same object must return the same hashCode. All keys that return the same hashCode will then be considered as candidates, and equals will be invoked on them. It's considered a match if equals returns true.
HashMap uses hashCode() and equals(). You have to implement them. If you don't know how. Check your IDE (eclipse) usually can generate them for you.
If you want to access your objects using your compareTo method, you should not use a hashCode/equals based Map, but a SortedMap, like TreeMap (or ConcurrentSkipListMap).
This has the added benefit that it enables range-based queries (e.g. "give me all categories larger than this one"), but is a bit slower (O(log n) instead of O(1)) for simple get accesses compared to hash-based access (with a good hash code, not a constant one).
For a general use class, defining both hashCode/equals and compareTo would be sensible, then the user of the class can decide which type of map to use. (If there are different ways to sort your objects, better provide different Comparator objects.)
As a side remark, you should not implement Comparable, but Comparable<Kategorie>. Then your compareTo method would look like this:
public int compareTo(Kategorie k) {
String name = k.getName();
return this.getName().compareTo(name);
}