HashMap与HashTable

HashMap

HashMap 是 Java 集合框架中最常用的类之一,它实现了 Map 接口,基于哈希表(Hash Table)的数据结构。HashMap 提供了高效的键值对存储和检索操作,适合存储大量数据

特点

  1. HashMap键不能重复,值可以相同,如果插入相同的键则会覆盖(如果onlyIfAbsent为false)
  2. 基于哈希表实现,HashMap 内部使用哈希表来存储键值对,哈希表是一种高效的数据结构,支持快速的插入、删除和查询操作
  3. 允许 null 键和 null 值,HashMap 允许键和值为 null,但只能有一个 null 键
  4. 无序性,HashMap 不保证键值对的顺序,元素的存储顺序可能与插入顺序不同
  5. HashMap 不是线程安全的,如果需要在多线程环境中使用使用 ConcurrentHashMap

HashMap的遍历

  1. 通过 keySet() 方法获取所有键,然后遍历键

    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    public class HashMapTraversalExample {
        public static void main(String[] args) {
            Map<String, Integer> map = new HashMap<>();
            map.put("apple", 10);
            map.put("banana", 20);
            map.put("cherry", 30);
    
            // 遍历键
            Set<String> keys = map.keySet();
            for (String key : keys) {
                System.out.println("Key: " + key);
            }
        }
    }
    
  2. 通过 values() 方法获取所有值,然后遍历值

    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;
    
    public class HashMapTraversalExample {
        public static void main(String[] args) {
            Map<String, Integer> map = new HashMap<>();
            map.put("apple", 10);
            map.put("banana", 20);
            map.put("cherry", 30);
    
            // 遍历值
            Collection<Integer> values = map.values();
            for (Integer value : values) {
                System.out.println("Value: " + value);
            }
        }
    }
    
  3. 通过 entrySet() 方法获取所有键值对,然后遍历键值对

    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    public class HashMapTraversalExample {
        public static void main(String[] args) {
            Map<String, Integer> map = new HashMap<>();
            map.put("apple", 10);
            map.put("banana", 20);
            map.put("cherry", 30);
    
            // 遍历键值对
            Set<Map.Entry<String, Integer>> entries = map.entrySet();
            for (Map.Entry<String, Integer> entry : entries) {
                System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
            }
        }
    }
    
  4. 使用 forEach 方法(Java 8+),通过 forEach 方法和 Lambda 表达式遍历 HashMap

    import java.util.HashMap;
    import java.util.Map;
    
    public class HashMapTraversalExample {
        public static void main(String[] args) {
            Map<String, Integer> map = new HashMap<>();
            map.put("apple", 10);
            map.put("banana", 20);
            map.put("cherry", 30);
    
            // 使用 forEach 方法遍历
            map.forEach((key, value) -> {
                System.out.println("Key: " + key + ", Value: " + value);
            });
        }
    }
    

HashMap底层结构

与HashSet相同

  1. HashMap 的底层是一个哈希表,核心字段包括
    • Node<K,V>[] table:哈希表,用于存储键值对。
    • int size:HashMap 中键值对的数量。
    • int threshold:扩容阈值,当 size 超过 threshold 时,会触发扩容。
    • float loadFactor:负载因子,用于计算扩容阈值(默认值为 0.75)
  2. 哈希表的结构
    • 哈希表是一个数组,数组的每个元素是一个链表或红黑树。
    • 当哈希冲突较少时,使用链表存储冲突的键值对。
    • 当哈希冲突较多时(链表长度超过 8 且哈希表容量大于 64),将链表转换为红黑树
  3. 扩容机制
    • 当 HashMap 中的键值对数量超过 threshold 时,会触发扩容。
    • 扩容时,哈希表的容量会加倍,并重新计算所有键值对的哈希值,将其分配到新的桶中

HashTable

  • HashTable:HashTable 是 Java 早期提供的哈希表实现,从 JDK 1.0 就已经存在,设计之初就考虑了线程安全问题,不过它的实现方式比较粗糙
  • HashTable 不允许键或值为 null。如果尝试将 null 作为键或值插入 HashTable 中,会抛出 NullPointerException
  • HashTable 使用 Enumeration 进行迭代,Enumeration 是一个较老的迭代器接口,功能相对有限,没有 remove 方法,只能遍历元素
  • HashTable 的初始容量是 11,扩容时会将容量变为原来的 2 倍加 1(oldCapacity * 2 + 1)

Properties

  • Properties 类是 Hashtable<Object,Object> 的子类,而 Hashtable 实现了 Map 接口,所以 Properties 本质上也是一个双列集合
  • Properties常用于文件中字符串的保存读取

常用方法

  • setProperty(String key, String value):调用 Hashtable 的方法 put,将键值对存储到 Properties 对象中

    import java.util.Properties;  
     
    public class PropertiesExample { 
        public static void main(String[] args) { 
            Properties properties = new Properties(); 
            properties.setProperty("database.url",  "jdbc:mysql://localhost:3306/mydb"); 
            properties.setProperty("database.username",  "root"); 
            properties.setProperty("database.password",  "password123"); 
        } 
    } 
    
  • getProperty(String key):用指定的键在此属性列表中搜索属性。如果此属性列表中没有该键,则会搜索默认属性列表及其默认值。如果仍未找到属性,则返回 null

  • getProperty(String key, String defaultValue):与 getProperty(String key) 类似,但如果未找到属性,会返回指定的默认值

    import java.util.Properties;  
     
    public class PropertiesExample { 
        public static void main(String[] args) { 
            Properties properties = new Properties(); 
            properties.setProperty("database.url",  "jdbc:mysql://localhost:3306/mydb"); 
            String url = properties.getProperty("database.url");  
            String port = properties.getProperty("database.port",  "3306"); 
            System.out.println("URL:  " + url); 
            System.out.println("Port:  " + port); 
        } 
    } 
    
  • load(InputStream inStream):从输入流中读取属性列表(键和元素对)。输入流可以是文件输入流等

    import java.io.FileInputStream;  
    import java.io.IOException;  
    import java.util.Properties;  
     
    public class PropertiesExample { 
        public static void main(String[] args) { 
            Properties properties = new Properties(); 
            try (FileInputStream fis = new FileInputStream("config.properties"))  { 
                properties.load(fis);  
                String username = properties.getProperty("database.username");  
                System.out.println("Username:  " + username); 
            } catch (IOException e) { 
                e.printStackTrace();  
            } 
        } 
    } 
    
  • store(OutputStream out, String comments):将此 Properties 表中的属性列表(键和元素对)写入输出流。comments 是可选的注释信息

    import java.io.FileOutputStream;  
    import java.io.IOException;  
    import java.util.Properties;  
     
    public class PropertiesExample { 
        public static void main(String[] args) { 
            Properties properties = new Properties(); 
            properties.setProperty("app.name",  "MyApp"); 
            properties.setProperty("app.version",  "1.0"); 
            try (FileOutputStream fos = new FileOutputStream("app.properties"))  { 
                properties.store(fos,  "Application Configuration"); 
            } catch (IOException e) { 
                e.printStackTrace();  
            } 
        } 
    } 
    

使用场景和优缺点

  • 常常使用 Properties 类来读取和写入配置文件,如数据库连接配置、应用程序的全局设置等
  • 可以将不同语言的文本信息存储在属性文件中,通过 Properties 类加载,实现多语言支持
  • 简单易用:Properties 类提供了简单的键值对存储和操作方法,易于上手。
  • 跨平台:属性文件以文本形式存储,方便在不同平台上使用
  • 数据类型单一:只能存储字符串类型的键值对,如果需要存储其他数据类型,需要进行额外的转换。
  • 不适合大规模数据:对于大规模的数据存储和管理,Properties 类的性能和功能可能不够,需要使用更专业的数据库或数据存储方案
posted @ 2025-03-03 21:57  QAQ001  阅读(33)  评论(0)    收藏  举报