HashMap 优化: SparseArray / ArrayMap

HashMap

参考:

HashMap详解以及源码分析

链表实战(带图分析)

我画了近百张图来理解红黑树

Hash算法解决冲突的四种方法

SparseArray

参考: SparseArray详解及源码简析

  • 其内部主要通过 2 个数组来存储 key 和 value,分别是 int[] 和 Object[]。这也限定了其 key 只能为 int 类型,且 key 不能重复,否则会发生覆盖。

  • 一切操作都是基于二分查找算法,将 key 以升序的方法 “紧凑” 的排列在一起,从而提高内存的利用率以及访问的效率。相比较 HashMap 而言,这是典型的时间换空间的策略。

  • 删除操作并不是真的删除,而只是标记为 DELETE,以便下次能够直接复用。

SparseArray是android里为<Interger,Object>这样的Hashmap而专门写的类,目的是提高内存效率,其核心是折半二分查找函数(binarySearch)。

注意内存二字很重要,因为它仅仅提高内存效率,而不是提高执行效率,
它只适用于android系统(内存对android项目有多重要,地球人都知道)。

SparseArray有两个优点:

  • 1.避免了自动装箱(auto-boxing),
  • 2.数据结构不会依赖于外部对象映射。

我们知道HashMap 采用一种所谓的“Hash 算法”来决定每个元素的存储位置,存放的都是数组元素的引用,通过每个对象的hash值来映射对象。而SparseArray则是用数组数据结构来保存映射,然后通过折半查找来找到对象。但其实一般来说,SparseArray执行效率比HashMap要慢一点,因为查找需要折半查找,而添加删除则需要在数组中执行,而HashMap都是通过外部映射。但相对来说影响不大,最主要是SparseArray不需要开辟内存空间来额外存储外部映射,从而节省内存。

//SparseArray
public class SparseArray<E> implements Cloneable {

    private int[] mKeys;
    private Object[] mValues;
    private int mSize;

    public E get(int key, E valueIfKeyNotFound) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

        if (i < 0 || mValues[i] == DELETED) {
            return valueIfKeyNotFound;
        } else {
            return (E) mValues[i];
        }
    }
}    



//SparseBooleanArray
public class SparseBooleanArray implements Cloneable {

    private int[] mKeys;
    private boolean[] mValues;
    private int mSize;

    public boolean get(int key, boolean valueIfKeyNotFound) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

        if (i < 0) {
            return valueIfKeyNotFound;
        } else {
            return mValues[i];
        }
    }
}



//SparseIntArray
public class SparseIntArray implements Cloneable {
    private int[] mKeys;
    private int[] mValues;
    private int mSize;

    public int get(int key, int valueIfKeyNotFound) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

        if (i < 0) {
            return valueIfKeyNotFound;
        } else {
            return mValues[i];
        }
    }
}


//SparseLongArray
public class SparseLongArray implements Cloneable {
    private int[] mKeys;
    private long[] mValues;
    private int mSize;

    public long get(int key, long valueIfKeyNotFound) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

        if (i < 0) {
            return valueIfKeyNotFound;
        } else {
            return mValues[i];
        }
    }
}   

ArrayMap

参考: ArrayMap详解及源码分析

  • mHashs 数组以升序的方式保存了所有的 hash code,在查找数据时则通过二分查找 hash code 所对应的 index。这也是它的 get() 比 HashMap 慢的根据原因所在。

  • 通过 hash code 在 mHashs 数组里的 index 值来确定 key 以及 value 在 mArrays 数组中的存储位置。一般来说分别就是 index << 1 以及 index << 1 + 1。再简单点说就是 index * 2 以及 index * 2 + 1。

  • hashCode 必然可能存在冲突,这里是怎么解决的呢?简单点说就是,在 mHashs 中相邻地存多份 hash code,而在 mArray 中分别以它们的 index 来计算 key-value 的存储位置。

  • 当进行 remove 操作时,在一定条件下,可能会发生数据的压缩,从而节省内存的使用。

public final class ArrayMap<K, V> implements Map<K, V> {

    int[] mHashes;
    Object[] mArray;
    int mSize;

    private static int binarySearchHashes(int[] hashes, int N, int hash) {
        try {
            return ContainerHelpers.binarySearch(hashes, N, hash);
        } catch (ArrayIndexOutOfBoundsException e) {
            if (CONCURRENT_MODIFICATION_EXCEPTIONS) {
                throw new ConcurrentModificationException();
            } else {
                throw e; // the cache is poisoned at this point, there's not much we can do
            }
        }
    }

}

ContainerHelpers

package android.util;

class ContainerHelpers {

    // This is Arrays.binarySearch(), but doesn't do any argument validation.
    static int binarySearch(int[] array, int size, int value) {
        int lo = 0;
        int hi = size - 1;

        while (lo <= hi) {
            final int mid = (lo + hi) >>> 1;
            final int midVal = array[mid];

            if (midVal < value) {
                lo = mid + 1;
            } else if (midVal > value) {
                hi = mid - 1;
            } else {
                return mid;  // value found
            }
        }
        return ~lo;  // value not present
    }

    static int binarySearch(long[] array, int size, long value) {
        int lo = 0;
        int hi = size - 1;

        while (lo <= hi) {
            final int mid = (lo + hi) >>> 1;
            final long midVal = array[mid];

            if (midVal < value) {
                lo = mid + 1;
            } else if (midVal > value) {
                hi = mid - 1;
            } else {
                return mid;  // value found
            }
        }
        return ~lo;  // value not present
    }
}

什么时候使用SparseArray/ArrayMap

1、如果key的类型已经确定为int类型,那么使用SparseArray,因为它避免了自动装箱的过程,
如果key为long类型,它还提供了一个LongSparseArray来确保key为long类型时的使用

2、如果key类型为其它的类型,则使用ArrayMap

demo

        //1、适用于键为int的map数据结构类型
        //key为int的时候才能使用,注意是int而不是Integer,
        // 这也是sparseArray效率提升的一个点,去掉了装箱的操作
        //2、适用于小规模数据存储
        //3、内部使用二分查找进行数据查询,查询效率高
        //4、不需要单独开辟内存来映射对象,节约内存
        SparseArray<String> intSparseArray = new SparseArray<>();
        intSparseArray.put(1, "java");
        intSparseArray.put(2, "js");
        intSparseArray.put(3, "kotlin");
        intSparseArray.put(4, "flutter");

        ArrayMap<String, String> arrayMap = new ArrayMap<>();
        arrayMap.put("1", "java");
        arrayMap.put("2", "js");
        arrayMap.put("3", "kotlin");
        arrayMap.put("4", "flutter");

        HashMap<String, String> hashMap = new HashMap<>();
        hashMap.put("1", "java");
        hashMap.put("2", "js");
        hashMap.put("3", "kotlin");
        hashMap.put("4", "flutter");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值