本人所有文章,暂时只作为个人闲暇之余的爱好,如偶有客官路过,权当我是打酱油的吧。
作为一个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的实例。