java集合 之 多列集合

目录

引言

一、Map接口概述

二、常见方法

[1、put(K key,V value)](#1、put(K key,V value))

[2、get(Object key)](#2、get(Object key))

[3、remove(Object key)](#3、remove(Object key))

[4、containsKey(Object key)](#4、containsKey(Object key))

[5、containsValue(Object value)](#5、containsValue(Object value))

6、size()

7、isEmpty()

9、keySet()

10、values()

11、entrySet()

[三、迭代器 Iterator](#三、迭代器 Iterator)

1、核心方法:

①hasNext()

②next()

③remove()

2、使用模式

3、不同集合的迭代器

[① List 的迭代器](#① List 的迭代器)

[② Set 的迭代器](#② Set 的迭代器)

[③ Map 的迭代器](#③ Map 的迭代器)

[④ ListIterator 的特殊功能](#④ ListIterator 的特殊功能)

[四、Map 的主要实现类](#四、Map 的主要实现类)

1、HashMap------对应HashSet

2、Hashtable------对应Vector

3、TreeMap------对应TreeSet

4、LinkedHashMap------对应LinkedHashSet


前面我们提到了单列集合:

Java集合 之 单列集合-CSDN博客https://blog.csdn.net/qq_73698057/article/details/150275046?spm=1001.2014.3001.5502

下面我们接着来看多列集合

引言

想象一下

1990年代初的Java开发团队正在设计一个图书馆管理系统

他们需要一种数据结构来存储"书名-位置"这样的键值对关系

如果使用List,每次查找都需要遍历整个列表

如果使用Set,又只能存储单一值(去重)

于是,他们创造了Map接口------一种革命性的双列集合

就像图书馆的目录系统一样

通过书名(键)快速找到书架位置(值)

这个设计灵感来源于现实世界中的字典、电话簿、目录索引等

它们都遵循"通过一个标识符查找相关信息"的模式

一、Map接口概述

Map 是一种双列集合,用于存储键值对(key - value)

特点:

  1. 存储元素时,必须以键值对的方式进行

  2. 键key:键是唯一的,每个键只能对应一个值

值value:值可以重复,不同的键可以关联相同的值

  1. 查找:高效的,通过唯一的键值可以快速查到对应的value值

  2. 底层:哈希表或者红黑树等 实现的

好,废话不多说

Map的学习思路和前面单列集合差不多

我们直接来看它有什么方法

二、常见方法

1、put(K key,V value)

方法声明:

V put(K key,V value)

解释:

泛型中常见的类型参数命名约定:

K - Key(键)

V - Value(值)

E - Element(元素)

T - Type(类型)

N - Number(数字)

在 Map 中,V 代表值的类型,示例:

java 复制代码
import java.util.*;

public class GenericTypeExample {
    public static void main(String[] args) {
        // Map<String, Integer> 中:
        // K = String(键的类型)
        // V = Integer(值的类型)
        Map<String, Integer> scoreMap = new HashMap<>();
        
        // put 方法:V = Integer
        Integer oldValue = scoreMap.put("张三", 95); // V 作为参数和返回值类型
        
        // get 方法:V = Integer
        Integer score = scoreMap.get("张三"); // V 作为返回值类型
        
        // remove 方法:V = Integer
        Integer removedValue = scoreMap.remove("张三"); // V 作为返回值类型
        
        // values 方法:返回 Collection<V>,这里 V = Integer
        Collection<Integer> scores = scoreMap.values(); // V 作为集合元素类型
    }
}

参数:

**key:**要关联的键,不能为null(某些实例例外)

value: 与键关联的值,可以为null

返回值:

返回被覆盖的值

如果 key 之前没有映射(即对应的值),则返回null

如果 key 已经存在,则返回被覆盖的之前的值

作用:

将指定的 键值对 添加到 Map 中

如果 键 已存在则替换旧值

**是否改变原始值:**

注意事项:

键必须唯一,重复的键会覆盖原有值

多个键可以映射到同一个值

java 复制代码
import java.util.*;

public class PutMethodExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        
        // 第一次添加,返回null
        Integer oldValue1 = map.put("Apple", 5);
        System.out.println("第一次添加Apple: " + oldValue1); // null
        
        // 再次添加相同键,返回旧值
        Integer oldValue2 = map.put("Apple", 10);
        System.out.println("第二次添加Apple: " + oldValue2); // 5
        System.out.println("当前Apple的值: " + map.get("Apple")); // 10
    }
}

2、get(Object key)

方法声明:

V get(Object key)

参数:

key:要 获取 的键

返回值:

如果该键有映射的值,返回对应的值

如果没有,返回null

作用:

返回指定键所映射的 值

是否改变原始值:否

注意事项:

返回 null 可能表示键不存在,也可能表示键映射到 null 值

需要区分这两种情况的话,用 containsKey 方法(下面会提到)

java 复制代码
import java.util.*;

public class GetMethodExample {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("name", "张三");
        map.put("nullValue", null); // 显式存储null值
        
        // 获取存在的键
        String name = map.get("name");
        System.out.println("name的值: " + name); // 张三
        
        // 获取不存在的键
        String notExist = map.get("age");
        System.out.println("不存在的键: " + notExist); // null
        
        // 获取显式存储为null的键
        String nullValue = map.get("nullValue");
        System.out.println("存储为null的值: " + nullValue); // null
        
        // 如何区分"键不存在"和"键映射到null"
        System.out.println("键是否存在: " + map.containsKey("age")); // false
        System.out.println("键是否存在: " + map.containsKey("nullValue")); // true
    }
}

3、remove(Object key)

方法声明:

V remove(Object key)

参数:

key:要 移除 的键

返回值:

如果该键有映射的值,返回被移除的 值

如果没有,则返回 null

作用:

从 Map 中移除指定键的映射关系,即删除这个 键值对

是否改变原始值:是

注意事项:

返回值同样存在 null 歧义问题

java 复制代码
import java.util.*;

public class RemoveMethodExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("Apple", 5);
        map.put("Banana", 3);
        map.put("Orange", null); // 存储null值
        
        // 移除存在的键
        Integer removedValue = map.remove("Apple");
        System.out.println("移除的值: " + removedValue); // 5
        System.out.println("移除后Map大小: " + map.size()); // 2
        
        // 移除不存在的键
        Integer notExist = map.remove("Grape");
        System.out.println("移除不存在的键: " + notExist); // null
        
        // 移除存储为null的键
        Integer nullRemoved = map.remove("Orange");
        System.out.println("移除null值: " + nullRemoved); // null
        System.out.println("移除后Map大小: " + map.size()); // 1
    }
}

4、containsKey(Object key)

方法声明:

boolean containsKey(Object key)

参数:

key:要 检查 的键

返回值:

如果 Map 包含指定键的映射关系,返回 true

否则返回 false

作用:

检查 Map 是否包含指定键

是否改变原始值:否

注意事项:

时间复杂度O(1)

java 复制代码
import java.util.*;

public class ContainsKeyMethodExample {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("name", "张三");
        map.put("address", null); // 显式存储null值
        
        // 检查存在的键
        System.out.println("包含name键: " + map.containsKey("name")); // true
        
        // 检查不存在的键
        System.out.println("包含age键: " + map.containsKey("age")); // false
        
        // 检查存储为null的键
        System.out.println("包含address键: " + map.containsKey("address")); // true
        
        // 正确处理get()返回null的情况
        String value = map.get("someKey");
        if (map.containsKey("someKey")) {
            System.out.println("键存在,值为: " + value);
        } else {
            System.out.println("键不存在");
        }
    }
}

5、containsValue(Object value)

方法声明:

boolean containsValue(Object value)

参数:

value:要 检查 的 值

返回值:

如果 Map 将一个或多个键映射到指定值,返回 true

否则返回 false

作用:

检查 Map 是否包含指定值

是否改变原始值:否

注意事项:

需要遍历所有值,时间复杂度O(n)

底层使用equals方法比较值

java 复制代码
import java.util.*;

public class ContainsValueMethodExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("Apple", 5);
        map.put("Banana", 3);
        map.put("Orange", 5); // 重复的值
        
        // 检查存在的值
        System.out.println("包含值5: " + map.containsValue(5)); // true
        
        // 检查不存在的值
        System.out.println("包含值10: " + map.containsValue(10)); // false
        
        // 多个键映射到同一个值
        System.out.println("值5出现次数: 多于1次");
    }
}

6、size()

方法声明:

int size()

参数:

返回值**&**作用:

返回 Map 中键值对的数量

是否改变原始值:否

注意事项:

空 Map 的 size 为0

挺简单的懒得写了

7、isEmpty()

方法声明:

boolean isEmpty()

参数:

返回值:

如果 Map不包含键值对,返回 true

否则返回 false

作用:

检查 Map 是否为空

是否改变原始值:否

注意事项:

等价于 size() == 0

8、clear()

方法声明:

void clear()

参数:

返回值:

void

作用:

移除 Map 中所有的键值对

是否改变原始值:是

注意事项:

执行后 Map 为空

不会将 Map 引用设为 null

java 复制代码
import java.util.*;

public class ClearMethodExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("A", 1);
        map.put("B", 2);
        map.put("C", 3);
        
        System.out.println("清除前大小: " + map.size()); // 3
        System.out.println("清除前是否为空: " + map.isEmpty()); // false
        
        map.clear();
        
        System.out.println("清除后大小: " + map.size()); // 0
        System.out.println("清除后是否为空: " + map.isEmpty()); // true
        
        // Map引用仍然有效,只是内容被清空
        map.put("D", 4);
        System.out.println("清空后重新添加: " + map.size()); // 1
    }
}

9、keySet()

方法声明:

Set<K> keySet()

参数:

返回值:

Map 中所有键的 Set 视图

(就是把 Map 里的所有键拿出来,放到 Set 里给你看)

作用:

返回 Map 中所有键的集合

是否改变原始值:否

注意事项:

返回的 Set 与原来的 Map关联,修改会影响原来的 Map

不能通过 keySet 添加元素

可以通过 keySet 移除元素

切记不是在遍历里修改,遍历里修改元素用 Iterator

下面介绍完三个获取视图的方法再说这个问题

java 复制代码
import java.util.*;

public class KeySetMethodExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("Apple", 5);
        map.put("Banana", 3);
        map.put("Orange", 8);
        
        // 获取所有键
        Set<String> keys = map.keySet();
        System.out.println("所有键: " + keys); // [Apple, Banana, Orange]
        
        // 遍历所有键
        for (String key : keys) {
            System.out.println(key + " -> " + map.get(key));
        }
        
        // 通过keySet移除元素会影响原Map
        keys.remove("Apple");
        System.out.println("移除Apple后Map: " + map); // {Banana=3, Orange=8}
        
        // 不能通过keySet添加元素
        // keys.add("Grape"); // UnsupportedOperationException
    }
}

10、values()

方法声明:

Collection<V> values()

参数:

返回值:

Map 中所有值的 Collection 视图

作用:

返回 Map 中所有值的集合

是否改变原始值:否

注意事项:

返回的 Collection 与原来的 Map关联

不能通过 values 添加或移除元素

值可以重复

java 复制代码
import java.util.*;

public class ValuesMethodExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("Apple", 5);
        map.put("Banana", 3);
        map.put("Orange", 5); // 重复的值
        map.put("Grape", 3);   // 重复的值
        
        // 获取所有值
        Collection<Integer> values = map.values();
        System.out.println("所有值: " + values); // [5, 3, 5, 3]
        
        // 统计值的出现次数
        Map<Integer, Integer> countMap = new HashMap<>();
        for (Integer value : values) {
            countMap.put(value, countMap.getOrDefault(value, 0) + 1);
        }
        System.out.println("值的统计: " + countMap); // {3=2, 5=2}
    }
}

11、entrySet()

方法声明:

Set<Map.Entry<K,V>> entrySet()

参数:

返回值:

Map 中所有键值对的 Set 视图

作用:

返回 Map 中所有键值对的集合

是否改变原始值:否

注意事项:

Map.Entry 是键值对的表示

可以通过 Entry 修改值

是遍历 Map 最高效的方式

java 复制代码
import java.util.*;

public class EntrySetMethodExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("Apple", 5);
        map.put("Banana", 3);
        map.put("Orange", 8);
        
        // 获取所有键值对
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        
        // 遍历键值对(最高效的方式)
        for (Map.Entry<String, Integer> entry : entries) {
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        }
        
        // 通过Entry修改值
        for (Map.Entry<String, Integer> entry : entries) {
            if (entry.getKey().equals("Apple")) {
                entry.setValue(10); // 修改值
            }
        }
        
        System.out.println("修改后Map: " + map); // {Apple=10, Banana=3, Orange=8}
    }
}

好,我们来补充一下 Iterator

三、迭代器 Iterator

主要是用来用统一的方式遍历不同类型的集合

是集合框架中的一种设计模式

可以想象成一个指针

它在集合的元素之间移动

逐个访问每个元素

1、核心方法:

①hasNext()

方法声明:

boolean hasNext()

返回值:

true:还有下一个元素

false:已经到达集合末尾

作用:

检查是否还有下一个元素

②next()

方法声明:

E next()

返回值:

集合中的下一个元素

作用:

返回下一个元素,并将迭代器向前移动一位

③remove()

方法声明:

void remove()

作用:

删除上一次 next() 返回的元素

注意事项:

必须在调用 next() 之后才能调用

每次 next() 调用之后只能调用一次

这是遍历过程中唯一安全的修改集合的方式

java 复制代码
import java.util.*;

public class RemoveExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));
        System.out.println("原始列表: " + list);
        
        Iterator<String> iterator = list.iterator();
        
        while (iterator.hasNext()) {
            String element = iterator.next();
            if ("B".equals(element) || "D".equals(element)) {
                iterator.remove(); // 安全删除
            }
        }
        
        System.out.println("删除后列表: " + list); // [A, C]
    }
}

2、使用模式

java 复制代码
import java.util.*;

public class BasicIteratorPattern {
    public static void main(String[] args) {
        Collection<String> collection = Arrays.asList("Apple", "Banana", "Orange");
        
        // 1. 获取迭代器
        Iterator<String> iterator = collection.iterator();
        
        // 2. 遍历元素的标准模式
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);
        }
    }
}

3、不同集合的迭代器

回到之前的问题

keySet、values、entrySet这哥仨是可以调用迭代器在遍历里修改元素的

为什么可以呢?

注意看他们仨的方法声明

两个返回Set,而Set继承自Collection

一个返回Collection

而Collection 实现了Iterable接口

所以可以调用 Iterator

① List 的迭代器

java 复制代码
import java.util.*;

public class ListIteratorExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
        
        // 普通 Iterator
        Iterator<String> iterator = list.iterator();
        System.out.println("普通迭代器遍历:");
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        
        // ListIterator(功能更强大)
        ListIterator<String> listIterator = list.listIterator();
        System.out.println("\nListIterator 正向遍历:");
        while (listIterator.hasNext()) {
            int index = listIterator.nextIndex();
            String element = listIterator.next();
            System.out.println("索引 " + index + ": " + element);
        }
        
        System.out.println("\nListIterator 反向遍历:");
        while (listIterator.hasPrevious()) {
            int index = listIterator.previousIndex();
            String element = listIterator.previous();
            System.out.println("索引 " + index + ": " + element);
        }
    }
}

② Set 的迭代器

java 复制代码
import java.util.*;

public class SetIteratorExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>(Arrays.asList("Apple", "Banana", "Orange"));
        
        // Set 的迭代器(无序)
        Iterator<String> iterator = set.iterator();
        System.out.println("HashSet 元素:");
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

③ Map

Map本身不可以使用迭代器,原因:

  1. Map接口没有实现Iterable接口
  2. Map是键值对的映射关系,不是线性集合结构
  3. 没有直接的iterator()方法

所以 Map 就用刚才那哥仨简介调用迭代器

java 复制代码
import java.util.*;

public class MapIteratorExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("Apple", 5);
        map.put("Banana", 3);
        map.put("Orange", 8);
        
        // Map 本身没有 Iterator,但可以通过视图获取
        System.out.println("通过 keySet 遍历:");
        Iterator<String> keyIterator = map.keySet().iterator();
        while (keyIterator.hasNext()) {
            String key = keyIterator.next();
            System.out.println(key + " -> " + map.get(key));
        }
        
        System.out.println("\n通过 entrySet 遍历:");
        Iterator<Map.Entry<String, Integer>> entryIterator = map.entrySet().iterator();
        while (entryIterator.hasNext()) {
            Map.Entry<String, Integer> entry = entryIterator.next();
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        }
    }
}

④ ListIterator 的特殊功能

ListIterator 是 Iterator 的子接口,专门为 List 设计,提供更多功能:

java 复制代码
import java.util.*;

public class ListIteratorFeatures {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
        System.out.println("原始列表: " + list);
        
        ListIterator<String> listIterator = list.listIterator();
        
        // 1. 正向遍历
        System.out.println("正向遍历:");
        while (listIterator.hasNext()) {
            int index = listIterator.nextIndex();
            String element = listIterator.next();
            System.out.println("索引 " + index + ": " + element);
        }
        
        // 2. 反向遍历
        System.out.println("\n反向遍历:");
        while (listIterator.hasPrevious()) {
            int index = listIterator.previousIndex();
            String element = listIterator.previous();
            System.out.println("索引 " + index + ": " + element);
        }
        
        // 3. 在当前位置添加元素
        listIterator.next(); // 移动到第一个元素
        listIterator.add("X"); // 在当前位置前添加
        System.out.println("\n添加X后: " + list); // [X, A, B, C]
        
        // 4. 设置当前元素
        listIterator.next(); // 移动到A
        listIterator.set("Y"); // 替换A为Y
        System.out.println("替换后: " + list); // [X, Y, B, C]
    }
}

四、Map 的主要实现类

1、HashMap------对应HashSet

底层:

哈希表

特点:

无序

存储顺序不保证一致

键唯一,所以key的自定义类要注意hashCode和equals方法的重写

值可以重复

线程不安全

null:

key 可以为null,只能有一个

value 可以为null,可以有多个

注意:

HashMap在添加值的时候先调用hashCode方法,再调用equals方法
HashSet底层用到了HashMap

java 复制代码
import java.util.*;

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        
        // HashMap允许null键和null值
        map.put(null, 1); // null键
        map.put("nullValue", null); // null值
        map.put(null, 2); // 覆盖null键的值
        
        System.out.println("HashMap: " + map);
        System.out.println("null键的值: " + map.get(null)); // 2
    }
}

2、Hashtable------对应Vector

底层:

哈希表

特点:

存储顺序不保证一致

键唯一,所以key的自定义类要注意hashCode和equals方法的重写

值可以重复

线程安全

null:

key 和 value 都不能为null

java 复制代码
import java.util.*;

public class HashtableExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new Hashtable<>();
        
        map.put("Apple", 1);
        map.put("Banana", 2);
        
        // Hashtable不允许null键和null值
        // map.put(null, 1); // NullPointerException
        // map.put("nullValue", null); // NullPointerException
        
        System.out.println("Hashtable: " + map);
    }
}

3、TreeMap------对应TreeSet

底层:

红黑树

实现了 SortedMap 接口,所以元素的顺序会自动排序

特点:

有序

对key值排序,自然排序和比较器排序(就近原则:比较器排序 > 自然排序)

键唯一,所以key的自定义类要注意hashCode和equals方法的重写

值可以重复

线程不安全,可以通过其他方式解决线程安全问题

null:

key 不能为null,会抛出空指针异常

value 可以为null,可以有多个

java 复制代码
import java.util.*;

public class TreeMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new TreeMap<>();
        
        map.put("Banana", 2);
        map.put("Apple", 1);
        map.put("Cherry", 3);
        
        // 自动按键排序
        System.out.println("TreeMap: " + map); // {Apple=1, Banana=2, Cherry=3}
    }
}

4、LinkedHashMap------对应LinkedHashSet

底层:

哈希表 和 双向链表

特点:

按照存入顺序排序

键唯一,所以key的自定义类要注意hashCode和equals方法的重写

值可以重复

线程不安全,可以通过其他方式解决线程安全问题

null:

key 能为null,只能有一个

value 可以为null,可以有多个

java 复制代码
import java.util.*;

public class LinkedHashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new LinkedHashMap<>();
        
        map.put("First", 1);
        map.put("Second", 2);
        map.put("Third", 3);
        
        // 保持插入顺序
        System.out.println("LinkedHashMap: " + map); // {First=1, Second=2, Third=3}
        
        // 访问不会改变顺序
        map.get("Second");
        System.out.println("访问后: " + map); // 顺序不变
    }
}
相关推荐
多读书19314 分钟前
Java多线程进阶-深入synchronized与CAS
java·开发语言·java-ee
AAA修煤气灶刘哥21 分钟前
Lombok坑哭了!若依框架一行@Data炸出Param为null,我卡了一下午才发现BaseEntity的猫腻
java·后端
啊阿狸不会拉杆32 分钟前
《算法导论》第 24 章 - 单源最短路径
开发语言·数据结构·c++·算法·php
衍余未了33 分钟前
Centos9傻瓜式linux部署CRMEB 开源商城系统(PHP)
开发语言·php
SimonKing1 小时前
手搓MCP客户端动态调用多MCP服务,调用哪个你说了算!
java·后端·程序员
AI偶然1 小时前
AI智能体|扣子(Coze)搭建【批量识别发票并录入飞书】Agent
经验分享
xzkyd outpaper1 小时前
Kotlin 协程启动方式
android·开发语言·kotlin
集成显卡1 小时前
在JVM跑JavaScript脚本 | 简单 FaaS 架构设计与实现
开发语言·javascript·jvm·设计模式·kotlin·软件开发·faas
数据熊猫Taobaoapi20141 小时前
JavaScript 实现模块懒加载的几种方式
开发语言·javascript·ecmascript