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是有序的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是一种不包含重复的元素的无序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 set = new HashSet();
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 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,依次类推,这样就可形成一个多级映射。
对于键对象来说,像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类型
SparseLongArray
SparseIntArray
SparseBooleanArray
SparseArray
SparseArray特点
1、SparseArray是android提供的一个工具类,它可以用来替代hashmap进行对象的存储
2、SparseArray比HashMap更省内存,它对数据采取了矩阵压缩的方式来表示稀疏数组的数据,从而节约内存空间
3、SparseArray只能存储key为整型的数据
4、SparseArray在存储和读取数据时候,使用的是二分查找法,提高了查找的效率
5、SparseArray有自己的垃圾回收机制。(当数量不是很多的时候,这个不必关心。)
官方对ArrayMap也有说明:它不是一个适应大数据的数据结构,相比传统的HashMap速度要慢,因为查找方法是二分法,并且当你删除或者添加数据时,会对空间重新调整,在使用大量数据时,效率并不明显,低于50%。
所以ArrayMap是牺牲了时间换区空间。在写手机app时,适时的使用ArrayMap,会给内存使用带来可观的提升。
HashMap和ArrayMap的区别
1、存储方式不同
2、添加数据时扩容时的处理不同
详情请参考Android内存优化:ArrayMap