HashMap 学习之旅 一

 

本人所有文章,暂时只作为个人闲暇之余的爱好,如偶有客官路过,权当我是打酱油的吧。

 

作为一个Java的完全新手, 网上看看Java的Map还是很神奇的,看看文章学习学习,写几段小代码记录记录。

 

一、最简单的自定义HashMap,用于测试HashMap本身的一些属性。

 

/*
 * 自定义一个HashMap,key为Integer,value为String
 */
public class MyHashMapS01 extends HashMap<Integer, String>{
	/*
	 * Constructor
	 * 啥事也不做,这种应用可以直接使用HashMap,并不需要自定义新类,这里只是为了方便管理代码
	 */
	public MyHashMapS01(){
		Log.d(TAG, "Enter MyHashMapS01 constructor!");
	}
	
	
	/*
	 * for debug
	 * 其实这种HashMap并不需要继承,直接用HashMap也可。
	 */
	private static MyHashMapS01 map;
	public static void main(){
		map = new MyHashMapS01();
		
		testOnKey();
		
		
	}
	private static void testOnKey(){
		map.put(1, "First one!");
		map.put(1, "First!");
		map.put(2, "Second!");
		
		//	证明同一个key只存在一个mapping,后者覆盖前者
		Log.d(TAG, "size: " + map.size());	//	2
		Log.d(TAG, "1: " + map.get(1));		//	First!
		
		map.remove(1);
		map.remove(3);		//	不会挂掉,没有Exception
		
		Log.d(TAG, "1: " + map.get(3));		//	null
	}
	
	private static final long serialVersionUID = 1L;
	private static final String TAG="MyHashMapS01";
}


 二、下面的例子中Value为一个自定义的类,并且使用了iterator遍历HashMap中的Mapping(entry)。

 

package com.maxuming.sparrow.hashmap;

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

import android.util.Log;

public class MyHashMapS02{
	
	/*
	 * For test
	 */
	private HashMap<Integer, MyData> map;
	public void main(){
		map = new HashMap<Integer, MyData>();
		
		testOnValue();
		
		testIterator();
	}
	private void testOnValue(){
		MyData value;
		value = new MyData(22, "Jack Li");
		map.put(1, value);
		value = new MyData(60, "Steve Paul Jobs");
		map.put(2, value);
		value = new MyData(28, "Smith Li");
		map.put(3, value);
		value = new MyData(99);
		map.put(4, value);
		value = new MyData("Bennett");
		map.put(5, value);
	
		//	可以分别获取value中的内容
		Log.d(TAG, "1: " + "Age: " + map.get(1).age + ", Name: " + map.get(1).name);
		Log.d(TAG, "4: " + "Age: " + map.get(4).age + ", Name: " + map.get(4).name);
	}
	private void testIterator(){
		
		//	Iterator 需要指明类型为 Entry
		//	Entry就是Map的 key/value mapping
		//	并且指定Entry的key和value的类型分别为Integer 和 MyData
		//	迭代的并不是HashMap,而是其中的mapping,即entrySet
		Iterator<Entry<Integer, MyData>> i = map.entrySet().iterator();
		while(i.hasNext()){
			
			//	这里可以用HashMap.Entry,也可以直接使用Map.Entry
			HashMap.Entry<Integer, MyData> item = i.next();
			
			//	item.getKey() 和 getValue() 只要是Entry就可以调用
			Log.d(TAG, "Key: " + item.getKey());
			
			//	age 和 name 只有在指定Entry的value类型为MyData的时候方可访问
			Log.d(TAG, "Age: " + item.getValue().age);
			Log.d(TAG, "Name: " + item.getValue().name);
				
		}
		
		//	从多次执行的结果来看,没有严格的顺序,map中内容发生变化的时候顺序会变化
	}
	
	/*
	 * 自定义个一个class,作为HashMap的value
	 */
	class MyData{
		public int age;
		public String name;
		
		public MyData(int age, String name){
			this.age = age;
			this.name = name;
		}
		public MyData(int age){
			this.age = age;
			this.name = "Unknown";
		}
		public MyData(String name){
			age = 0;
			this.name = name;
		}
	}

	private static final String TAG="MyHashMapS02";
}

 

通常我们获取mapping时,使用map.get(Object K)或者iterator中的item.getKey(), item.getValue() 。上面的例子中,key为基本类型Integer,value为非基本类型,自定义的class。那么反过来是什么样子的呢?即key为非基本类型,value为基本类型(或者非基本类型),参见下例。

 

三、下面的例子中,key为非基本类型,value为基本类型(也可以设置成非基本类型)。

 

package com.maxuming.sparrow.hashmap;

import java.util.HashMap;

import android.util.Log;

public class MyHashMapS04 {
	
	public void main(){
		Log.d(TAG, "Enter MyHashMapS04!");
		
		init();
		test();
	}
	
	private void init(){
		map = new HashMap<MyKey, Integer>();
		
		MyKey key;	//	reuse
		//	Add several mappings
		key = new MyKey(0, "Zero!");
		map.put(key, 10);
		
		key = new MyKey(1, "One!");
		map.put(key, 11);
		
		key = new MyKey(2, "Two!");
		map.put(key, 12);
		
		key = new MyKey(3, "Three!");
		map.put(key, 13);
		specialKey = key;		//	保存
		
		key = new MyKey(4, "Four!");
		map.put(key, 14);
	}
	private MyKey specialKey;
	private void test(){
		int value = map.get(specialKey);
		Log.d(TAG, "value: " + value);		// 13
		
		
		MyKey key = new MyKey(3, "Three!");
		Log.d(TAG, "new key value: " + map.get(key));	//	null
		//	这个表明内容相同的另一个实例并不能替代key
		//	也就是说get方法中并没有去判断key的内容,只是直接判断参数key是否存在map中。
		
	}
	
	/*
	 * 自定义一个class,作为key
	 */
	class MyKey{
		public int index;
		public String name;
		public MyKey(){
			index = 0;
			name = null;
		}
		public MyKey(int index){
			this.index = index;
			name = null;
		}
		public MyKey(String name){
			this.name = name;
			index = 0;
		}
		public MyKey(int index, String name){
			this.index = index;
			this.name = name;
		}
	}
	private HashMap<MyKey, Integer> map;
	private static final String TAG="MyHashMapS04";
}


从Exception可以说明,内容相同的另外一个实例并不能作为之前put到map中的key使用。

那么,每次从map中获取mapping比较合理的方式是不是用枚举呢?因为一把不会保存所有的key的实例的吧?

 

 

四、下面的例子学习一下HashMap的几个构造函数,重点关注一下比较难理解的capacity和loadFactor。

HashMap总共有四个构造函数,如下图:

 

其中第一个和第四个都比较容易理解,第二、第三两个中的参数capaticy和loadFactor比较难理解。

先看下面的代码:

package com.maxuming.sparrow.hashmap;

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

import android.util.Log;

public class MyHashMapS03 {
	
	/*
	 * For test
	 */
	public void main(){
		Log.d(TAG, "Enter " + TAG);
		
		testConstructor02();
		
		testConstructor03();
		
	}
	private void testConstructor02() {
		//	调用HashMap(int capacity)
		map = new HashMap<Integer, String>(10);
		
		//	初始化分配大小并不能直接生成相应的mapping
		Log.d(TAG, "Size: " + map.size());	//	0
		
		for(int i = 0; i < 200; i++){
			map.put(i, "value " + i);
			
			//	其实在这期间HashMap做了好几次扩容了
			//	从Java的代码来看,扩容的size临界值为 (capacity * loadFactor)
			//	而capacity的初始默认值为 16
			//	loadFactor的初始默认值为 0.75,文档里提到这个0.75是时间和空间的均衡值
		}
		Log.d(TAG, "Size: " + map.size());	//	200
		
	}
	private void testC0201() {
		for(int i = 0; i < 10000; i++){
			map.put(i, "value " + i);
		}
		
		long start = System.currentTimeMillis();
		Iterator<Entry<Integer, String>> i = map.entrySet().iterator();
		while(i.hasNext()){
			HashMap.Entry<Integer, String> item = i.next();
			if(item.getValue() == "no value!"){
				Log.d(TAG, "Got the no value item!");
			}
		}
		long escape = System.currentTimeMillis() - start;
		Log.d(TAG, "Escape time: " + escape + " ms!");
	}
	
	private void testConstructor03(){
		//	调用HashMap(int capacity, float loadFactor)
		Log.d(TAG, "0.1 loadFactor!");
		map = new HashMap<Integer, String>(16, (float)0.1);
		testC0201();
		
		//	调用HashMap(int capacity, float loadFactor)
		Log.d(TAG, "default loadFactor!");
		map = new HashMap<Integer, String>(16);
		testC0201();
		
		//		调用HashMap(int capacity, float loadFactor)
		Log.d(TAG, "21 loadFactor!");
		map = new HashMap<Integer, String>(16, (float)21);
		testC0201();
		
		//	很奇怪的是,上面三种配置执行下来,Escape time并没有明显区别!!!!
	}
	
	private HashMap<Integer, String> map;
	private static final String TAG="MyHashMapS03";
}


 特别需要注意的是:

testConstructor03中三种参数,运行结果显示并没有明显的或者特定的区别!

虽然字面意义以及Java的代码可以说明capacity和loadFactor的作用,但是实现效果为什么呢,我试验的方法有问题?

 

 

 小结:

1、HashMap比较方便,因为key和value都是泛型的。

2、一般情况下直接使用HashMap即可,没有必要自己写一个class继承HashMap,因为HashMap包含了基本会用到的所有功能。

3、HashMap做iterator的时候,mapping的顺序不一定。

4、HashMap的capacity和loadFactor作用没有被我证实!

5、HashMap的key为非基本类型的时候,需要注意,只有同一个key的实例才能作为map的下标,内容相同的另外一个实例并不能代替key的实例。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值