Comparison of java objects
Objectives of this section
- Raising questions
- Comparison of elements
- Comparison of objects in Java
- Comparison method of PriorityQueue in collection framework
- Simulate the implementation of PriorityQueue
1. Questions raised
Last class, we talked about priority queue. When inserting elements into priority queue, there is a requirement: the inserted elements cannot be null or must be able to be inserted between elements
For comparison, for simplicity, we only inserted Integer type. Can we insert custom type objects in the priority queue?
class Card { public int rank; //numerical value public String suit; //Decor public Card(int rank, String suit) { this.rank = rank; this.suit = suit; } } public class TestPriorityQueue { public static void TestPriorityQueue() { PriorityQueue<Card> p = new PriorityQueue<>(); p.offer(new Card(1, "♠")); p.offer(new Card(2, "♠")); //p.offer(new Card(null)); Null pointer exception } public static void main(String[] args) { TestPriorityQueue(); } }
The bottom layer of the priority queue uses the heap. When inserting elements into the heap, in order to meet the nature of the heap, the elements must be compared. At this time, the Card does not do anything
Method is directly compared, so an exception is thrown.
2. Comparison of elements
2.1 comparison of basic types
In Java, objects of basic types can be directly compared in size.
public class TestCompare { public static void main(String[] args) { int a = 10; int b = 20; System.out.println(a > b); System.out.println(a < b); System.out.println(a == b); char c1 = 'A'; char c2 = 'B'; System.out.println(c1 > c2); System.out.println(c1 < c2); System.out.println(c1 == c2); boolean b1 = true; boolean b2 = false; System.out.println(b1 == b2); System.out.println(b1 != b2); } }
2.2 comparison of objects
class Card { public int rank; // numerical value public String suit; // Decor public Card(int rank, String suit) { this.rank = rank; this.suit = suit; } } public class TestPriorityQueue { public static void main(String[] args) { Card c1 = new Card(1, "♠"); Card c2 = new Card(2, "♠"); Card c3 = c1; //System. out. println(c1 > c2); // Compilation error System.out.println(c1 == c2); //Compilation succeeded ---- > Print false because c1 and c2 point to different objects //System. out. println(c1 < c2); // Compilation error System.out.println(c1 == c3); //Compilation succeeded ----- > Print true because c1 and c3 point to the same object } }
c1, c2 and c3 are reference variables of Card type respectively. When comparing and compiling the above code:
C1 > C2 compilation failed
c1== c2 compiled successfully
C1 < C2 compilation failed
It can be seen from the compilation resu lt s that variables of reference type in Java cannot be directly compared in the way of > or < directly. Then why can we compare?
Because: for user-defined types, they inherit from the Object class by default, and the equal method is provided in the Object class, which is lowered by default
The equal method is used, but the comparison rule of this method is: instead of comparing the contents of the reference object of the reference variable, the place of the reference variable is directly compared
But in some cases, the comparison is not in line with the meaning of the question.
// The implementation of equal in Object shows that the addresses of two reference variables are directly compared public boolean equals(Object obj) { return (this == obj); }
3. Comparison of objects
In some cases, what needs to be compared is the content of the object. For example, when inserting an object into the priority queue, it needs to be adjusted according to the content of the object
Heap, how to deal with it?
TestDemo.java
import java.util.Comparator; import java.util.Objects; import java.util.PriorityQueue; /** * Created with IntelliJ IDEA. * User: 12629 * Date: 2022/1/22 * Time: 9:49 * Description: */ /*class Card implements Comparable<Card>{ public int rank; // numerical value public String suit; // Decor public Card(int rank, String suit) { this.rank = rank; this.suit = suit; } @Override public int compareTo(Card o) { return this.rank - o.rank ; } @Override public String toString() { return "Card{" + "rank=" + rank + ", suit='" + suit + '\'' + '}'; } }*/ class Card{ public int rank; // numerical value public String suit; // Decor public Card(int rank, String suit) { this.rank = rank; this.suit = suit; } @Override public String toString() { return "Card{" + "rank=" + rank + ", suit='" + suit + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; //getClass() != o.getClass() compares whether it is the same type if (o == null || getClass() != o.getClass()) return false; Card card = (Card) o; return rank == card.rank && Objects.equals(suit, card.suit); } @Override public int hashCode() { return Objects.hash(rank, suit); }//Don't care } /*class RankComparator implements Comparator<Card> { @Override public int compare(Card o1, Card o2) { return o1.rank-o2.rank; } }*/ public class TestDemo { public static void main(String[] args) { Card card1 = new Card(1,"♥"); Card card2 = new Card(1,"♥"); System.out.println(card1.equals(card2)); } public static void main4(String[] args) { Card card1 = new Card(1,"♥"); Card card2 = new Card(2,"♥"); /*PriorityQueue<Card> priorityQueue = new PriorityQueue<>(new Comparator<Card>() { @Override public int compare(Card o1, Card o2) { return o1.rank-o2.rank; } });*/ PriorityQueue<Card> priorityQueue = new PriorityQueue<>((x,y)->{return y.rank-x.rank;}); priorityQueue.offer(card1);//Directly put the 0 subscript of the underlying queue array priorityQueue.offer(card2); System.out.println(priorityQueue); } /* public static void main4(String[] args) { Card card1 = new Card(1,"♥"); Card card2 = new Card(2,"♥"); RankComparator rankComparator = new RankComparator(); PriorityQueue<Card> priorityQueue = new PriorityQueue<>(rankComparator); priorityQueue.offer(card1);//Directly put the 0 subscript of the underlying queue array priorityQueue.offer(card2); System.out.println(priorityQueue); }*/ public static void main3(String[] args) { Card card1 = new Card(1,"♥"); Card card2 = new Card(2,"♥"); //The default is a small root heap /* PriorityQueue<Card> priorityQueue = new PriorityQueue<>(); priorityQueue.offer(card1);//Directly put the 0 subscript of the underlying queue array priorityQueue.offer(card2); System.out.println(priorityQueue);*/ //System.out.println(card1.compareTo(card1)); /*RankComparator rankComparator = new RankComparator(); int ret = rankComparator.compare(card1,card2); System.out.println(ret);*/ }
3.1 override equal of base class
public class Card { public int rank; //numerical value public String suit; //Decor public Card(int rank, String suit) { this.rank = rank; this.suit = suit; } @Override public boolean equals(Object o) { //Compare yourself with yourself if (this == o) { return true; } // o if it is a null object, or o is not a subclass of Card if (o == null || !(o instanceof Card)) { return false; } //Note that basic types can be compared directly, but it is better for reference types to call their equal method Card c = (Card)o; return rank == c.rank && suit.equals(c.suit); } }
Note: the general routine of overriding equals is demonstrated above
- Returns true if it points to the same object
- If the passed in is null, false is returned
- If the object type passed in is not Card, false is returned
- Complete the comparison according to the realization goal of the class. For example, as long as the color and value are the same, it is considered to be the same card
- Note that equals is also required to call the comparison of other reference types, such as the comparison of suit here
Although the methods of overriding the base class equal can be compared, the defect is that equal can only be compared according to equality, not greater than or less than
compare
3.2 comparison based on Comparble interface classes
Comparble is a generic comparison interface class provided by JDK. The source code implementation is as follows:
public interface Comparable<E> { //Return value: // < 0: indicates that the object pointed to by this is less than the object pointed to by o // ==0: indicates that the object pointed to by this is equal to the object pointed to by o // >0: indicates that the object pointed to by this is equal to the object pointed to by o int compareTo(E o); }
For user-defined types, if you want to compare them by size and mode: when defining a class, you can implement the Comparble interface, and then
Override the compareTo method in.
Comparable is Java Lang, which can be used directly.
public class Card implements Comparable<Card> { public int rank; //numerical value public String suit; //Decor public Card(int rank, String suit) { this.rank = rank; this.suit = suit; } //According to the numerical comparison, regardless of design and color //Here we think null is the smallest @Override public int compareTo(Card o) { if (o == null) { return 1; } return rank - o.rank; } public static void main(String[] args){ Card p = new Card(1, "♠"); Card q = new Card(2, "♠"); Card o = new Card(1, "♠"); System.out.println(p.compareTo(o)); // ==0 indicates that the cards are equal System.out.println(p.compareTo(q));// < 0 indicates that p is relatively small System.out.println(q.compareTo(p));// >0 indicates that q is relatively large } }
3.3 comparator based comparison
Compare according to the comparator mode, and the specific steps are as follows
- User defined Comparator class to implement Comparator interface
public interface Comparator<T> { //Return value: // < 0: indicates that the object pointed to by o1 is smaller than the object pointed to by o2 // ==0: indicates that the object pointed to by o1 is equal to the object pointed to by o2 // >0: indicates that the object pointed to by o1 is equal to the object pointed to by o2 int compare(T o1, T o2); }
Note: distinguish between Comparable and Comparator.
- Override compare method in Comparator
import java.util.Comparator; class Card { public int rank; //numerical value public String suit; //Decor public Card(int rank, String suit) { this.rank = rank; this.suit = suit; } } class CardComparator implements Comparator<Card> { //According to the numerical comparison, regardless of design and color //Here we think null is the smallest @Override public int compare(Card o1, Card o2) { if (o1 == o2) { return 0; } if (o1 == null) { return -1; } if (o2 == null) { return 1; } return o1.rank - o2.rank; } public static void main(String[] args){ Card p = new Card(1, "♠"); Card q = new Card(2, "♠"); Card o = new Card(1, "♠"); //Define comparator object CardComparator cmptor = new CardComparator(); //Compare using comparator objects System.out.println(cmptor.compare(p, o)); // ==0 indicates that the cards are equal System.out.println(cmptor.compare(p, q)); // < 0 indicates that p is relatively small System.out.println(cmptor.compare(q, p)); // >0 indicates that q is relatively large } }
Note: Comparator is Java The generic interface class in util package must be imported into the corresponding package.
3.4 comparison of three methods
4. Comparison method of PriorityQueue in the collection framework. The bottom layer of PriorityQueue in the collection framework uses heap structure, so its internal elements must be able to compare the size. PriorityQueue adopts:
Comparble and Comparator.
- Comparble is the default internal comparison method. If users insert custom type objects, such objects must implement comparble connection
And override the compareTo method - You can also choose to use the comparator object. If you insert a custom type object, you must provide a comparator class to implement it
Comparator interface and override the compare method.
// Implementation of PriorityQueue in JDK: public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable { // ... //Default capacity private static final int DEFAULT_INITIAL_CAPACITY = 11; //The internally defined comparator object is used to receive the comparator object provided when the user instantiates the PriorityQueue object private final Comparator<? super E> comparator; //If the user does not provide a comparator object, the default internal comparison is used and the comparator is set to null public PriorityQueue() { this(DEFAULT_INITIAL_CAPACITY, null); } //If the user provides a comparator, use the comparator provided by the user for comparison public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) { // Note: This restriction of at least one is not actually needed, // but continues for 1.5 compatibility if (initialCapacity < 1) throw new IllegalArgumentException(); this.queue = new Object[initialCapacity]; this.comparator = comparator; } // ... //Upward adjustment: //If the user does not provide a comparator object, use Comparable for comparison //Otherwise, the user supplied comparator object is used for comparison private void siftUp(int k, E x) { if (comparator != null) siftUpUsingComparator(k, x); else siftUpComparable(k, x); } //Using Comparable @SuppressWarnings("unchecked") private void siftUpComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>) x; while (k > 0) { int parent = (k - 1) >>> 1; Object e = queue[parent]; if (key.compareTo((E) e) >= 0) break; queue[k] = e; k = parent; } queue[k] = key; } //Compare using a user supplied comparator object @SuppressWarnings("unchecked") private void siftUpUsingComparator(int k, E x) { while (k > 0) { int parent = (k - 1) >>> 1; Object e = queue[parent]; if (comparator.compare(x, (E) e) >= 0) break; queue[k] = e; k = parent; } queue[k] = x; } }
5. Simulate the implementation of PriorityQueue
Students refer to the following code to simulate and implement a general PriorityQueue that can be compared in the way of Comparble and comparator objects.