JAVA集合框架二:Map接口及子实现类,Collections工具类

目录

上一篇:Collection(LIst,Set)和Iterator(迭代器)

一、Map接口概述

二、特点

三、Map中key-value特点

四、常用方法

五、键值遍历 

六、Map的主要实现类:HashMap

1.特点

2.实例:将省份和城市的名称保存在集合中,当用户选择省份以后,二级联动,显示对应省份的地级市供用户选择。

 七、Map实现类之二:LinkedHashMap

1.特点

 八、Map实现类之三:TreeMap

1.特点

自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException

定制排序:创建 TreeMap 时,构造器传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口

九、Map实现类之四:Hashtable

特点

 Hashtable和HashMap的区别:

十、Map实现类之五:Properties

特点:

示例: 

 十一、Collections工具类

排序操作:

 例如:编写一个程序,对用户输入的 5 个商品价格进行排序后输出。这里要求使用 Collections 类中 sort() 方法按从低到高的顺序对其进行排序,最后将排序后的成绩输出。

查找操作:

 复制、替换、添加操作:

同步操作:

模拟洗牌打牌小游戏: 

总结:


上一篇:Collection(LIst,Set)和Iterator(迭代器)

一、Map接口概述

  • Map与Collection并列存在。用于保存具有映射关系的数据:key-value

    • Collection集合称为单列集合,元素是孤立存在的(理解为单身)。

    • Map集合称为双列集合,元素是成对存在的(理解为夫妻)。

  • Map 中的 key 和 value 都可以是任何引用类型的数据。

二、特点

  • 高效的查找操作:Map提供了根据键快速查找对应值的方法,例如get(key)方法。

  • 动态可变大小:Map的大小是可变的,可以根据需要动态地添加、修改和删除键值对。

  • 顺序性:Map的实现类可以根据插入顺序或其他规则来维护键值对的顺序。例如,LinkedHashMap会根据插入顺序或访问顺序保持键值对的顺序,而HashMap则没有顺序保证。

  • 迭代:Map提供了遍历所有键值对的方法,例如keySet()、entrySet()和values()等,通过这些方法可以获取Map中的键、键值对或值的集合,进行遍历和操作。

  • 多种实现:Java提供了多个Map接口的实现类,包括HashMap、TreeMap、LinkedHashMap等,每个实现类都有自己的特点和适用场景。

三、Map中key-value特点

  1. 键值对映射:Map接口用于存储一组键值对,其中每个键都是唯一的,并且每个键都与一个值相关联。

  2. 键的唯一性:Map中的键是唯一的,没有重复的键。如果多次插入相同的键,则后面的键值对会覆盖之前的键值对

  3. 值的重复性:Map中的值可以重复,可以使用不同的键关联相同的值

四、常用方法

操作类型 方法名 描述
添加、修改操作 Object put(Object key,Object value) 将指定key-value添加到(或修改)当前map对象中
void putAll(Map m) 将m中的所有key-value对存放到当前map中
删除操作 Object remove(Object key) 移除指定key的key-value对,并返回value
void clear() 清空当前map中的所有数据
元素查询的操作 Object get(Object key) 获取指定key对应的value
boolean containsKey(Object key) 是否包含指定的key
boolean containsValue(Object value) 是否包含指定的value
int size() 返回map中key-value对的个数
boolean isEmpty() 判断当前map是否为空
boolean equals(Object obj) 判断当前map和参数对象obj是否相等
元视图操作的方法 Set keySet() 返回所有key构成的Set集合
Collection values() 返回所有value构成的Collection集合
Set entrySet() 返回所有key-value对构成的Set集合

五、键值遍历 

public class Test {
    public static void main(String[] args) {
        Map studentMap = new HashMap<>();
        studentMap.put("Alice", 18);
        studentMap.put("Bob", 20);
        studentMap.put("Charlie", 19);
        studentMap.put("Alice", 21);

        System.out.println("使用keySet()遍历键:");
        for (String name : studentMap.keySet()) {
            System.out.println("Name: " + name );
            //根据建就能得到值
            //System.out.println("Name: " + name + ", Age: " + studentMap.get(name));
        }

        System.out.println("使用values()遍历值:");
        for (Integer age : studentMap.values()) {
            System.out.println("Age: " + age);
        }

        System.out.println("使用entrySet()遍历键值对:");
        for (Map.Entry entry : studentMap.entrySet()) {
            String name = entry.getKey();
            Integer age = entry.getValue();
            System.out.println("Name: " + name + ", Age: " + age);
        }

        System.out.println("使用迭代器遍历键值对:");
        Iterator> iterator = studentMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry entry = iterator.next();
            String name = entry.getKey();
            Integer age = entry.getValue();
            System.out.println("Name: " + name + ", Age: " + age);
        }

        System.out.println("使用Lambda表达式遍历:");
        studentMap.forEach((name, age) -> {
            System.out.println("Name: " + name + ", Age: " + age);
        });
    }
}

 输出:

使用keySet()遍历键:
Name: Bob
Name: Alice
Name: Charlie
使用values()遍历值:
Age: 20
Age: 21
Age: 19
使用entrySet()遍历键值对:
Name: Bob, Age: 20
Name: Alice, Age: 21
Name: Charlie, Age: 19
使用迭代器遍历键值对:
Name: Bob, Age: 20
Name: Alice, Age: 21
Name: Charlie, Age: 19
使用Lambda表达式遍历:
Name: Bob, Age: 20
Name: Alice, Age: 21
Name: Charlie, Age: 19

六、Map的主要实现类:HashMap

1.特点

它有Map的所以特点,另外:

  1. 无序性: HashMap中的键值对没有固定的顺序,不保证插入顺序和存储顺序一致。

  2. 允许为空: HashMap中的键和值都可以为null。但是只能有一个键为null,因为HashMap的键是唯一的。

  3. 线程不安全: HashMap不是线程安全的,多线程环境下需要自行实现同步机制。

  4. 高效性能: 在大多数情况下,HashMap的插入、删除和查找操作的时间复杂度为O(1)。但是在极端情况下可能会退化为O(n)。

  5. 存储数据采用的哈希表结构,底层使用一维数组+单向链表+红黑树进行key-value数据的存储

 2.实例:将省份和城市的名称保存在集合中,当用户选择省份以后,二级联动,显示对应省份的地级市供用户选择。

public class Test {
    public static void main(String[] args) {
        for(Object s : CityMap.model.keySet()) {
            System.out.print(s + " ");
        }
        System.out.println();
        System.out.println("请选择你所在的省份:");
        Scanner scan = new Scanner(System.in);
        String province = scan.next();

        String[] citys = (String[])CityMap.model.get(province);
        for(String city : citys) {
            System.out.print(city + " ");
        }
        System.out.println();
        System.out.println("请选择你所在的城市:");
        String city = scan.next();

        System.out.println("信息登记完毕");
    }
}

黑龙江 上海 吉林 天津 河北 重庆 北京 
请选择你所在的省份:
黑龙江
哈尔滨 齐齐哈尔 牡丹江 大庆 伊春 双鸭山 绥化 

  • 请选择你所在的城市:

大庆
信息登记完毕

 七、Map实现类之二:LinkedHashMap

1.特点

  • LinkedHashMap 是 HashMap 的子类(具有HashMap的特点)

  • 顺序一致:存储数据采用的哈希表结构+链表结构,在HashMap存储结构的基础上,使用了一对双向链表记录添加元素的先后顺序,可以保证遍历元素时,与添加的顺序一致。

 八、Map实现类之三:TreeMap

1.特点

  • 键的有序性:TreeMap根据键的自然顺序或自定义比较器来维护键值对的有序性。
  • 自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException

public class Test {
    public static void main(String[] args) {
        TreeMap map = new TreeMap();

        map.put("CC", 45);
        map.put("MM", 44);
        map.put("DD", 56);
        map.put("GG", 19);
        map.put("JJ", 99);

        Set entrySet = map.entrySet();
        for (Object entry : entrySet) {
            System.out.println(entry);
        }
    }
}
  • 定制排序:创建 TreeMap 时,构造器传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口

public class Test {
    public static void main(String[] args) {
        //按照User的姓名的从小到大的顺序排列

        TreeMap map = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof User && o2 instanceof User){
                    User u1 = (User)o1;
                    User u2 = (User)o2;

                    return u1.name.compareTo(u2.name);
                }
                throw new RuntimeException("输入的类型不匹配");
            }
        });

        map.put(new User("Tom",12),67);
        map.put(new User("Rose",23),"87");
        map.put(new User("Jerry",2),88);
        map.put(new User("Eric",18),45);

        Set entrySet = map.entrySet();
        for(Object entry : entrySet){
            System.out.println(entry);
        }
    }
}
  • 高效的查找操作:由于TreeMap基于红黑树,查找操作的时间复杂度为O(log n),因此查找特定键的值非常高效。这使得TreeMap在需要频繁进行查找操作的场景下具有较好的性能。

  • 支持范围查询:TreeMap提供了一些方法来支持范围查询,如subMap()headMap()tailMap()。这些方法允许按范围获取子映射或头部/尾部映射。

九、Map实现类之四:Hashtable

特点

  1. 线程安全性:Hashtable是线程安全的,多个线程可以同时访问Hashtable并进行读写操作,因此在多线程环境下可以直接使用Hashtable而无需担心并发问题。Hashtable通过采用同步机制来实现线程安全。

  2. 不允许null键或null值:Hashtable不允许存储null键或null值,如果尝试存储null键或null值,将会抛出 NullPointerException 异常。

  3. 效率较低:由于Hashtable是线程安全的,它使用了同步的机制来保证线程安全性。然而,这也导致了Hashtable的性能相对较低,特别是在高并发环境下。

  4. Hashtable是Map接口的古老实现类,JDK1.0就提供了。

 Hashtable和HashMap的区别:

  1. 线程安全性:Hashtable是线程安全的,多个线程可以同时访问Hashtable并进行读写操作,而HashMap不是线程安全的。如果需要在多线程环境下使用哈希表,可以选择Hashtable来保证线程安全性。

  2. 允许null键或null值:Hashtable不允许存储null键或null值,如果尝试存储null键或null值,将会抛出NullPointerException异常。而HashMap允许存储一个null键和多个null值。

  3. 效率:由于Hashtable是线程安全的,它使用了同步机制来保证线程安全性。然而,这也导致了Hashtable的性能相对较低,特别是在高并发环境下。而HashMap不需要进行同步操作,因此具有更高的性能。

  4. 迭代器:在Java 8及以后的版本中,HashMap提供了更快速的迭代器(Iterator)和遍历方式,而Hashtable则较旧的Java版本只提供了Enumeration迭代器。

综上所述,Hashtable和HashMap在线程安全性、允许null键值、性能和迭代器方面存在一些差异。如果不需要考虑线程安全和允许存储null键值,更倾向于使用HashMap。如果需要线程安全性,可以考虑使用Hashtable,但要注意性能问题。

十、Map实现类之五:Properties

Properties是Java中的一个类,它继承自Hashtable类,用于处理属性文件(.properties)的读取和保存。属性文件通常用于存储配置信息,例如数据库连接参数、系统设置等。

特点:

  1. 键值对都为字符串:Properties类以键值对的形式存储数据,由于属性文件里的 key、value 都是字符串类型,所以键和值都为字符串类型。每个键值对之间使用等号(=)或冒号(:)进行分隔。

  2. 读取属性文件:Properties类提供了load()方法,可以从属性文件中读取键值对数据。属性文件的格式为每行一个键值对,以等号或冒号分隔键和值。

  3. 存储属性值:Properties类提供了setProperty()和getProperty()方法用于设置和获取属性值。可以通过键获取对应的值,也可以使用默认值来获取属性值。

  4. 输出属性文件:Properties类提供了store()方法,可以将属性键值对数据保存到属性文件中。输出的属性文件格式为默认的 ISO 8859-1 字符编码,可以使用其他字符编码进行保存。

示例: 

public class Test {
    public static void main(String[] args) {
        Properties properties = new Properties();
        try {
            FileInputStream input = new FileInputStream("D:\\123.txt");
            properties.load(input);
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 获取属性值
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");

        System.out.println("Username: " + username);
        System.out.println("Password: " + password);

        // 设置属性值
        properties.setProperty("database", "mysql");
        properties.setProperty("port", "3306");

        // 将配置信息保存到属性文件中
        try {
            FileOutputStream output = new FileOutputStream("D:\\123.txt");
            properties.store(output, "Test");
            output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 输出:

Username: 2938
Password: 1111

 JAVA集合框架二:Map接口及子实现类,Collections工具类_第1张图片

 十一、Collections工具类

参考操作数组的工具类:Arrays,Collections 是一个操作 Set、List 和 Map 等集合的工具类。

排序操作:

方法名 功能描述
reverse(List list) 对指定 List 集合元素进行逆向排序。
shuffle(List list) 对 List 集合元素进行随机排序(shuffle 方法模拟了“洗牌”动作)。
sort(List list) 根据元素的自然顺序对指定 List 集合的元素按升序进行排序。
sort(List list, Comparator c) 根据指定 Comparator 产生的顺序对 List 集合元素进行排序。
swap(List list, int i, int j) 将指定 List 集合中的 i 处元素和 j 处元素进行交换。
rotate(List list, int distance) 当 distance 为正数时,将 list 集合的后 distance 个元素“整体”移到前面;当 distance 为负数时,将 list 集合的前 distance 个元素“整体”移到后面。该方法不会改变集合的长度。

 例如:编写一个程序,对用户输入的 5 个商品价格进行排序后输出。这里要求使用 Collections 类中 sort() 方法按从低到高的顺序对其进行排序,最后将排序后的成绩输出。

import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        ArrayList prices = new ArrayList<>();

        // 通过循环获取用户输入的 5 个商品价格
        Scanner scanner = new Scanner(System.in);
        for (int i = 0; i < 5; i++) {
            System.out.print("请输入第 " + (i + 1) + " 个商品的价格:");
            double price = scanner.nextDouble();
            prices.add(price);
        }

        // 使用 Collections 类的 sort() 方法对价格进行排序
        Collections.sort(prices);

        // 输出排序后的价格
        System.out.println("排序后的商品价格为:");
        for (double price : prices) {
            System.out.println(price);
        }
    }
}

请输入第 1 个商品的价格:10
请输入第 2 个商品的价格:22
请输入第 3 个商品的价格:66
请输入第 4 个商品的价格:22
请输入第 5 个商品的价格:11
排序后的商品价格为:
10.0
11.0
22.0
22.0
66.0

查找操作:

方法名称 描述
Object max(Collection) 根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator) 根据 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection) 根据元素的自然顺序,返回给定集合中的最小元素
Object min(Collection,Comparator) 根据 Comparator 指定的顺序,返回给定集合中的最小元素
int binarySearch(List list,T key) 在List集合中查找(二分查找)某个元素的下标,但是List的元素必须是T或T的子类对象,而且必须是可比较大小的,即支持自然排序的,且集合也必须是有序的。否则结果不确定。
int binarySearch(List list,T key,Comparator c) 在List集合中查找某个元素的下标,但是List的元素必须是T或T的子类对象,而且集合也必须是按照c比较器规则进行排序过的,否则结果不确定。
int frequency(Collection c,Object o) 返回指定集合中指定元素的出现次数

 复制、替换、添加操作:

方法 功能
void copy(List dest,List src) 将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal,Object newVal) 使用新值替换 List 对象的所有旧值
boolean addAll(Collection c,T... elements) 将所有指定元素添加到指定 collection 中。

示例:在一个集合中保存了 5 个商品名称,现在要使用 Collections 类中的 copy() 方法将其中的 3 个替换掉。具体实现的代码 

public class Test {
    public static void main(String[] args) {
        ArrayList originalNames = new ArrayList<>();
        originalNames.add("商品1");
        originalNames.add("商品2");
        originalNames.add("商品3");
        originalNames.add("商品4");
        originalNames.add("商品5");

        // 创建替换商品名称的集合
        ArrayList newNames = new ArrayList<>();
        newNames.add("新商品1");
        newNames.add("新商品2");
        newNames.add("新商品3");

        // 使用Collections类的copy()方法将新名称列表的元素替换到原始名称列表中
        Collections.copy(originalNames, newNames);

        // 输出替换后的商品名称集合
        System.out.println("替换后的商品名称集合:");
        for (String name : originalNames) {
            System.out.println(name);
        }
    }
}

替换后的商品名称集合:
新商品1
新商品2
新商品3
商品4
商品5 

同步操作:

方法名 描述
synchronizedCollection(Collection c) 返回由指定集合支持的同步(线程安全)集合。
synchronizedList(List list) 返回由指定列表支持的同步(线程安全)列表。
synchronizedMap(Map m) 返回由指定地图支持的同步(线程安全)映射。
synchronizedNavigableMap(NavigableMap m) 返回由指定的可导航地图支持的同步(线程安全)可导航地图。
synchronizedNavigableSet(NavigableSet s) 返回由指定的可导航集支持的同步(线程安全)可导航集。
synchronizedSet(Set s) 返回由指定集合支持的同步(线程安全)集。
synchronizedSortedMap(SortedMap m) 返回由指定的排序映射支持的同步(线程安全)排序映射。
synchronizedSortedSet(SortedSet s) 返回由指定的排序集支持的同步(线程安全)排序集。

模拟洗牌打牌小游戏: 

import java.util.ArrayList;
import java.util.Collections;

public class DouDiZhu {
    public static void main(String[] args) {
        // 创建一副扑克牌
        ArrayList pokerDeck = new ArrayList<>();
        String[] suits = {"♠️", "♥️", "♣️", "♦️"};
        String[] ranks = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
        for (String suit : suits) {
            for (String rank : ranks) {
                pokerDeck.add(suit + rank);
            }
        }
        pokerDeck.add("小王");
        pokerDeck.add("大王");

        // 洗牌
        Collections.shuffle(pokerDeck);

        // 发牌
        ArrayList player1 = new ArrayList<>();
        ArrayList player2 = new ArrayList<>();
        ArrayList player3 = new ArrayList<>();
        ArrayList landlord = new ArrayList<>();

        for (int i = 0; i < pokerDeck.size() - 3; i++) {
            if (i % 3 == 0) {
                player1.add(pokerDeck.get(i));
            } else if (i % 3 == 1) {
                player2.add(pokerDeck.get(i));
            } else {
                player3.add(pokerDeck.get(i));
            }
        }

        for (int i = pokerDeck.size() - 3; i < pokerDeck.size(); i++) {
            landlord.add(pokerDeck.get(i));
        }

        // 输出玩家手中的牌和地主牌
        System.out.println("玩家1的牌:" + player1);
        System.out.println("玩家2的牌:" + player2);
        System.out.println("玩家3的牌:" + player3);
        System.out.println("地主的牌:" + landlord);
    }
}

玩家1的牌:[♦️10, ♣️9, ♦️Q, ♠️Q, ♠️4, ♠️6, ♣️8, ♣️3, ♦️8, ♦️9, ♥️K, ♦️A, ♣️7, ♣️Q, ♠️J, ♦️3, ♣️10]
玩家2的牌:[♠️K, ♥️6, ♠️10, ♥️7, ♥️2, ♦️6, ♣️6, ♥️10, ♣️5, ♥️3, ♠️9, ♦️7, 大王, ♣️K, ♠️7, ♠️A, ♦️J]
玩家3的牌:[♠️5, ♣️4, ♦️K, ♠️3, ♠️2, ♥️J, 小王, ♥️5, ♣️A, ♣️2, ♥️8, ♥️4, ♣️J, ♦️5, ♦️2, ♦️4, ♠️8]
地主的牌:[♥️Q, ♥️A, ♥️9] 

总结:

  1. HashMap:使用哈希表实现。它是最常用的Map实现类之一,提供了快速的查找和插入操作。不保证元素的顺序,也不保证对元素的迭代顺序。允许使用null键和null值。
  2. TreeMap:使用红黑树实现。按照键的自然顺序或者自定义的比较器对键进行排序。保证元素按照键的升序排列。不允许使用null键,但允许使用null值。
  3. LinkedHashMap:使用哈希表和双向链表实现。按照插入顺序或者最近访问顺序保持元素的顺序。保证元素的迭代顺序与插入顺序或访问顺序相同。允许使用null键和null值。
  4. Hashtable:使用哈希表实现。它是早期的Map实现类,线程安全,但性能相对较差。不保证元素的顺序。不允许使用null键和null值。
  5. Properties:是Java中的一个类,它继承自Hashtable类,用于处理属性文件(.properties)的读取和保存。

你可能感兴趣的:(JAVA,java,开发语言)