Java - Comparison of Java Objects

1. Asking questions

In the last lesson, we talked about the priority queue. The priority queue has a requirement when inserting elements: the inserted element must not be null or the elements must be able to
be compared
. For the sake of simplicity, we just inserted the Integer type, which takes precedence. Can a custom type object be inserted into the level queue?

class Card {
    
    
    public int rank; //数值
    public String suit; //花色
    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)); 空指针异常
    }
    public static void main(String[] args) {
    
    
        TestPriorityQueue();
    }
}

The bottom layer of the priority queue uses the heap, and when inserting elements into the heap, in order to satisfy the nature of the heap, the elements must be compared. At this time, there is no way for Card
to compare directly, so an exception is thrown.
insert image description here

2. Comparison of elements

2.1 Comparison of basic types

In Java, objects of primitive 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; // 数值
	public String suit; // 花色
	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); //编译报错
	System.out.println(c1 == c2); //编译成功 ---->打印false,因为c1和c2指向的是不同对象
	//System.out.println(c1 < c2); //编译报错
	System.out.println(c1 == c3); //编译成功 ---->打印true,因为c1和c3指向的是同一个对象
	}
}

c1, c2 and c3 are reference variables of Card type respectively. When comparing the above code when compiling:
c1 > c2 Compilation fails
c1== c2 Compilation succeeds
c1 < c2 Compilation fails
It can be seen from the compilation results that reference type variables in Java cannot Compare directly according to the > or < method. so whycan be compared?
Because: For user-defined types, they all inherit from the Object class by default, and the equal method is provided in the Object class , and
By default
, the equal method is called, but the comparison rule of this method is: instead of comparing the content of the reference variable reference object, it directly compares the
address
of the reference variable , but in some cases this comparison does not meet the meaning of the question.

// Object中equal的实现,可以看到:直接比较的是两个引用变量的地址
public boolean equals(Object obj) {
    
    
	return (this == obj);
}

3. Comparison of objects in PriorityQueue

In some cases, the content of the object needs to be compared. For example, when an object is inserted into the priority queue, the
heap needs to be adjusted according to the content of the object. How to deal with it?

The comparison method of PriorityQueue in the collection framework The bottom layer of PriorityQueue in the collection framework uses a heap structure, so its internal elements must be able to compare the size, PriorityQueue adopts:

Comparble and Comparator two ways.

  1. Comparble is the default internal comparison method. If the user inserts an object of a custom type, the object of this class must implement the Comparble interface and override the compareTo method.
  2. The user can also choose to use a comparator object. If the user inserts a custom type object, he must provide a comparator class that implements the Comparator interface and overrides the compare method.

3.1 Overriding the equal of the base class

class Card{
    
    
    public int rank; // 数值
    public String suit; // 花色
    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;
        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);
    }
}
    public static void main(String[] args) {
    
    
        Card card1 = new Card(1,"♥");
        Card card2 = new Card(1,"♥");
        System.out.println(card1.equals(card2));
    }
//进行内容是否相同的比较,结果为:true

Note : The general routine for overriding equals is as demonstrated above

  1. Returns true if it points to the same object
  2. If the passed in is null, return false
  3. If the passed object type is not Card, return false
  4. The comparison is done according to the realization goal of the class. For example, as long as the suit and value are the same, it is considered to be the same card.
  5. Note that calling comparisons of other reference types also requires equals, such as the comparison of suits here

Although the method of overriding the base class equal can be compared, the disadvantage is: equal can only be compared according to equality, and cannot be compared according to greater than or less than.

3.2 Comparison based on Comparble interface class

Comparble is a generic comparison interface class provided by JDK. The source code implementation is as follows:

public interface Comparable<E> {
    
    
	//返回值:
	// < 0:表示 this指向的对象小于 o指向的对象
	// == 0:表示 this指向的对象等于 o指向的对象
	// > 0:表示 this指向的对象等于 o指向的对象
	int compareTo(E o);
}

For user-defined types, if you want to compare by size and method: when defining a class, implement the Comparble interface, and then override the compareTo method in the class.
When rewriting, it this.rank - o.rankis a small heap, o.rank - this.ranka large heap
insert image description here

    @Override
    public int compareTo(Card o) {
    
    
        return  this.rank - o.rank  ;
    }

Comparable is an interface class in java.lang that can be used directly.

class Card implements Comparable<Card>{
    
    
    public int rank; // 数值
    public String suit; // 花色
    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 + '\'' +
                '}';
    }
}
public class TestDemo {
    
    
    public static void main3(String[] args) {
    
    
        Card card1 = new Card(1,"♥");
        Card card2 = new Card(2,"♥");

        //默认就是一个小根堆
        PriorityQueue<Card> priorityQueue = new PriorityQueue<>();
        priorityQueue.offer(card1);//直接放到底层的queue数组的0下标
        priorityQueue.offer(card2);

        System.out.println(priorityQueue);
        System.out.println(card1.compareTo(card1));

    }
}

3.3 Comparison based on Comparator interface class

Compare according to the comparator method, the specific steps are as follows

  • User-defined comparator class that implements the Comparator interface
public interface Comparator<T> {
    
    
	//返回值:
	// < 0:表示 o1指向的对象小于 o2指向的对象
	// == 0:表示 o1指向的对象等于 o2指向的对象
	// > 0:表示 o1指向的对象等于 o2指向的对象
	int compare(T o1, T o2);
}

Note : Distinguish Comparable and Comparator.

  • Override the compare method in Comparator
class Card {
    
    
    public int rank; // 数值
    public String suit; // 花色
    public Card(int rank, String suit) {
    
    
        this.rank = rank;
        this.suit = suit;
    }

    @Override
    public String toString() {
    
    
        return "Card{" +
                "rank=" + rank +
                ", suit='" + suit + '\'' +
                '}';
    }
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        Card card1 = new Card(1,"♥");
        Card card2 = new Card(2,"♥");
        //方法一:
        RankComparator rankComparator = new RankComparator();
        PriorityQueue<Card> priorityQueue = new PriorityQueue<>(rankComparator);
        //方法二:
//        PriorityQueue<Card> priorityQueue = new PriorityQueue<>(new Comparator<Card>() {
    
    
//            @Override
//            public int compare(Card o1, Card o2) {
    
    
//                return o1.rank-o2.rank;
//            }
//        });
        //方法三:  lambda表达式:可读性差
//        PriorityQueue<Card> priorityQueue = new PriorityQueue<>((x,y)->{return y.rank-x.rank;});

        priorityQueue.offer(card1);//直接放到底层的queue数组的0下标
        priorityQueue.offer(card2);
        System.out.println(priorityQueue);

        int ret = rankComparator.compare(card1,card2);
        System.out.println(ret);
    }
}

Note : Comparator is a generic interface class in the java.util package, and the corresponding package must be imported when using it.

3.4 Comparison of three methods

insert image description here

4. Implementation of PriorityQueue in JDK

// JDK中PriorityQueue的实现:
public class PriorityQueue<E> extends AbstractQueue<E>
	implements java.io.Serializable {
    
    
	// ...
	//默认容量
	private static final int DEFAULT_INITIAL_CAPACITY = 11;
	//内部定义的比较器对象,用来接收用户实例化PriorityQueue对象时提供的比较器对象
	private final Comparator<? super E> comparator;
	//用户如果没有提供比较器对象,使用默认的内部比较,将comparator置为null
	public PriorityQueue() {
    
    
		this(DEFAULT_INITIAL_CAPACITY, null);
		}
	//如果用户提供了比较器,采用用户提供的比较器进行比较
	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;
	}
	// ...
	//向上调整:
	//如果用户没有提供比较器对象,采用Comparable进行比较
	//否则使用用户提供的比较器对象进行比较
	private void siftUp(int k, E x) {
    
    
		if (comparator != null)
			siftUpUsingComparator(k, x);
	else
		siftUpComparable(k, x);
	}
	//使用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;
	}
	//使用用户提供的比较器对象进行比较
	@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 generic PriorityQueue that can be compared in the way of Comparble and Comparator objects.

Guess you like

Origin blog.csdn.net/qq_43398758/article/details/122892628