Collection集合和Map集合的知识梳理

本文详细介绍了Java集合框架中HashSet和TreeSet的区别与应用场景。HashSet基于哈希表实现,无序不可重复,存储自定义对象需重写hashCode()和equals()。TreeSet使用红黑树,无序可排序,不可重复,需要实现Comparable接口或传入Comparator。此外,还对比了ArrayList、LinkedList和Vector等List类型的特性。对于Map集合,HashMap和TreeMap分别展示了其存储和遍历方式,强调了排序和唯一性的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Set集合的特点:无序不可重复

HashSet集合:无序不可重复,可以存储 null 对象

A:底层代码是哈希表(动态数组)+链表+红黑树

B:存储自定义对象,需要重写 hashCode() 方法和 equals() 方法

通过 HashSet 存储对象,会根据对象的 hashCode() 生成hash值,根据 hash 生成索引,根据索引找到 hash 表中的位置;判断位置上是否有对象,没有对象直接插入,有对象则通过 equals() 判断内容是否相同,如果相同则不插入,不同则根据链表往下检索,直到重复或者链表没有下一个对象,准备插入最后的时候判断链表是否超过 8 个对象,当链表超过 8 个对象时,总储存的对象大于64则把链表转换成红黑树,当 hash 表总储存的对象大于负载因子值(0.75),则会进行数组的扩容,扩容为原本的 2 倍

TreeSet集合:无序可排序,不可重复,不能存储 null 对象的集合

A:底层代码是红黑树(是一个平衡的二叉树结构)

B:使用 TreeSet 存储对象,对象类要么实现自然排序,要么传递比较器

自然排序:存储进 TreeSet 集合的对象类必须实现 Comparable 接口,重写 comparaTo(),在 ComparaTo() 里制定排序规则,返回正数存储的对象往右子树方向,返回负数存储的对象往左子树方向,返回0则不存储

比较器:在创建TreeSet 集合对象的时候,传递 Comparator 接口的实现类对象,重写 Comparator 接口的 compare() ,在 compare() 里制定排序规则

 总结:TreeSet 和 HashSet 的使用场景具体看题目要求是否需要排序,HashSet性能优于 TreeSet 能够节省内存,需要排序则选择 TreeSet 

 Collection集合的总结:

List:有序,可重复

        ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高

        LinkedList:底层数据结构是链表,查询慢,增删快,线程不安全,效率高

        Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低

Set:无序不重复

        HashSet:底层数据结构是哈希表,依赖 hashCode() 和 equals() 保证元素的唯一性

        TreeSet:底层数据结构是红黑树,使用自然排序或者传递比较器实现元素的排序,根据底层代码比较的返回值是否为 0 来决定元素的唯一性

针对Collection集合我们到底使用谁呢?

唯一吗?

        是:Set

                排序吗?

                        是:TreeSet

                        否:HashSet

如果你知道是 Set,但是不知道是哪个 Set,就用 HashSet

        否:List

                要安全吗?

                        是:Vector

                        否:ArrayList 或者 LinkedList

                                查询多:ArrayList

                                增删多:LinkedList

如果你知道是List,但是不知道是哪个List,就用 ArrayList,如果你知道是Collection集合,但是不知道使用谁,就用 ArrayList,如果你知道用集合,就用 ArrayList

 

Map集合

Map 集合与 Collection 集合在集合框架中属于并列存在,Map 中的元素是两个对象,一个对象为键值,另一个对象为 value 值。键值不可以重复,但是 value 值可以重复,因此 Map 集合可以用来存储多个对象的集合。

常见的方法:添加(map.put())和 删除(map.remove())

HashMap:

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Demo01 {
    public static void main(String[] args) {
        HashMap<String,Integer> map = new HashMap<>();
        map.put("zs",20);
        map.put("ls",18);
        map.put("ww",18);
        map.put("z6",22);
        map.put("t7",24);
        map.put("ls",20);
        System.out.println("------KeySet()------")
        //map.keySet() 返回 HashMap 的键值
        Set<String> set = map.keySet();
        //迭代器获取 set 集合的 key
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            String key = it.next();
            Integer value = map.get(key);
            System.out.println(key + ":" + value);
        }
            System.out.println("------entrySet()-------");
            //把Entry存放到Set集合中
            Set<Map.Entry<String, Integer>> set1 = /*把key和value存放到Entry对象中*/map.entrySet();
            //迭代Set集合获取Entry对象
            Iterator<Map.Entry<String, Integer>> it1 = set1.iterator();
            while(it1.hasNext()){
                Map.Entry<String, Integer> entry = it1.next();
                System.out.println(entry.getKey()+":"+entry.getValue());
            }
    }
}
import java.util.HashMap;

public class Demo02 {
    public static void main(String[] args) {
        HashMap<Person, User> map = new HashMap<>();
        map.put(new Person(1),new User(1));
        map.put(new Person(2),new User(2));
        map.put(new Person(1),new User(2));
        map.put(new Person(2),new User(1));
        map.put(new Person(1),new User(1));
        System.out.println(map);
    }
}

//-----------------类分割线------------------

import java.util.Objects;

public class Person {
    private int id;

    public Person(int id) {
        this.id = id;
    }

    public Person() {
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return id == person.id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                '}';
    }
}

//--------------------类分割线------------------------

public class User {
    private int id;

    public User() {
    }

    public User(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                '}';
    }
}

遍历出来的结果只有两个对象,并且在Person类id相同的情况下,后添加的会覆盖前面添加的

TreeMap:

import java.util.TreeMap;

public class Demo02 {
    public static void main(String[] args) {
        TreeMap<User,String> map = new TreeMap<>();
        map.put(new User(1),"zs");
        map.put(new User(5),"ls");
        map.put(new User(3),"ww");
        map.put(new User(4),"z6");
        map.put(new User(2),"t7");
        //id从小到大排序
        System.out.println(map);
    }
}

//------------------类分割线------------------

public class User implements Comparable<User>{
    private int id;

    public User() {
    }

    public User(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                '}';
    }

    @Override
    public int compareTo(User user) {
        return this.getId() - user.getId();
    }
}

结论:无论 TreeMap 还是 TreeSet 需要排序则都要通过重写 compareTo(), HashMap 还是 HashSet 需要排序则都要通过重写 hashCode() 和 equals() 方法来实现

<think>好的,我现在需要帮助用户解答关于Java基础知识中的Map集合的问题。首先,我要回顾用户之前的对话历史,了解他们之前遇到了什么问题。之前的对话中,用户询问了一段使用HashMap的代码为什么无法正常输出企鹅信息,我指出是因为没有重写toString方法,并给出了改进建议。现在用户的新问题是关于Java Map集合的基础知识,他们可能想系统地了解Map的相关内容。 首先,我需要确认用户的需求是什么。他们可能刚开始学习Java,对集合框架中的Map不太熟悉,需要基础知识的讲解。或者他们在实际编程中遇到了Map相关的问题,需要进一步理解其特性使用方法。根据用户的问题“java基础知识map集合”,可以推断他们希望获得一个概述性的介绍,包括Map的基本概念、常用实现类、核心方法、遍历方式以及使用注意事项。 接下来,我需要组织回答的结构。通常,基础知识介绍需要清晰的分点,涵盖定义、特点、常用实现类、核心方法、遍历方式、使用场景、注意事项等。同时,用户之前的代码示例中使用了HashMapIterator遍历,因此在讲解遍历方式时,可以提到不同的方法,包括Iterator增强型for循环,这有助于用户将新旧知识联系起来。 在回答中需要注意用户可能的盲点。例如,用户之前的问题涉及到toString方法的重写,可能在自定义对象作为Value时需要注意正确实现equalshashCode方法,这也是Map使用中的一个重要点,需要提及。此外,线程安全的问题,比如HashMap非线程安全,而Hashtable是线程安全的,但ConcurrentHashMap更高效,这些高级内容虽然基础,但可以简要提到,帮助用户建立全面的认识。 同时,用户可能对Map的各个实现类的区别不太清楚,比如HashMap、LinkedHashMap、TreeMap之间的不同,需要明确它们的特点适用场景。例如,HashMap无序,LinkedHashMap保持插入顺序,TreeMap可排序,这些都需要用简洁的例子或对比表格来说明。 还要注意使用用户之前接触过的代码示例中的元素,比如HashMap的entrySet方法遍历,这样用户更容易理解。在讲解遍历方式时,可以提到用户之前使用的Iterator方式,并推荐更简洁的增强型for循环,这样既巩固旧知识,又引入新建议。 另外,用户可能对泛型的使用有疑问,之前的代码中已经正确使用了泛型,因此在注意事项中可以强调类型安全的重要性,并建议始终使用泛型,避免原始类型,这有助于预防类型转换错误。 最后,总结部分需要简明扼要地回顾Map的核心知识点,并鼓励用户进行实践。考虑到用户之前的问题可能是在实际编码中遇到的,强调实践的重要性可以帮助他们更好地掌握这些概念。 在编写过程中,要确保语言通俗易懂,避免过于专业的术语,但必要的术语仍需准确使用。同时,检查是否有遗漏的重要知识点,如Map的键唯一性、值可重复、允许空键空值(根据实现类不同)等,这些都需要涵盖在内。 总结下来,回答的结构应该是: 1. Map的定义与特点 2. 常用实现类对比 3. 核心方法 4. 遍历方式 5. 使用场景 6. 注意事项 7. 总结 每个部分需要简明扼要,举例说明,并尽量联系用户之前的代码示例,帮助他们更好地理解。同时,注意代码示例的正确格式,使用中文注释,帮助用户理解每段代码的作用。</think>以下是Java中Map集合的核心知识体系梳理: 一、Map的定义与特点 1. 双列集合结构:存储键值对(key-value)数据,数学表示为$K \rightarrow V$的映射关系 2. 三大核心特性: - 键唯一性:每个key对应唯一value(类似函数定义域) - 值可重复:不同key可以对应相同value - 键值对存取:通过key快速定位value(时间复杂度接近$O(1)$) 二、常用实现类对比 | 类名 | 数据结构 | 顺序特性 | 线程安全 | 允许null键值 | 时间复杂度 | |-----------------|-----------|-----------------|--------|------------|----------| | HashMap | 数组+链表/红黑树 | 无序 | 不安全 | 允许 | O(1) | | LinkedHashMap | 链表+哈希表 | 保持插入/访问顺序 | 不安全 | 允许 | O(1) | | TreeMap | 红黑树 | 按键自然排序 | 不安全 | 不允许 | O(log n) | | Hashtable | 数组+链表 | 无序 | 安全 | 不允许 | O(1) | | ConcurrentHashMap| 分段锁机制 | 无序 | 安全 | 不允许 | O(1) | 三、核心方法详解 ```java // 基础操作 V put(K key, V value); // 添加/替换键值对 V get(Object key); // 根据key获取value boolean containsKey(Object key);// 检查key是否存在 V remove(Object key); // 删除指定key的映射 // 集合视图 Set<K> keySet(); // 获取所有key的集合 Collection<V> values(); // 获取所有value的集合 Set<Map.Entry<K,V>> entrySet(); // 获取所有键值对集合 // 特殊操作 default V getOrDefault(Object key, V defaultValue); // 安全获取值 void forEach(BiConsumer<? super K,? super V> action); // Java8遍历 ``` 四、遍历方式对比(以HashMap为例) 1. EntrySet迭代器遍历(用户示例中的方式) ```java Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator(); while(it.hasNext()) { Map.Entry<Integer, String> entry = it.next(); System.out.println(entry.getKey() + ":" + entry.getValue()); } ``` 2. 增强for循环遍历(推荐方式) ```java for(Map.Entry<Integer, String> entry : map.entrySet()) { System.out.println(entry.getKey() + "->" + entry.getValue()); } ``` 3. Lambda表达式遍历(Java8+) ```java map.forEach((k, v) -> System.out.println(k + "=" + v)); ``` 五、典型使用场景 1. 数据缓存:使用HashMap实现快速查询 2. 配置存储:Properties类继承Hashtable 3. 数据统计:用TreeMap实现有序统计 4. 并发场景:ConcurrentHashMap处理高并发访问 六、注意事项 1. 键对象必须正确重写`hashCode()``equals()`方法 2. 初始容量与负载因子设置(默认0.75,空间与时间的权衡) 3. 并发修改异常:遍历时修改集合可能抛出ConcurrentModificationException 4. 使用泛型保证类型安全(如`Map<String, Integer>`) 七、底层原理简析(以HashMap为例) 1. 哈希桶结构:数组的每个元素称为桶(bucket) 2. 哈希冲突处理: - 链表法:JDK1.8前使用纯链表 - 红黑树:当链表长度>8时转换(提升查询效率) 3. 扩容机制:当size > capacity*loadFactor时,扩容为原来2倍 建议通过调试工具观察HashMap的结构变化,结合《Effective Java》中关于equals/hashCode的实现规范,可以深入理解Map集合的设计哲学。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值