java的hashmap.

时间:2020-02-23 14:34:49  来源:igfitidea点击:

HashMap是基于哈希表的实现 Map接口。

它存储在键值对中的条目。
它将键映射到值。
它是最常用的集合之一。

Java Hashmap.

  • HashMap实施 Map映射到值键的接口。
  • 它不同步,并且不是线程安全的。
  • 不允许重复键
  • null钥匙和多个 null允许值
  • 它是无序的集合,并不为任何特定元素的保证提供保证。

您是否注意到: 即使AbstractMap已经实现了它,HashMap实现了Map接口?
》 是的,为了让事情变得更明显,HashMap再次实现了Map接口,实现接口并没有错又来了。你呢不必遍历类层次结构就可以发现HashMap实现了Map接口。

Hashmap构造函数

Java HashMap类有四个构造函数

public HashMap():这是默认构造函数,主要用于。

它创建一个空散列图,默认初始容量为16和负载因子0.75.

public HashMap(int initialCapacity):此构造函数用于指定HashMap和默认负载因子0.75的初始容量。
public HashMap(int initialCapacity,float loadFactor):此构造函数用于指定HashMap和负载因子的初始容量。
在大多数场景中,我们应该避免使用此构造函数,除非我们确定这一点,因为负载因子0.75提供时间和空间之间的良好权衡。
public HashMap(Map<? extends K,? extends V> m):当我们希望从Treemap或者LinkedHashMap等其他地图中创建HashMap时使用此构造函数。

将键值对添加到HashMap

我们可以用 put()添加条目的方法 HashMap
例子:

package org.igi.theitroad;
package org.igi.theitroad;
import java.util.HashMap;
 
public class HashMapBuiltMain {
 
	public static void main(String[] args) {
		HashMap<Integer, String> employeeHashmap = new HashMap<Integer, String>();
		//Putting key-values pairs in HashMap
		employeeHashmap.put(1, "igi");
		employeeHashmap.put(2, "John");
		employeeHashmap.put(3, "Martin");
		employeeHashmap.put(4, "Vaibhav");
		
		System.out.println(employeeHashmap);
	}
}

运行上面的程序时,我们将得到以下输出

{1=igi, 2=John, 3=Martin, 4=Vaibhav}

如果只有在HashMap中尚未存在时,才能添加条目,该怎么办?
我们可以使用 putIfAbsent()这种情况下的方法。

删除hashmap的条目

有两种方法可以在HashMap中删除条目。

  • remove(Object key):它从hashmap删除密钥
  • remove(Object key,Object value):如果值与传递参数值相同,则会删除键。
package org.igi.theitroad.HashMap;
 
import java.util.HashMap;
 
public class HashMapRemoveMain {
 
	public static void main(String[] args) {
		HashMap<String, Integer> vehicleMaxSpeedMap = new HashMap<String, Integer>();
		//Putting key-values pairs in HashMap
		
		vehicleMaxSpeedMap.put("Bike", 120);
		vehicleMaxSpeedMap.put("Car", 220);
		vehicleMaxSpeedMap.put("Truck", 160);
		vehicleMaxSpeedMap.put("Activa", 140);
		System.out.println(vehicleMaxSpeedMap);
		
		
		//Remove truck key
		Integer speed = vehicleMaxSpeedMap.remove("Truck");
		System.out.println("===============================");
		System.out.println("Vehicle Truck with max speed "+speed+" removed from HashMap");
		System.out.println(vehicleMaxSpeedMap);
		System.out.println("================================");
		
		//Remove Car if value is 200
	    boolean isCarRemoved = vehicleMaxSpeedMap.remove("Car",200);
	    //Car key won't be removed as associated value is 220
	    System.out.println("Did car removed from HashMap: "+isCarRemoved);
		System.out.println(vehicleMaxSpeedMap);
		System.out.println("===============================");
		
		//Remove Car if value is 200
	    boolean isActivaRemoved = vehicleMaxSpeedMap.remove("Activa",140);
	    //Activa key will be removed as associated value is 140
	    System.out.println("Did activa removed from HashMap: "+isActivaRemoved);
		System.out.println(vehicleMaxSpeedMap);
		System.out.println("===============================");
		
	}
}

重要的HashMap方法

get():从HashMap检索值
put():将值放入HashMap中
isEmpty:检查HashMap是否为空。
containsKey():检查键存在是否是HashMap
containsValue():检查hashmap中是否存在值
size():检查HashMap的大小
clear():删除HashMap的所有元素
clone():它创造了HashMap的浅副本。

以下是涵盖这些方法的示例。

package org.igi.theitroad.HashMap;
 
import java.util.HashMap;
 
public class HashMapMethodsMain {
 
	public static void main(String[] args) {
		HashMap<String, String> employeeDeptmap = new HashMap<>();
		
		//check if map is empty
		boolean empty = employeeDeptmap.isEmpty();
		System.out.println("is employeeDeptmap empty: "+empty);
		
		//Putting key-values pairs in HashMap
		employeeDeptmap.put("igi","Tech");
		employeeDeptmap.put("John", "Sales");
		employeeDeptmap.put("Martin", "HR");
		employeeDeptmap.put("Vaibhav","Tech");
		
      System.out.println(employeeDeptmap);
		//check size of map
		System.out.println("size of employeeDeptmap: "+employeeDeptmap.size());
		
		//get value from HashMap
		System.out.println("Martin's department: "+employeeDeptmap.get("Martin"));
		//Robin's department will be null as we don't have key as "Robin"
		System.out.println("Robin's department: "+employeeDeptmap.get("Robin"));
		
		if(employeeDeptmap.containsKey("John"))
		{
			System.out.println("employeeDeptmap has John as key");
		}
		
		if(employeeDeptmap.containsValue("Sales"))
		{
			System.out.println("employeeDeptmap has Sales as value");
		}
		
		//Removing all entries from Map
		employeeDeptmap.clear();
		System.out.println(employeeDeptmap);
	}
}

使用hashmap写入语句式样式代码

Java 8的地图接口推出了新方法,如

compute(), computeIfPresent() and computeIfAbsent()哪些使用lambda表达式编写代码。

compute()

让我们说你有一个团队的HashMap和没有。
目标如下。

HashMap<String,Integer> teamGoalMap=new HashMap<>();
teamGoalMap.put("team1",1);
teamGoalMap.put("team2",1);

现在你想加一个目标 team1通常,你这样做如下。

teamGoalMap.put("team1",teamGoalMap.get("team1")+1);

相反,我们可以轻松地使用以下计算。

teamGoalMap.compute("team1",(team,goal) ->goal+1);

因此,无论何时要基于键应用映射,那么应该使用价值对。

computeifpresent()

ComputeIfPresent如果存在指定的键,则重新计算值,并且值不是null。
我们之前可能有以下类似的代码:

if(teamGoalMap.containsKey("team1"))
{
   teamGoalMap.put("team1",teamGoalMap.get("team1")+1);
}

你可以重写这个 computeIfPresent如下

teamGoalMap.computeIfPresent("team1",(team,goal) ->goal+1);

如果函数返回null,则将从hashmap中删除键。

computeifabsent()

ComputeIfabsent重新计算如果未存在指定的键并且函数不会返回NULL,则该值将重新计算该值。
我们之前可能有以下类似的代码:

if(!teamGoalMap.containsKey("team3"))
{
   teamGoalMap.put("team3",0);
}

你可以重写这个 computeIfAbsent如下

teamGoalMap.computeIfAbsent("team3",value -> 0);

如果键已在地图中存在,那么什么都不会改变。

让我们看到另一个例子来以声明方式重写HashMap代码。

问题:我们要在字符串中找到每个字符的频率。

我们可能会以以下方式编写该程序。

package org.igi.theitroad.HashMap;
import java.util.HashMap;
public class FrequencyOfEachWord {
   public static void main(String[] args) {
      String str = "thisisjavablog";
 
      HashMap<Character,Integer> hMap = new HashMap<>();
      for(int i= 0 ; i< str.length() ; i++) {
    	  Character c=str.charAt(i);
         if(hMap.containsKey(c)) {
            int count = hMap.get(c);
            hMap.put(c,count+1);
         } else {
            hMap.put(c,1);
         }
      }
      System.out.println(hMap);
   }
}

上面的程序使用简单的逻辑来计算字符串中每个字符的频率。

  • 创建一个包含字符以计算映射的HashMap。
  • 字符迭代字符串字符
  • 如果地图中不存在字符,则计数应为1
  • 如果角色已经存在于地图中,则将其计数递增1

让我们使用这个逻辑 computeIfPresentcomputeIfAbesent方法。

package org.igi.theitroad.HashMap;
import java.util.HashMap;
public class FrequencyOfEachWord {
   public static void main(String[] args) {
      String str = "thisisjavablog";
 
      HashMap<Character,Integer> hMap = new HashMap<>();
      for(int i= 0 ; i< str.length() ; i++) {
    	  Character c=str.charAt(i);
        hMap.computeIfPresent(c, (character,count)-> count+1);
	     hMap.computeIfAbsent(c, (character)-> 1);
      }
      System.out.println(hMap);
   }
}

如我们所见,逻辑看起来非常可读 computeIfPresentcomputeIfAbesent方法。

获取hashmap的entryset(),keyset()和values()

entryset()

entrySet():哈希图以形式存储关键值对
Entry,我们可以通过调用来检索entryset() map.entrySet()

keyset()

keySet():提供一组密钥。

values()

values():提供一个值的集合。

这是一个例子。

package org.igi.theitroad;
 
import java.util.Collection;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
 
public class HashMapEntrySetMain {
 
	public static void main(String[] args) {
		HashMap<Integer, String> studentIDNameMap = new HashMap<>();
		
		//Putting key-values pairs in HashMap
		studentIDNameMap.put(101,"Andy");
		studentIDNameMap.put(102, "Mary");
		studentIDNameMap.put(103, "Sam");
		studentIDNameMap.put(104,"Sandy");
		
		//get entrySet
		Set<Entry<Integer, String>> entrySet = studentIDNameMap.entrySet();
		System.out.println("EntrySet: "+entrySet);
		
		//get keySet
		Set<Integer> keySet = studentIDNameMap.keySet();
		System.out.println("keySet: "+keySet);
		
		//get values
		Collection<String> values = studentIDNameMap.values();
		System.out.println("values: "+values);
	}
}

迭代Hashmap.

有很多方法可以迭代HashMap

  • 迭代HashMap使用 keyset()
  • 迭代HashMap使用 keyset()使用foreach()和lambda表达[Java 8]
  • 使用foreach()和lambda表达式迭代hashmap [Java 8]
  • 迭代HashMap的 entrySet()使用 iterator
  • 迭代HashMap的 entrySet()使用foreach()和lambda表达式[Java 8]
  • 迭代HashMap的 entrySet()使用foreach循环
package org.igi.theitroad.HashMap;
 
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
 
public class HashMapIterationMain {
 
	public static void main(String[] args) {
 
		HashMap<String, String> userCountryMap = new HashMap<>();
		
		//Putting key-values pairs in HashMap
		userCountryMap.put("Anchit","Netherlands");
		userCountryMap.put("Andy", "USA");
		userCountryMap.put("Roy", "Germary");
		userCountryMap.put("Mary","France");
		
		System.out.println("=========================================================");
		System.out.println("Iterating over HashMap with foreach and lambda:");
		userCountryMap.forEach((user,country) -> { 
			System.out.println(user+" --> "+country);
			} 
		);
		
		System.out.println("=========================================================");
		System.out.println("Iterating over HashMap using keyset() with foreach loop:");
		for(String user:userCountryMap.keySet())
		{
			System.out.println(user+" --> "+userCountryMap.get(user));
		}
		System.out.println("=========================================================");
		System.out.println("Iterating over HashMap's keyset() with foreach and lambda:");
		userCountryMap.keySet().forEach((user) -> { 
			System.out.println(user+" --> "+userCountryMap.get(user));
			} 
		);
		
		System.out.println("=========================================================");
		System.out.println("Iterating over HashMap's entrySet with iterator");
		Iterator<Entry<String, String>> iterator = userCountryMap.entrySet().iterator();
		while(iterator.hasNext())
		{
			Entry<String, String> next = iterator.next();
			System.out.println(next.getKey()+" --> "+next.getValue());
		}
		
		System.out.println("=========================================================");
		System.out.println("Iterating over HashMap's entrySet with foreach and lambda");
		userCountryMap.entrySet().forEach((entry) -> { 
			System.out.println(entry.getKey()+" --> "+entry.getValue());
			} 
		);
		
		System.out.println("=========================================================");
		System.out.println("Iterating over HashMap's entrySet with foreach loop");
		for(Map.Entry<String, String> entry:userCountryMap.entrySet())
		{
			System.out.println(entry.getKey()+" --> "+entry.getValue());
		}
		
	}
}

将自定义对象存储为键

我们可以将自定义对象存储为HashMap中的键,但我们应该实现HashCode并等于方法,否则可能无法按预期工作。

创建一个叫做 Country.java

package org.igi.theitroad;  
public class Country  {  
 
	String name;  
	long population;  
 
	public Country(String name, long population) {  
		super();  
		this.name = name;  
		this.population = population;  
	}  
	public String getName() {  
		return name;  
	}  
	public void setName(String name) {  
		this.name = name;  
	}  
	public long getPopulation() {  
		return population;  
	}  
	public void setPopulation(long population) {  
		this.population = population;  
	}
 
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + (int) (population ^ (population >>> 32));
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Country other = (Country) obj;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
 
   @Override
	public String toString() {
		
		return "Country: "+name+" | population:"+population;
	}
}

创造另一个程序 HashMapMain.java

package org.igi.theitroad;
 
import java.util.HashMap;
 
public class HashMapMain {
 
	public static void main(String args[])
	{
		Country Netherlands=new Country("Netherlands",10000);
		Country japan=new Country("Japan",3000);
		Country france=new Country("France",5000);
		Country russia=new Country("Russia",4000);
		
		//HashMap with Country name as key and capital as value
		//HashMap stores elements in key value pairs
		HashMap<Country,String> countryCapitalMap=new HashMap<>();
		countryCapitalMap.put(Netherlands,"Delhi");
		countryCapitalMap.put(japan,"Tokyo");
		countryCapitalMap.put(france,"Paris");
		countryCapitalMap.put(russia,"Moscow");
 
		System.out.println("-----------------------------");
		//Iterating HashMap Using keySet() and for each loop
		System.out.println("Iterating HashMap Using keySet() and for each loop");
		for (Country countryKey:countryCapitalMap.keySet()) {
			System.out.println(countryKey +" and  Capital:"+countryCapitalMap.get(countryKey));
 
		}
		System.out.println("-----------------------------");
	}
 
}

在java中排序hashmap

按键排序

我们可以使用Treemap对HashMap中的键进行排序。

我们只需要将HashMap传递给Treemap的构造函数。

按值排序

我们需要遵循以下步骤来按值对HashMap进行排序。

  • 得到 entrySet()来自Hashmap.
  • 将entryset转换为 List
  • 在比较器的帮助下对列表进行排序
  • 迭代列表并将条目对象放在LinkedHashMap中

让我们编写一个例子来按键和值对HashMap进行排序。
我们将创建一个名为车辆的类,并将其用作HashMap中的键,值将是车辆的所有者。

创建一个名为的类 Vehicle.java

package org.igi.theitroad;
 
public class Vehicle implements Comparable<Vehicle>{
 
	String name;
	long maxSpeed;
	
	public Vehicle(String name, long maxSpeed) {
		super();
		this.name = name;
		this.maxSpeed = maxSpeed;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public long getMaxSpeed() {
		return maxSpeed;
	}
	public void setMaxSpeed(long maxSpeed) {
		this.maxSpeed = maxSpeed;
	}
	
	@Override
	public String toString() {
		return "Vehicle name: "+name+"|Max speed: "+maxSpeed;
	}
 
	@Override
	public int compareTo(Vehicle v) {
		return this.getName().compareTo(v.getName());
	}
}

请注意,我们在此实现了可比的接口,将两个车辆与其名称进行比较。

这个 Comparable将使用键在构建Treemap时按键对其进行排序。

创建一个名为的类 HashMapSortMain.java

package org.igi.theitroad;
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
 
public class HashMapSortMain {
 
	public static void main(String[] args) {
 
		Vehicle v1=new Vehicle("Car", 150);
		Vehicle v2=new Vehicle("Truck", 130);
		Vehicle v3=new Vehicle("Bike", 150);
		Vehicle v4=new Vehicle("Jeep", 180);
		
		//HashMap stores elements in key value pairs
		HashMap<Vehicle,String> vehicleOwnerMap=new HashMap<>();
		vehicleOwnerMap.put(v1,"John");
		vehicleOwnerMap.put(v2,"Chris");
		vehicleOwnerMap.put(v3,"Mary");
		vehicleOwnerMap.put(v4,"Harry");
		
		//Sort by keys 
		//As Vehicle class implements Comparable which defines sorting by vehicle name
		TreeMap<Vehicle,String> treeMap=new TreeMap<Vehicle,String>(vehicleOwnerMap);
		System.out.println("Sorted TreeMap by vehicle name: "+treeMap);
		
		//Sort by values
		Set<Entry<Vehicle, String>> entrySet = vehicleOwnerMap.entrySet();
		List<Entry<Vehicle, String>> vehicleEntryList=new ArrayList<>(entrySet);
		Collections.sort(vehicleEntryList,(e1,e2) -> 
		                 e1.getValue().compareTo(e2.getValue()));
		
		LinkedHashMap<Vehicle, String> lmp=new LinkedHashMap<Vehicle, String>();
		vehicleEntryList.forEach((entry)-> {
			lmp.put(entry.getKey(), entry.getValue());
		});
		System.out.println("Sorted Map by owner name: "+lmp);
	}
}

如果我们不明白我们在上面使用的Lambda表达式的语法来对输入对象的排序列表,则需要通过Java 8中的Lambda表达式。

Hashmap线程安全吗?

默认情况下,HashMap不是线程安全的,并且在多线程环境的情况下,它可以给出非确定性结果。
让我们在一个例子的帮助下演示这一点:

package org.igi.theitroad;
 
import java.util.Collections;
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 Counter {
 
	public static void main(String[] args) throws InterruptedException {
 
		Map<String, Integer> counterMap=new HashMap<>();
		
		counterMap.put("counter1",0);
		counterMap.put("counter2",100);
		
		ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);
		
		Runnable counterTask = () ->
		{
			incrementTime(counterMap,"counter1");
			incrementTime(counterMap,"counter2");
		
		};
		
		for (int i = 1; i <= 100; i++) {
			newFixedThreadPool.submit(counterTask);
		}
		
		newFixedThreadPool.shutdown();
		newFixedThreadPool.awaitTermination(30, TimeUnit.SECONDS);
		
		System.out.println("Time for Counter1: "+counterMap.get("counter1"));
		System.out.println("Time for Counter2: "+counterMap.get("counter2"));
	}
	
	public static void incrementTime(Map<String,Integer> counterMap,String counter)
	{
		Integer count = counterMap.get(counter)
		count++;
		counterMap.put(counter,count);
	}
}

我在MAP中将两个条目放在MAP中,键分别为COURCT1和COUNTER2,并且值分别为时间0和100.WE创建了一个任务,该任务将逆1递增1,并且我们正在使用ExecuterService将其提交100次。

让我们运行程序并检查输出:

计数器的时间1:逆2:195的95次

但我们的预期输出应该是

计数器的时间1:逆2:200的100次

由于我们提交了100次并在每个任务执行中提交了任务,所以它调用incrementTime()并增加时间1.让我们再次运行程序。

Time for Counter1: 98
Time for Counter2: 197

它与上次执行不同,这是由于HashMap中的线程安全问题。

我们可以以两种方式解决此主题安全问题:

  • CollectionS.SynchronizeMap.
  • concurrenthashmap.

CollectionS.SynchronizeMap.

我们可以用 Collections.synchronizedMap()要使HashMap线程的所有操作安全,并使IncrementTime()方法同步以解决上述问题。
递增时间()也应该同步,否则会有明确的问题。

package org.igi.theitroad.HashMap;
 
import java.util.Collections;
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 Counter {
 
	public static void main(String[] args) throws InterruptedException {
 
		Map<String, Integer> counterMap=Collections.synchronizedMap(new HashMap<>());
		
		counterMap.put("counter1",0);
		counterMap.put("counter2",100);
		
		ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);
		
		Runnable counterTask = () ->
		{
			incrementTime(counterMap,"counter1");
			incrementTime(counterMap,"counter2");
		
		};
		
		for (int i = 1; i <= 100; i++) {
			newFixedThreadPool.submit(counterTask);
		}
		
		newFixedThreadPool.shutdown();
		newFixedThreadPool.awaitTermination(30, TimeUnit.SECONDS);
		
		System.out.println("Time for Counter1: "+counterMap.get("counter1"));
		System.out.println("Time for Counter2: "+counterMap.get("counter2"));
	}
	
	public static synchronized void incrementTime(Map<String,Integer> counterMap,String counter)
	{
		Integer count = counterMap.get(counter);
		count++;
		counterMap.put(counter,count);
	}
 
}

正如我们所看到的,我们在使用后得到正确的输出 Collections.synchronizedMap()和制作 incrementTime同步。

concurrenthashmap.

使用集合的缺点。

HashMap如何在内部工作

此主题值得单独的帖子,因此我已经写了完整的教程,就HashMap如何在Java中工作。

Java 8 HashMap更新

要了解这一变化,我们需要了解HashMap在内部工作原理。
在太多哈希碰撞的情况下,Java 8引入了良好的性能改进。

在Java 7之前,如果两个对象具有相同的哈希码并且不等于,则两者都将存储在相同 bucket在单独的名单的帮助下。

如果有太多的哈希碰撞,那么性能 get()put()可能会受苦。
在最坏的情况下,如果所有键都具有相同的哈希码 get()HashMap的操作可能需要O(n)时间。

Java 8在Java 8中更新,HashMap将链接列表更改为二进制树,以便在元素数量达到某个阈值时。

在此更改的帮助下,HashMap中的Get()操作可能需要在最坏情况下o(log(n))时间。