Java 给hashMap中添加类元素,然后去重,再排序输出

70 阅读3分钟

上代码!

import java.util.*;

public class hashSetExample {
    /*
    * 如果我们只需要存储不重复的key,并不需存储映射的 value,可以使用 set
    * set 用于存储不重复的元素集合,主要提供以下几个方法:
    *   - 将元素添加进Set<E>: boolean add(E e)
    *   - 将元素从 Set<E> 删除:boolean remove(Object e)
    *   - 判断是否包含元素:Boolean contains(Object e)
    * */

    public static void main(String[] args) {

        /*
        * Set 接口并不保证有序,而 SortedSet 接口则保证元素是有序的
        *   - HashSet 是无序的,因为它实现了 set 接口,并没有实现 sortedset 接口
        *   - TreeSet 是有序的,因为它实现了 sortedset 接口
        * */

        /*
        * 练习:在聊天软件中,发送放发送消息时,遇到网络超时后会自动重发,因此,接收方可能会收到重复的消息,在显示给用户看的时候,
        * 需要首先去重。使用 set 去重
        * */
        List<Message> received = new ArrayList<>();
        received.add(new Message(1,"今天是几月几号啊?"));
        received.add(new Message(2,"今天是20240121"));
        received.add(new Message(3,"你们什么时候放假"));
        received.add(new Message(4,"请假两天吧,2月"));
        received.add(new Message(4,"请假两天吧,2月"));
        received.add(new Message(4,"请假两天吧,2月"));
        received.add(new Message(5,"那就是明天开始抢票了"));

        // 这里最开始发现一个问题就是,ArrayList 转 hashSet 的时候,因为作为key 是一个类,所以转成 hashSet 之后并没有去重
        // 原因是:作为key的类没有实现 equals 和 hashCode  方法,
        // 一定要记住!!! 作为key 的类如果没有实现这两个方法,这两个方法在 hashSet 中用于判断元素的唯一性。没有实现则无法去重
        HashSet<Message> SetRecevied = new HashSet<>(received);

//        SetRecevied.addAll(received);
        for (Message msg:SetRecevied){
            System.out.println(msg.toString());
        }

        System.out.println("-----------------分割线------------------");

        /*
        * 下面是将上面的无需set集合(去重过的),进行排序,再进行输除
        * 思路:hashSet > ArrayList > Collection.sort(ArrayList对象) 进行排序
        *       注意: 这里要给Message类实现一个 Comparator 接口,MyClassComparator类。
        *             这个类是另外定义的,不能在Message类中进行定义。
        * */
        List<Message> setList = new ArrayList<>(SetRecevied);

        Comparator<Message> myClassComparator = new MyClassComparator();
        Collections.sort(setList,myClassComparator);

        for(Message msg : setList){
            System.out.println(msg.toString());
        }

    }
}


class Message{
    public final int sequence;
    public final String text;

    Message(int sequence, String text) {
        this.sequence = sequence;
        this.text = text;
    }

    public String toString(){
        return this.sequence + "_" + this.text;
    }

    @Override
    public int hashCode(){
        // 选择一个初始值,这个值要是质数,质数17最常用,表现也最好。因为质数具有良好的分布特性
        int result = 17;
        // 乘以31是为了避免相邻的哈希值太相似
        result = 31 * result + sequence;
        result = 31 * result + (text == null ? 0 : text.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj){
        if (this == obj){
            return true;
        }
        if (obj == null || getClass() != obj.getClass()){
            return false;
        }
        // 将对象转换为相应的类
        Message msg = (Message) obj;
        // 根据类的实际需求,比对类的各个属性是否相等。
        return sequence == msg.sequence && text.equals(msg.text);
    }
    /*
    * 关于以上 hashCode  和 equals 方法实现:
    * hashCode:
    *   1、初始值 result :哈希码的初始值,通常选择一个质数,这里是17,因17表现良好。
    *   2、乘以 31 :选择 31 是因为它是一个奇质数,而且 31 * result 可以被 JVM 优化为 (result << 5) - result,这样可以更高效地计算。
    *   3、属性 name 的哈希码:使用三元运算符 (text == null ? 0 : text.hashCode()) 来获取属性 name 的哈希码。如果 name 为null,则使用 0 作为哈希码,否则使用 name 的哈希码。
    *   4、组合到最终的哈希码中:将前面计算得到的结果与属性 name 的哈希码相加,得到最终的哈希码,并将其复制给 result。
    * */

}


/*
* 需要单独创建一个类,实现Comparator 接口,不能在被实例的类中进行实现Comparator 方法。
* */
class MyClassComparator implements Comparator<Message>{
    @Override
    public int compare(Message obj1,Message obj2){
        return Integer.compare(obj1.sequence,obj2.sequence);
    }
}