Android中List、Set、Map数据结构详解

数据结构 同时被 2 个专栏收录
16 篇文章 0 订阅
65 篇文章 0 订阅

Android中一般使用的数据结构有java中的基础数据结构List,Set,Map。还有一些Android中特有的几个,SparseArray(使用Map时Key是int类型的时候可以用这个代替)等。

继承关系:

Collection<–List<–ArrayList

Collection<–List<–Vector

Collection<–List<–LinkedList

Collection<–Set<–HashSet

Collection<–Set<–HashSet<–LinkedHashSet

Collection<–Set<–SortedSet<–TreeSet

Map<–HashMap (补充一个HashMap的子类LinkedHashMap:)

Map<–SortedMap<–TreeMap

注:这里的 Collection、List、Set和Map都是接口(Interface),其中Collection是所有集合类的接口,Set和List也都实现该接口。

下面分别来介绍List、Set、Map。

List

List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。 List比较常用的有ArrayList和LinkedList,还有一个比较类似的Vector。

1、ArrayList

基于数组(Array)的List,ArrayList其实是对数组的动态扩充,底层的数据结构使用的是数组结构(数组长度是可变的百分之五十延长)。

特点:

对于数据的随机get和set或是少量数据的插入或删除,效率会比较高。ArrayList是线程不安全的,在不考虑线程安全的情况下速度也比较快的。ArrayList插入数据可以重复,也是有序的,按照插入的顺序来排序。性能上要比Vector(是线程安全的)好一些。

Vector

基于数组(Array)的List,Vector其实是对数组的动态扩充,底层的数据结构使用的是数组结构(数组长度是可变的百分之百延长)。

特点:

Vector的使用方法和内部实现基本和ArrayList相同,只不过它在add(), remove(), get()等方法中都加了同步。所以Vector是线程同步(sychronized)的,线程安全的。但是使用效率上就不如ArrayList了。

LinkedList

LinkedList不同于前面两个,是基于链表实现的双向链表数据结构。
它每一个节点(Node)都包含三方面的内容:

1、节点本身的数据(data)

2、前一个节点的信息(previousNode)

3、下一个节点的信息(nextNode)

所以当对LinkedList做添加,删除动作的时候就不用像基于数组的ArrayList一样,必须进行大量的数据移动。
只要更改nextNode的相关信息就可以实现了,这是LinkedList的优势。

LinkedList根据序号获取数据,是根据二分法进行遍历,如果序号小于总长度的一半,就从链表头部开始往后遍历,直到找到对应的序号。如果序号大于总长度的一半,就从链表尾部往前进行遍历,直到找到对应的序号。拿到数据。

List总结

1、所有的List中只能容纳单个不同类型的对象组成的表,而不是Key-Value键值对。例如:[ tom,1,c ]

2、所有的List中可以有相同的元素,例如Vector中可以有 [ tom,koo,too,koo ]

3、所有的List中可以有null元素,例如[ tom,null,1 ]

4、基于Array的List(Vector,ArrayList)适合查询,而LinkedList 适合添加,删除操作

Set

Set是一种不包含重复的元素的无序Collection。 一般使用的有HashSet和TreeSet。

虽然Set同List都实现了Collection接口,但是他们的实现方式却大不一样。List基本上都是以Array为基础。但是Set则是在 HashMap的基础上来实现的,这个就是Set和List的根本区别。

HashSet

HashSet是根据hashCode来决定存储位置的,是通过HashMap实现的,所以对象必须实现hashCode()方法,存储的数据无序不能重复,可以存储null,但是只能存一个。

看看 HashSet的add(Object obj)方法的实现就可以一目了然了。

public boolean add(Object obj) {   
   return map.put(obj, PRESENT) == null;   
}

这个也是为什么在Set中不能像在List中一样有重复的项的根本原因,因为HashMap的key是不能有重复的。

HashSet代码示例:

public class Main {

    public static void main(String[] args) throws IOException {
        hashSet();
    }

    public static void hashSet(){
        Set<String> set = new HashSet<String>();
        set.add("2");
        set.add("1");
        set.add(null);
        set.add("1");

        for(String s : set){
            System.out.println(s);
        }
    }
}

运行结果:

null
1
2

LinkedHashSet

HashSet的一个子类,一个链表。

它和HashSet的区别就在于LinkedHashSet的元素严格按照放入顺序排列。LinkedHashSet内部使用LinkedHashMap实现,所以它和HashSet的关系就相当于HashMap和LinkedHashMap的关系。如果你想让取出元素的顺序和插入元素的顺序完全相同,那么就使用LinkedHashSet代替HashSet。

LinkedHashSet也不是线程安全的。

TreeSet

SortedSet的子类,它不同于HashSet的根本就是TreeSet是有序的。它是通过SortedMap来实现的。TreeSet是根据二叉树实现的,也就是TreeMap,,放入数据不能重复且不能为null,可以重写comparator()方法来确定元素大小,从而进行升序排序。

代码示例:

public class Main {

    public static void main(String[] args) throws IOException {
        treeSet();
    }

    public static void treeSet(){
        //通过传入MyComparator对象自定义的排序方法,来实现从大到小的排序。
        Set<Integer> treeSet = new TreeSet<>(new MyComparator());
        treeSet.add(1);
        treeSet.add(3);
        treeSet.add(4);
        treeSet.add(2);

        for(Integer i : treeSet){
            System.out.println(i);
        }
    }

    static class MyComparator implements Comparator<Integer>{

        @Override
        public int compare(Integer o1, Integer o2) {
            if(o1 < o2 ){
                return -1;
            }
            if(o1 == o2 ){
                return 0;
            }
            if(o1 > o2 ){
                return 1;
            }
            return 0;
        }
    }
}

运行结果:

1
2
3
4

Set总结

1、Set实现的基础是Map(HashMap)

2、Set中的元素是不能重复的,如果使用add(Object obj)方法添加已经存在的对象,则会覆盖前面的对象

Map

Map 是一种把键对象和值对象进行关联的容器,而一个值对象又可以是一个Map,依次类推,这样就可形成一个多级映射。

对于键对象来说,像Set一样,一个 Map容器中的键对象不允许重复,这是为了保持查找结果的一致性;如果有两个键对象一样,那你想得到那个键对象所对应的值对象时就有问题了,可能你得到的并不是你想的那个值对象,结果会造成混乱,所以键的唯一性很重要,也是符合集合的性质的。

当然在使用过程中,某个键所对应的值对象可能会发生变化,这时会按照最后一次修改的值对象与键对应。对于值对象则没有唯一性的要求,你可以将任意多个键都映射到一个值对象上,这不会发生任何问题(不过对你的使用却可能会造成不便,你不知道你得到的到底是那一个键所对应的值对象)。

Map有两种比较常用的实现:HashMap和TreeMap。

HashMap

HashMap是基于散列链表来实现的,简单的来说,根据key算出一个hash值,确定一个存放index,但是hash值有可能会冲突重复,所以如果冲突的hash值就需要以链表的形式在同一个index存放了。详情请看HashMap原理和代码浅析这篇文章。

代码示例:

    Map<String, String> hashMap = new HashMap<>();
    //存储
    hashMap.put("1", "abc");
    hashMap.put("2", "bcd");

    //根据key来删除
    hashMap.remove("1");
    //根据key获取
    String value = hashMap.get("2");

    //map的遍历,有很多方法遍历,这里只列举一种。
    for(Map.Entry<String, String> entry : hashMap.entrySet()){
        //获取key
        entry.getKey();
        //获取value
        entry.getValue();
    }

LinkedHashMap

LinkedHashMap继承自HashMap,特点是内部存入数据是有顺序的,增加了记住元素插入或者访问顺序的功能,这个是通过内部一个双向的循环链表实现的。与 HashMap 一样,它可以为基本操作(add、contains 和 remove)提供稳定的性能,假定哈希函数将元素正确分布到桶中。由于增加了维护链接列表的开支,其性能很可能比 HashMap 稍逊一筹,不过这一点例外:LinkedHashMap 的 collection 视图迭代所需时间与映射的大小 成比例。HashMap 迭代时间很可能开支较大,因为它所需要的时间与其容量 成比例。

TreeMap

TreeMap的使用大致跟HashMap类似,但是内部实现是根据红黑树来实现的。红黑树是一种平衡有序的二叉树,TreeMap的插入删除查询都是依据红黑树的规则来进行的。可以参考 Java提高篇(二七)—–TreeMap

HashTable

HashMap和TreeMap都是线程不安全的,多线程操作的时候可能会造成数据错误。Hashtable是线程安全的。其他内部实现,与HashMap都是一样的。

常用类的比较

常用类

1.ArrayList:元素单个,效率高,多用于查询

2.Vector: 元素单个,线程安全,多用于查询

3.LinkedList:元素单个,多用于插入和删除

4.HashMap:元素成对,元素可为空

5.HashTable:元素成对,线程安全,元素不可为空

6.HashSet:元素单个,元素不可重复

Vector、ArrayList和LinkedList

大多数情况下,从性能上来说ArrayList最好

当集合内的元素需要频繁插入、删除时LinkedList会有比较好的表现

它们三个性能都比不上数组,另外Vector是线程同步的

能用数组的时候(元素类型固定,数组长度固定),请尽量使用数组来代替List

没有频繁的删除插入操作,又不用考虑多线程问题,优先选择ArrayList

在多线程条件下使用,可以考虑Vector

需要频繁地删除插入,LinkedList就有了用武之地

如果你什么都不知道,用ArrayList没错。

以上是java中的基础的数据结构,下面来看下android中特有的数据结构。

SparseArray

SparseArray类型

SparseLongArray

SparseIntArray

SparseBooleanArray

SparseArray

SparseArray特点

1、SparseArray是android提供的一个工具类,它可以用来替代hashmap进行对象的存储

2、SparseArray比HashMap更省内存,它对数据采取了矩阵压缩的方式来表示稀疏数组的数据,从而节约内存空间

3、SparseArray只能存储key为整型的数据

4、SparseArray在存储和读取数据时候,使用的是二分查找法,提高了查找的效率

5、SparseArray有自己的垃圾回收机制。(当数量不是很多的时候,这个不必关心。)

ArrayMap

官方对ArrayMap也有说明:它不是一个适应大数据的数据结构,相比传统的HashMap速度要慢,因为查找方法是二分法,并且当你删除或者添加数据时,会对空间重新调整,在使用大量数据时,效率并不明显,低于50%。

所以ArrayMap是牺牲了时间换区空间。在写手机app时,适时的使用ArrayMap,会给内存使用带来可观的提升。

HashMap和ArrayMap的区别

1、存储方式不同

2、添加数据时扩容时的处理不同

详情请参考Android内存优化:ArrayMap

  • 2
    点赞
  • 0
    评论
  • 12
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值