Java HashMap 教程与示例

x33g5p2x  于2021-10-16 转载在 Java  
字(12.6k)|赞(0)|评价(0)|浏览(319)

Java HashMap 是基于 hash table 的 Java Map 接口实现。您可能知道,Map 是键值对的集合。它将键映射到值。

以下是有关 Java 中 HashMap 的几个需要注意的要点 -

HashMap 不能包含重复的键。
*
Java HashMap 允许 null 值和 null 键。
*
HashMap 是一个无序的集合。它不保证元素的任何特定顺序。
*
Java HashMap 不是线程安全的。您必须显式同步对 HashMap 的并发修改。

创建一个 HashMap 并向其添加键值对

以下示例显示了如何创建 HashMap,并向其添加新的键值对。

import java.util.HashMap;
import java.util.Map;

public class CreateHashMapExample {
    public static void main(String[] args) {
        // Creating a HashMap
        Map<String, Integer> numberMapping = new HashMap<>();

        // Adding key-value pairs to a HashMap
        numberMapping.put("One", 1);
        numberMapping.put("Two", 2);
        numberMapping.put("Three", 3);

        // Add a new key-value pair only if the key does not exist in the HashMap, or is mapped to `null`
        numberMapping.putIfAbsent("Four", 4);

        System.out.println(numberMapping);
    }
}
# Output
{One=1, Four=4, Two=2, Three=3}

在 HashMap 中访问键并修改它们的关联值

下面的例子显示:

  • 如何检查HashMap 是否为空| isEmpty()
  • 如何找到一个HashMap 的大小| size()
  • 如何检查一个给定的键是否存在于 HashMap | containsKey()
  • 如何检查一个给定的值是否存在于 HashMap | containsValue()
  • 如何在HashMap 中获取与给定键关联的值| get()
  • 如何修改HashMap 中与给定键关联的值| put()
import java.util.HashMap;
import java.util.Map;

public class AccessKeysFromHashMapExample {
    public static void main(String[] args) {
        Map<String, String> userCityMapping = new HashMap<>();

        // Check if a HashMap is empty
        System.out.println("is userCityMapping empty? : " + userCityMapping.isEmpty());

        userCityMapping.put("John", "New York");
        userCityMapping.put("Rajeev", "Bengaluru");
        userCityMapping.put("Steve", "London");

        System.out.println("userCityMapping HashMap : " + userCityMapping);

        // Find the size of a HashMap
        System.out.println("We have the city information of " + userCityMapping.size() + " users");

        String userName = "Steve";
        // Check if a key exists in the HashMap
        if(userCityMapping.containsKey(userName)) {
            // Get the value assigned to a given key in the HashMap
            String city = userCityMapping.get(userName);
            System.out.println(userName + " lives in " + city);
        } else {
            System.out.println("City details not found for user " + userName);
        }

        // Check if a value exists in a HashMap
        if(userCityMapping.containsValue("New York")) {
            System.out.println("There is a user in the userCityMapping who lives in New York");
        } else {
            System.out.println("There is no user in the userCityMapping who lives in New York");
        }


        // Modify the value assigned to an existing key
        userCityMapping.put(userName, "California");
        System.out.println(userName + " moved to a new city " + userCityMapping.get(userName) + ", New userCityMapping : " + userCityMapping);

        // The get() method returns `null` if the specified key was not found in the HashMap
        System.out.println("Lisa's city : " + userCityMapping.get("Lisa"));
    }
}
# Output
is userCityMapping empty? : true
userCityMapping HashMap : {Steve=London, John=New York, Rajeev=Bengaluru}
We have the city information of 3 users
Steve lives in London
There is a user in the userCityMapping who lives in New York
Steve moved to a new city California, New userCityMapping : {Steve=California, John=New York, Rajeev=Bengaluru}
Lisa's city : null

从 HashMap 中删除键

以下示例显示了如何:

  • 从 HashMap 中删除一个键 |删除(对象键)
  • 仅当键与给定值相关联时才从 HashMap 中删除键 |删除(对象键,对象值)
import java.util.HashMap;
import java.util.Map;

public class RemoveKeysFromHashMapExample {
    public static void main(String[] args) {
        Map<String, String> husbandWifeMapping = new HashMap<>();
        husbandWifeMapping.put("Jack", "Marie");
        husbandWifeMapping.put("Chris", "Lisa");
        husbandWifeMapping.put("Steve", "Jennifer");

        System.out.println("Husband-Wife Mapping : " + husbandWifeMapping);

        // Remove a key from the HashMap
        // Ex - Unfortunately, Chris got divorced. Let's remove him from the mapping
        String husband = "Chris";
        String wife = husbandWifeMapping.remove(husband);

        System.out.println("Couple (" + husband + " => " + wife + ") got divorced");
        System.out.println("New Mapping : " + husbandWifeMapping);

        // Remove a key from the HashMap only if it is mapped to the given value
        // Ex - Divorce "Jack" only if He is married to "Linda"
        boolean isRemoved = husbandWifeMapping.remove("Jack", "Linda");
        System.out.println("Did Jack get removed from the mapping? : " + isRemoved);

        // remove() returns null if the mapping was not found for the supplied key
        wife = husbandWifeMapping.remove("David");
        if(wife == null) {
            System.out.println("Looks like David is not married to anyone");
        } else {
            System.out.println("Removed David and his wife from the mapping");
        }
    }
}
# Output
Husband-Wife Mapping : {Steve=Jennifer, Chris=Lisa, Jack=Marie}
Couple (Chris => Lisa) got divorced
New Mapping : {Steve=Jennifer, Jack=Marie}
Did Jack get removed from the mapping? : false
Looks like David is not married to anyone

从 HashMap 中获取 entrySet、keySet 和值

Map 接口提供了检索条目集(键值对)、键集和值集合的方法。

以下示例显示了如何从 HashMap 中检索它们 -

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

public class HashMapEntryKeySetValuesExample {
    public static void main(String[] args) {
        Map<String, String> countryISOCodeMapping = new HashMap<>();

        countryISOCodeMapping.put("India", "IN");
        countryISOCodeMapping.put("United States of America", "US");
        countryISOCodeMapping.put("Russia", "RU");
        countryISOCodeMapping.put("Japan", "JP");
        countryISOCodeMapping.put("China", "CN");

        // HashMap's entry set
        Set<Map.Entry<String, String>> countryISOCodeEntries = countryISOCodeMapping.entrySet();
        System.out.println("countryISOCode entries : " + countryISOCodeEntries);

        // HashMap's key set
        Set<String> countries = countryISOCodeMapping.keySet();
        System.out.println("countries : " + countries);

        // HashMap's values
        Collection<String> isoCodes = countryISOCodeMapping.values();
        System.out.println("isoCodes : " + isoCodes);
    }
}
# Output
countryISOCode entries : [United States of America=US, Japan=JP, China=CN, India=IN, Russia=RU]
countries : [United States of America, Japan, China, India, Russia]
isoCodes : [US, JP, CN, IN, RU]

遍历 HashMap

以下示例显示了迭代 HashMap 的不同方法 -

使用 Java 8 forEach 和 lambda 表达式迭代 HashMap。
1.
使用 iterator() 迭代 HashMap 的 entrySet。
1.
使用 Java 8 forEach 和 lambda 表达式迭代 HashMap 的 entrySet。
1.
使用简单的 for-each 循环迭代 HashMap 的 entrySet。
1.
迭代 HashMap 的 keySet。

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

public class IterateOverHashMap {
    public static void main(String[] args) {
        Map<String, Double> employeeSalary = new HashMap<>();
        employeeSalary.put("David", 76000.00);
        employeeSalary.put("John", 120000.00);
        employeeSalary.put("Mark", 95000.00);
        employeeSalary.put("Steven", 134000.00);

        System.out.println("=== Iterating over a HashMap using Java 8 forEach and lambda ===");
        employeeSalary.forEach((employee, salary) -> {
            System.out.println(employee + " => " + salary);
        });

        System.out.println("\n=== Iterating over the HashMap's entrySet using iterator() ===");
        Set<Map.Entry<String, Double>> employeeSalaryEntries = employeeSalary.entrySet();
        Iterator<Map.Entry<String, Double>> employeeSalaryIterator = employeeSalaryEntries.iterator();
        while (employeeSalaryIterator.hasNext()) {
            Map.Entry<String, Double> entry = employeeSalaryIterator.next();
            System.out.println(entry.getKey() + " => " + entry.getValue());
        }

        System.out.println("\n=== Iterating over the HashMap's entrySet using Java 8 forEach and lambda ===");
        employeeSalary.entrySet().forEach(entry -> {
            System.out.println(entry.getKey() + " => " + entry.getValue());
        });

        System.out.println("\n=== Iterating over the HashMap's entrySet using simple for-each loop ===");
        for(Map.Entry<String, Double> entry: employeeSalary.entrySet()) {
            System.out.println(entry.getKey() + " => " + entry.getValue());
        }

        System.out.println("\n=== Iterating over the HashMap's keySet ===");
        employeeSalary.keySet().forEach(employee -> {
            System.out.println(employee + " => " + employeeSalary.get(employee));
        });
    }
}
# Output
=== Iterating over a HashMap using Java 8 forEach and lambda ===
David => 76000.0
John => 120000.0
Mark => 95000.0
Steven => 134000.0

=== Iterating over the HashMap's entrySet using iterator() ===
David => 76000.0
John => 120000.0
Mark => 95000.0
Steven => 134000.0

=== Iterating over the HashMap's entrySet using Java 8 forEach and lambda ===
David => 76000.0
John => 120000.0
Mark => 95000.0
Steven => 134000.0

=== Iterating over the HashMap's entrySet using simple for-each loop ===
David => 76000.0
John => 120000.0
Mark => 95000.0
Steven => 134000.0

=== Iterating over the HashMap's keySet ===
David => 76000.0
John => 120000.0
Mark => 95000.0
Steven => 134000.0

Java HashMap 与用户定义的对象

查看以下示例以了解如何创建和使用用户定义对象的 HashMap。

import java.util.HashMap;
import java.util.Map;

class Employee {
    private Integer id;
    private String name;
    private String city;

    public Employee(Integer id, String name, String city) {
        this.id = id;
        this.name = name;
        this.city = city;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

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

public class HashMapUserDefinedObjectExample {
    public static void main(String[] args) {
        Map<Integer, Employee> employeesMap = new HashMap<>();

        employeesMap.put(1001, new Employee(1001, "Rajeev", "Bengaluru"));
        employeesMap.put(1002, new Employee(1002, "David", "New York"));
        employeesMap.put(1003, new Employee(1003, "Jack", "Paris"));

        System.out.println(employeesMap);
    }
}
# Output
{1001=Employee{name='Rajeev', city='Bengaluru'}, 1002=Employee{name='David', city='New York'}, 1003=Employee{name='Jack', city='Paris'}}

同步访问 Java HashMap

Java HashMap 不是线程安全的。在多个线程尝试同时修改 HashMap 的多线程环境中,它可能变得不确定。

示例演示 HashMap 在多线程环境中的不可预测行为

以下示例演示了当多个线程尝试同时修改 HashMap 时如何变得不确定 -

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class UnsafeHashMapExample {
    public static void main(String[] args) throws InterruptedException {
        Map<String, Integer> cricketTeamScore = new HashMap<>();
        cricketTeamScore.put("Australia", 349);
        cricketTeamScore.put("India", 250);

        // Create an ExecutorService with a Thread Pool of size 10
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        // Create a Runnable object that increments the value associated with a given key in the HashMap by one.
        Runnable task = () -> {
            incrementTeamScore(cricketTeamScore, "India");
        };

        // Submit the Runnable object to the executorService 100 times to test concurrent modifications
        for(int i = 0; i < 100; i++) {
            executorService.submit(task);
        }

        executorService.shutdown();
        executorService.awaitTermination(60, TimeUnit.SECONDS);

        System.out.println("Final Score of Team India : " + cricketTeamScore.get("India"));
    }

    // Increment the score of a team by one
    private static void incrementTeamScore(Map<String, Integer> cricketTeamScore, String team) {
        Integer score = cricketTeamScore.get(team);
        cricketTeamScore.put(team, score + 1);
    }
}

上述程序的最终输出应该是 350,因为初始值是 250,我们将它递增 100 次。

但是由于多个线程同时尝试修改 HashMap,一个线程所做的更改会被其他线程覆盖,并且输出变得不确定。

如果多次运行上述程序,您会发现每次运行都会产生不同的输出。

# Output
Final Score of Team India : 343

您可以从我的 Java 并发问题和线程同步教程中了解有关此类并发问题的更多信息。

示例演示如何将并发修改同步到 HashMap

让我们编写上一个程序的线程安全版本。我们可以对程序进行以下两项更改以使其成为线程安全的 -

使用 Collections.synchronizedMap() 方法获取 HashMap 的同步视图。
*
synchronized 块中编写增量逻辑。

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;

public class SynchronizedHashMapExample {
    public static void main(String[] args) throws InterruptedException {
        Map<String, Integer> cricketTeamScore = Collections.synchronizedMap(new HashMap<>());
        cricketTeamScore.put("Australia", 349);
        cricketTeamScore.put("India", 250);

        // Create an ExecutorService with a Thread Pool of size 10
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        // Create a Runnable object that increments the value associated with a given key in the HashMap by one.
        Runnable task = () -> {
            incrementTeamScore(cricketTeamScore, "India");
        };

        // Submit the Runnable object to the executorService 100 times to test concurrent modifications
        for(int i = 0; i < 100; i++) {
            executorService.submit(task);
        }

        executorService.shutdown();
        executorService.awaitTermination(60, TimeUnit.SECONDS);

        System.out.println("Final Score of Team India : " + cricketTeamScore.get("India"));
    }

    // Increment the score of a team by one
    private static void incrementTeamScore(Map<String, Integer> cricketTeamScore, String team) {
        synchronized (cricketTeamScore) {
            Integer score = cricketTeamScore.get(team);
            cricketTeamScore.put(team, score + 1);
        }
    }
}

该程序产生正确的输出 -

# Output
Final Score of Team India : 350

您也可以使用 ConcurrentHashMap 来保证线程安全,而不是通过 Collections.synchronizedMap() 方法获得的 HashMap。 ConcurrentHashMap 在 Map 上提供线程安全的操作。

结论

恭喜各位!在本文中,您了解了什么是 HashMap、如何创建 HashMap、如何向 HashMap 添加新的键值对、如何从 HashMap 中删除键、如何迭代 HashMap 以及如何同步 HashMap .

相关文章