<h3>写在开始之前:</h3>
Java为数据结构中的映射定义了一个接口java.util.Map,此接口主要有四个常用的实现类,分别是HashMap、Hashtable、LinkedHashMap和TreeMap。
(1) HashMap:它根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不确定的。 HashMap最多只允许一条记录的键为null,允许多条记录的值为null。HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。
(2) Hashtable:Hashtable是遗留类,很多映射的常用功能与HashMap类似,不同的是它承自Dictionary类,并且是线程安全的,任一时间只有一个线程能写Hashtable,并发性不如ConcurrentHashMap,因为ConcurrentHashMap引入了分段锁。Hashtable不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换。
(3) LinkedHashMap:LinkedHashMap是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。
(4) TreeMap:TreeMap实现SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。如果使用排序的映射,建议使用TreeMap。在使用TreeMap时,key必须实现Comparable接口或者在构造TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常。
本文介绍的HashMap主要基于JDK 1.7 喽,JDK 1.8 里对HashMap做了一些优化,比如采用使用红黑树结构、优化扩容算法等在本文不做介绍。
<h3>背景介绍</h3>
在做改版过程中,有个地方是5个并排的按钮,分别跳转到不同的页面。机智如我写下了如下代码来初始化5个按钮(Key是按钮对应资源文件ID,Value是按钮点击事件):
private HashMap<Integer, View.OnClickListener> resources = new HashMap<>();
结果发现5个按钮的显示顺序与添加顺序不一致。才突然间记起HashMap是不能保证顺序的。那么为什么它不能保证顺序呢?它是如何存储数据的呢?
<h4>简介</h4>
Java 中HashMap是Map的最常用的实现类。HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key\value都可以为null。
<h4>数据存取</h4>
HashMap是基于Hashing的原理,我们一般使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。
当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到Bucket位置来储存Entry对象。
如果存储的时候遇到不同的对象有相同的hashCode呢?
需要注意的是,Bucket是一个LinkedList结构。如果多个元素的Key对象的HasCode相同,则存储于同一个LinkedList。而LinkedList中包含了每个元素的键值信息,所以当取数据遇到Key对应的HashCode相同的情况也可以通过Key值的比较来查找到想要的元素。
<h4>加载因子</h4>
加载因子是表示Hsah表中元素的填满的程度.加载因子越大,填满的元素越多,空间利用率高了,但冲突的机会加大了。反之,加载因子越小填满的元素越少,好处是冲突的机会减小了,但空间浪费多了。冲突的机会越大,则查找的成本越高.反之,查找的成本越小.因而,查找时间就越小.因此,必须在 "冲突的机会"与"空间利用率"之间寻找一种平衡与折衷. 这种平衡与折衷本质上是数据结构中有名的"时-空"矛盾的平衡与折衷.
默认情况下HashMap是自增的,它的初始长度是16,加载因子是 0.75 。当HashMap中的条目数超出了加载因子与当前容量的乘积时,通过调用 rehash 方法将容量 <strong> 翻倍 </strong>。
<h4>Android中的HashMap</h4>
因为HashMap内存使用不充足。在内存相对吃紧的移动客户端简直是浪费嘛。
所以Google提供了两个新的数据结构 SparseArray和ArrayMap来供大家使用。但是这两个数据结构由于各自的局限性,并不能完全取代HashMap。SparseArray只能适用于key为int的数据结构。而ArrayMap的问题是在大数据量下性能较低。所以,三者之中具体选用哪个还得看情况而定哦。
说了这么多,那5个按钮到底如何保证顺序呢?
答:使用LinkedHashMap!
网友评论