Java之映射(map)

目录

1.两种常用映射比较

2.基本映射操作

3.更新映射项


集是一个集合,它可以快速地查找现有的元素。但是,要查看一个元素,需要有要查找元素的精确副本。这不是一种非常通用的查找方式,因为在集合中查找元素总是要遍历集合。通常,我们知道某些键的信息,并想要查找与之对应的元素。映射(map)数据结构就是为此而设计的。映射用来存放键/值对。如果提供了键,就能够查找到值。它们之间是一一对应关系。


----注:以下所有介绍基于java 8(java 1.8)----

1.两种常用映射比较

  • Java类库为映射提供了常用的两个通用的实现:HashMap和TreeMap,这两个类都实现了Map接口。
  • HashMap对键进行散列,TreeMap用键的整体顺序对元素进行排序,并将其组织成搜索树。
  • 散列或比较函数只能作用于键。与键关联的值不能进行散列或比较。
  • HashMap比TreeMap稍微快一些,所以在不需要按照排列顺序访问键的时候,最好选用HashMap。
  • HashMap有一个子类也比较常用——LinkedHashMap,这个映射集合和HashMap的不同点就是,它将键/值对以插入顺序进行排列,即遍历的时候会按照插入的顺序进行访问元素。

2.基本映射操作

  • OP->>要进行键值存储,必须使用put方法。键必须是唯一的,如果对一对映射调用两次put方法,则后一次调用会覆盖前一次调用,并返回第一次调用的结果。
  • OP->>要进行键值访问,必须使用get方法,且只能通过键来访问到值。
  • OP->>如果找不到值却不想返回一个空对象,则使用getOrDefault(var1,var2),如果找不到值则返回var2。
  • OP->>要进行键值对的移除,则要使用remove(key)的方法。
  • OP->>要想获取键值对的数量,则要使用size()方法。
  • OP->>要迭代处理每个键和值,最好是使用forEach方法,可以向这个方法提供一个lambda表达式,用来依次处理映射中的每一个元素。比如 scores.forEach((k,v)->System.out.println("key="+k+",value="+v));

 

下面示例程序演示了映射的操作过程,首先将键/值对添加到映射中。然后从映射中删除一个键,同时与之对应的值也被删除了。接下来,修改与某一个键对应的值,并调用get方法查看这个值。最后,迭代处理该映射集。

代码:

package Collection.Map;

import java.util.*;

public class MapTest {
	public static void main(String[] args) {
		Map<String,Employee> staff=new HashMap<>();
		staff.put("144-25-5464", new Employee("Amy Lee"));
		staff.put("567-24-2546", new Employee("Harry Hacker"));
		staff.put("157-62-7935", new Employee("Gary Cooper"));
		staff.put("456-62-5527", new Employee("Francesca Cruz"));
		
		//打印所有成员
		System.out.println(staff);
		//删除某一成员
		staff.remove("567-24-2546");
		//替代一个成员
		staff.put("456-62-5527", new Employee("Francesca Miller"));
		//查看一个成员
		System.out.println(staff.get("157-62-7935"));
		//遍历映射
		staff.forEach((k,v)->System.out.println("key="+k+",value="+v));
	}
}

class Employee{
	String name;

	public Employee(String name) {
		this.name = name;
	}

	public String toString() {
		return "[name="+name+"]";
	}
}

运行结果:

 

下面是Map接口中常用的几个方法及其解释(从JDK 8文档copy的)

  • V get(Object key)

获取与键对应的值;返回与键对应的对象,如果在映射中没有这个对象则返回null。键可以为null。

  • default V getOrDefault(Object key,V defaultValue)

获得与键关联的值;返回与键关联的对象,或者如果未在映射中找到这个键,则返回defaultValue。

  • V put(K key,V value)

将键与对应的值关系插入到映射中。如果这个键已经存在,新的对象将取代与这个键对应的旧对象。这个方法将返回键对应的旧值。如果这个键以前没有出现过则返回null。键可以为null,但值不能为null。

  • void putAll(Map<? extends K,? extends V> entries)

将给定映射中所有条目添加到这个映射中。

  • boolean containsKey(Object key)

如果映射中已经有这个键,则返回true。

  • boolean containsValue(Object value)

如果映射中已经有这个值,返回true。

  • default void forEach(BiConsumer<? super K,? super V> action)

对这个映射中的所有键/值应用这个动作。

 

下面是HashMap类中常用的几个方法及其解释(从JDK 8文档copy的)

  • HashMap()

默认构造器

  • HashMap(int initialCapacity)

用给定的容量构造一个空散列映射

  • HashMap(int initialCapacity,float loadFactor)

用给定的容量和装填因子构造一个空散列映射(装填因子是一个0.0~1.0之间的一个数值。这个数值决定散列表填充百分比。一旦到了这个比例,就要将其再散列到更大的表中)。默认的装填因子是0.75。

 

下面是TreeMap类中常用的几个方法及其解释(从JDK 8文档copy的)

  • TreeMap()

为实现Comparable接口的键构造一个空的树映射

  • TreeMap(Comparator<? super K> c)

构造一个树映射,并使用一个指定的比较器对键进行排序

  • TreeMap(Map<? extends K,? extends V> entries)

构造一个树映射,并将某个映射中的所有条目添加到树映射中

  • TreeMap(SortedMap<? extends K,? extends V> entries)

构造一个树映射,将某个有序映射中的所有条目添加到树映射中,并使用与给定的有序映射相同的比较器。

 

下面是SortedMap接口(TreeMap实现了这个接口)的几个方法及其解释(从JDK 8文档copy的)

  • Comparator<? super K> comparator()

返回键进行排序的比较器。如果键是用Comparable接口的comparaTo方法进行比较的,返回null

  • K firstKey()
  • K lastKey()

返回映射中最小元素和最大元素


3.更新映射项

我们从前面的方法中知道,更新一个映射项使用的是put方法,但是,考虑下面一种情况,假如我想将下面一段话进行单词统计,然后将得到的结果存放到一个映射表中。

句子:I am a handsome boy and you are a beautiful girl , so we can marry , then make love , finally create new lives . I believe certainly that our son will be a handsome boy and daughter will be a beautiful girl too , because I am a handsome boy and you are a beautiful girl .

我们可以定义一个counts的映射表。如果我们想向里面添加元素,我们会像如下那样做:

counts.put(word,counts.get(word)+1);

但是有一个问题,如果原先映射表中不存在这个单词,get方法就会返回一个null,这个时候就会抛出一个空指针异常。代码和结果如下:

package Collection.Map;

import java.util.*;

public class MapTest2 {

	public static void main(String[] args) {
		String words="I am a handsome boy and you are a beautiful girl , so we can marry , then make love , finally create new lives . I believe certainly "
				+ "that our son will be a handsome boy and daughter will be a beautiful girl too , because I am a handsome boy and you are a beautiful girl .";
		Map<String,Integer> counts=new HashMap<>();
		Scanner in=new Scanner(words);
		String word=null;
		while(in.hasNext()) {
			word=in.next();
			counts.put(word, counts.get(word)+1);
		}
		in.close();
		System.out.println(counts);
	}

}

结果:

如果就使用前面我们所介绍方法,可以有以下两种解决方案,第一种是在使用put方法之前首先进行检查,第二种是使用getOrDefault方法。

下面是两种示例代码:

第一种:

package Collection.Map;

import java.util.*;

public class MapTest2 {

	public static void main(String[] args) {
		String words="I am a handsome boy and you are a beautiful girl , so we can marry , then make love , finally create new lives . I believe certainly "
				+ "that our son will be a handsome boy and daughter will be a beautiful girl too , because I am a handsome boy and you are a beautiful girl .";
		Map<String,Integer> counts=new HashMap<>();
		Scanner in=new Scanner(words);
		String word=null;
		while(in.hasNext()) {
			word=in.next();
			if(counts.get(word)==null) {
				counts.put(word,1);
			}else {
				counts.put(word, counts.get(word)+1);
			}	
		}
		in.close();
		System.out.println(counts);
	}
}

第二种:

package Collection.Map;

import java.util.*;

public class MapTest2 {

	public static void main(String[] args) {
		String words="I am a handsome boy and you are a beautiful girl , so we can marry , then make love , finally create new lives . I believe certainly "
				+ "that our son will be a handsome boy and daughter will be a beautiful girl too , because I am a handsome boy and you are a beautiful girl .";
		Map<String,Integer> counts=new HashMap<>();
		Scanner in=new Scanner(words);
		String word=null;
		while(in.hasNext()) {
			word=in.next();
			counts.put(word, counts.getOrDefault(word, 0)+1);
		}
		in.close();
		System.out.println(counts);
	}

}

结果:...

除了像以上那样进行修改,下面还可以提供两种方法使其做的更好:

第一种是使用putIfAbsent方法,从字面意思上理解,就是不在的时候放入,正如我们所想,就是这样。
具体在这个例子中使用的时候首先调用这个方法,如果不存在就放入一个0,然后调用put方法。第二种就是使用merge方法,merge方法是解决这个问题最好的方法,merge方法可以简化这个常见的操作,如果原先的键不存在,下面的调用:counts.merge(word,1,Integer::sum);将把word与1关联,否则使用Integer::sum方法组合原值和1(也就是将原值和1求和)。下面演示这两个解决方案:

方案一:

package Collection.Map;

import java.util.*;

public class MapTest2 {

	public static void main(String[] args) {
		String words="I am a handsome boy and you are a beautiful girl , so we can marry , then make love , finally create new lives . I believe certainly "
				+ "that our son will be a handsome boy and daughter will be a beautiful girl too , because I am a handsome boy and you are a beautiful girl .";
		Map<String,Integer> counts=new HashMap<>();
		Scanner in=new Scanner(words);
		String word=null;
		while(in.hasNext()) {
			word=in.next();
			counts.putIfAbsent(word, 0);
			counts.put(word, counts.get(word)+1);
		}
		in.close();
		System.out.println(counts);
	}

}

方案二:

package Collection.Map;

import java.util.*;

public class MapTest2 {

	public static void main(String[] args) {
		String words="I am a handsome boy and you are a beautiful girl , so we can marry , then make love , finally create new lives . I believe certainly "
				+ "that our son will be a handsome boy and daughter will be a beautiful girl too , because I am a handsome boy and you are a beautiful girl .";
		Map<String,Integer> counts=new HashMap<>();
		Scanner in=new Scanner(words);
		String word=null;
		while(in.hasNext()) {
			word=in.next();
			counts.merge(word, 1, Integer::sum);
		}
		in.close();
		System.out.println(counts);
	}

}

结果同上。

 

下面给出了几个常见的关于更新映射项的方法(从JDK 8文档copy的)

  • default V merge(K key,V value,Bifunction<? super Vzaa,? super V,? extends V> remappingFunction)

如果key与一个非null值v关联,将函数应用到v和value,将key与结果关联,或者如果结果为null,则删除这个键。否则,将key与value关联,返回get(key)

  • default V compute(K key,Bifunction<? super K,? super V,? extends V> remappingFunction)

将函数应用到key和get(key)。将key与结果相关联。或者如果结果为null,则删除这个键,返回get(key)

  • default V computeIfPresent(K key,Bifunction<? super K,? super V,? extends V> remappingFunction)

如果key与一个非null值v关联,将函数应用到key和v,将key与结果关联,或者如果为null,则删除这个键。返回get(key)

  • default V computeIfAbsent(K key,Function<? super K,? extends V> mappingFunction)

将函数应用到key,除非key与一个非null值关联。键key与结果关联,或者如果结果为null,则删除这个键,返回get(key)

  • default replaceAll(Bifunction<? super K,? super V,? extends V> remappingFunction)

在所有映射项上应用函数。将键与非null结果关联,对于null结果,则将相应的键删除。


4.映射视图

有时候我们需要查看映射中的键集合,值集合(因为值可能存在相同的元素,所以严格来说不算是一个集合),以及键/值对集合。

下面三种方式分别返回键集合,值集合,键/值对集合

Set<K> keySet()
Collection<V> values()
Set<Map.Entry<K,V>> entrySet()

下面一个程序演示了以上方法的调用,及其使用意义:

代码:

package Collection.Map;

import java.util.*;
import java.util.Map.Entry;

public class MapView {
	public static void main(String[] args) {
		Map<String,Integer> test=new HashMap<>();
		test.put("于理想",1);
		test.put("张琛",2);
		test.put("张艳杰",3);
		test.put("张哲鑫",4);
		test.put("闫智",5);
		test.put("孙崇伟",6);
		
		//下面枚举键:
		Set<String> keys=test.keySet();
		for(String key:keys) {
			System.out.print(key+" ");
		}
		System.out.println();
		
		//下面枚举值:
		Collection<Integer> values=test.values();
		for(Integer value:values) {
			System.out.print(value+" ");
		}
		System.out.println();
		
		//下面枚举键/值对
		Set<Map.Entry<String, Integer>> mapping=test.entrySet();
		for(Map.Entry<String, Integer> map:mapping) {
			System.out.print(map+" ");
		}
		System.out.println();
		//注意:上面最后一个曾经是访问所有映射条目的最有效的方法,但是,现在只需要使用forEach方法即可;
		test.forEach((k,v)->System.out.println("键="+k+",值="+v));
	}
}

运行结果:

 

参考资料《Java核心技术 卷I 第10版》

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Kirito

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值