Skip to content

Map

在 Java 中,Map 是一个用于存储键值对的数据结构接口。它提供了一种通过键快速查找值得方式,确保每个键都是唯一的。

特点:

  • 键值对存储:每个元素由一个键和值组成;
  • 键唯一:同一个 Map 中的键不能重复,但值可以重复;
  • 无序或有序:HashMap实现是无序的,LinkedHashMap和TreeMap实现是有序的;

常用方法:

方法作用
put(key, value)将指定键值对插入到 Map 中,返回之前与该键关联的值
get(key)根据键返回对应的值
containsKey(key)判断是否包含指定的键
containsValue(value)判断是否包含指定的值
remove(key)根据键移除对应的键值对
size()返回 Map 中键值对的数量
keySet()返回所有键的集合
values()返回所有值的集合
isEmpty()判断 Map 是否为空
clear()清空 Map 中的所有键值对
entrySet()返回所有键值对的集合

HashMap

HashMap原理

  • 数据结构:基于 数组 + 链表 + 红黑树 实现。
  • 原理:
    • 通过键的 hashCode() 方法计算哈希值,然后将其映射到数组的某个索引位置;
    • 如果发生哈希冲突(也叫哈希碰撞,指多个键的哈希值映射到同一个索引),会在该位置形成链表,Java8之后,当链表长度超过8时,会转为红黑树以提升性能;
  • 优点:查询效率高,时间复杂度接近O(1);
  • 缺点:无序
java
public static void main(String[] args) {
  HashMap<String, String> map = new HashMap<>();
  // HashMap存储时无序的
  map.put("王一博", "20");
  map.put("陈伟霆", "23");
  map.put("彭于晏", "25");

  System.out.println(map); //{陈伟霆=23, 王一博=20, 彭于晏=25}

  String s = map.get("王一博"); //20

  boolean b = map.containsKey("王一博"); //true

  int size = map.size(); //2

  Set<String> keys = map.keySet(); //[王一博, 彭于晏]

  Collection<String> values = map.values(); //[20, 25]

  boolean empty = map.isEmpty(); //false

  for (var entry : map.entrySet()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
  }
}

LinkedHashMap

LinkedHashMap原理

数据结构:继承自 HashMap,在其基础上添加了 双向链表 以维护插入顺序;

特点:即具备 HashMap 的快速访问性能,又能按照插入顺序访问遍历;

java
public static void main(String[] args) {
  LinkedHashMap<String, String> linkedMap = new LinkedHashMap<>();

  linkedMap.put("apple", "苹果");
  linkedMap.put("banana", "香蕉");
  linkedMap.put("orange", "橙子");

  System.out.println(linkedMap); //{apple=苹果, banana=香蕉, orange=橙子}

  String s = linkedMap.get("apple"); //苹果

  for (String key : linkedMap.keySet()) {
    System.out.println(key + ": " + linkedMap.get(key));
  }
}

TreeMap

TreeMap原理

数据结构:基于 红黑树(自平衡二叉搜索树)实现;

特点:

  • 键按自然顺序或指定的比较器顺序排序;
  • 插入、删除和查询操作的时间复杂度为 O(log n);
java
public static void main(String[] args) {
  TreeMap<String, String> treeMap1 = new TreeMap<>();
  treeMap1.put("b", "曲项向天歌");
  treeMap1.put("d", "红掌拨清波");
  treeMap1.put("a", "鹅鹅鹅");
  treeMap1.put("c", "白毛浮绿水");

  System.out.println(treeMap1); //{a=鹅鹅鹅, b=曲项向天歌, c=白毛浮绿水, d=红掌拨清波}
}
java
public static void main(String[] args) {
  TreeMap<Person, String> treeMap2 = new TreeMap<>(new Comparator<Person>() {
    @Override
    public int compare(Person o1, Person o2) {
      return o1.getAge() - o2.getAge();
    }
  });

  treeMap2.put(new Person("王一博", 22),"");
  treeMap2.put(new Person("陈伟霆", 12),"");
  treeMap2.put(new Person("彭于晏", 8),"");

  System.out.println(treeMap2);
	//{Person{name='彭于晏', age=8}=, Person{name='陈伟霆', age=12}=, Person{name='王一博', age=22}=}
}
java
public static void main(String[] args) {
  // Person 内部已经实现 Comparable 接口
  TreeMap<Person, String> treeMap2 = new TreeMap<>();
  treeMap2.put(new Person("王一博", 22),"");
  treeMap2.put(new Person("陈伟霆", 12),"");
  treeMap2.put(new Person("彭于晏", 8),"");

  System.out.println(treeMap2);
  //{Person{name='彭于晏', age=8}=, Person{name='陈伟霆', age=12}=, Person{name='王一博', age=22}=}
}
java
public class Person implements Comparable<Person>{
  private String name;
  private Integer age;

  public Person() {
  }

  public Person(String name, Integer age) {
    this.age = age;
    this.name = name;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Integer getAge() {
    return age;
  }

  public void setAge(Integer age) {
    this.age = age;
  }

  @Override
  public String toString() {
    return "Person{" +
      "name='" + name + '\'' +
      ", age=" + age +
      '}';
  }


  @Override
  public int compareTo(Person o) {
    return this.age - o.age;
  }
}

Hashtable

Hashtable 从 Java1.0 就存在,它实现了 Map 接口,是一个线程安全的集合类,用于存储键值对。

Hashtable原理

数据结构:底层基于 哈希表 实现,通过键的 hashCode() 确定键值对的存储位置。

特点:

  • 线程安全:所有方法都是同步的,因此性能比较低,但适用于多线程环境;
  • 元素无序:键值对没有特定的顺序存储;
  • 键和值不允许为null:不允许存储 null键和 null值,但 HashMap 允许哦

常用方法:

方法作用
put(key, value)将指定的键值对插入到 Hashtable 中
get(key)根据键获取对应的值,如果键不存在,则返回null
remove(key)按键删除键值对
containsKey(key)检查是否包含指定的键
containsValue(val)检查是否包含指定的值
size()返回键值对的数量
isEmpty()检查 Hashtable 是否为空
keys()返回包含所有键的枚举
elements()返回包含所有值的枚举
keySet()返回所有的键到 Set 集合中
java
public static void main(String[] args) {
  Hashtable<String, String> table = new Hashtable<>();

  table.put("name", "王一博");
  table.put("city", "北京市");
  table.put("job", "演员&歌手");

  table.put("name", "陈伟霆"); //会覆盖前面 name 属性的值
  table.put(null, null);      //NullPointerException
  System.out.println(table);

  String name = table.get("name"); //王一博

  for (String s : table.keySet()) {
    System.out.println(s + ": " + table.get(s));
  }
}

Hashtable 与 HashMap 的对比:

特性HashtableHashMap
是否无序
有无索引
线程是否安全
是否允许存储 null 键值对不允许只允许一个 null键和 null值
性能较低较高
引入时间JDK 1.0JDK 1.2

Properties

Properties 是 Hashtable 的一个子类,专门用于存储 String 类型的键值对。主要用于文件IO流和配置文件读取写入。

Properties原理

数据结构:哈希表

特点:

  • 键值对存储:键和值都是 String 类型;
  • 继承自 Hashtable:因为继承自 Hashtable,所以线程安全,方法默认都是同步的;
  • 常用于配置文件:常常与 .properties 配置文件一起使用;

常用方法:

方法作用
setProperty(key, value)设置键值对,键已存在时,更新其值
getProperty(key)根据键获取值,不存在返回 null
stringPropertyNames()获取键值对集合,以 Set<String> 形式返回
java
public static void main(String[] args) {
  Properties prop = new Properties();
  prop.setProperty("username", "admin");
  prop.setProperty("password", "123456");

  Set<String> names = properties.stringPropertyNames();
  for (String key : names) {
    String value = properties.getProperty(key);
    System.out.println(key + "=" + value);
  }
}

Released under the MIT License.