1.常用数据有哪些?
1.1如下图所示
2数据的说明
2.1 数组
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行,于是衍生出两个子类接口List和Set。
面试题 Collections与Collection区别
Collection 是java.utils下的接口,它是各种集合结构的父接口,定义了集合对象的基本操作方法 。
Collections是java.utils下的工具类,它包含有各种有关集合操作的静态方法,主要针对集合类的一个包装类,它提供一系列对各种集合的搜索、排序、线程安全化等操作方法。 ————————————————
无序数组
优点:查询快,如果知道索引可以快速地存取
缺点:删除慢,大小固定
有序数组
优点:比无序数组查找快
缺点:删除和插入慢,大小固定 2.2 栈
优点:提供后进先出的存取方式
缺点:存取其他项很慢
比如,Android中管理activity进出就是使用栈
2.3 队列
优点:提供先进先出的存取方式
缺点:存取其他项都很慢
2.4 链表
优点:插入快,删除快
缺点:查找慢(一个个节点查)
2.5 二叉树
优点:查找,插入,删除都快(平衡二叉树)
缺点:删除算法复杂
2.6 红-黑树
优点:查找,插入,删除都快,树总是平衡的(局部调整)
缺点:算法复杂
2.7 哈希表
优点:如果关键字已知则存取速度极快,插入快
缺点:删除慢,如果不知道关键字则存取很慢,对存储空间使用不充分
2.8 堆
优点:插入,删除快,对最大数据的项存取很快
缺点:对其他数据项存取很慢
2.9 图
优点:对现实世界建模
缺点:有些算法慢且复杂
================================================
3 ArrayList内部用到了数组来做基本的数据存储结构,那就说明是它一个连续内存块或者存储块的顺序存储结构。
这样的存储结构是优缺点是:
优点:由于是连续的内存地址存储块,对于查询一个或者多个存储内容,就很容易了,只要找到一个元素的内容a,加上每个元素所占的内存字节数c,a+c就很容易找到下一个元素,计算量小很高效。
缺点:缺点也显而易见,举个栗子。 a: 你在火车站买票,这个时候,一个漂亮的主播妹纸突然插队到你前面,你和你后面的一起排队的人是不是都得往后退一个位置,妹纸才能插入到你的位置上吧。这个时候这一队中需要挪动位置的人(也就是数组位置)是不是很多,当你和后面排队的人都后退一个位置后,妹纸的插队才算是完成了。这是不是很耗费时间。 b: 你前面的一个哥们突然不想排队走了,空出了一个位置,这时候你和后面排队的人是不是都得往前挪动一个位置,当所有人都挪动完后,这个队伍才恢复原来没有人走的情况,是不是也很耗费时间。
这优缺点要说明的是什么呢,顺序存储的线性表插入和删除的都比较耗费时间,而查询确非常的快。
三、总结
1ArrayList是线性表中的顺序存储结构的顺序表,因为内部维护的是一个数组,数组是一个拥有连续存储地址的存储块。
2ArrayList因为内部维护的是一个数组,查询和修改的效率很高,但是插入添加和删除的效率比较低,特别是数据量大的情况下必较明显。 在使用普通的for循环遍历ArrayList的时候删除其中的元素容易出现数据删除错乱问题,改用Iterator迭代器能够很好的解决这个问题。
3ArrayList在添加元素的时候是允许加入null元素的,为了避免后续使用数据时出现NullPointerException的异常,请先对要添加的元素做非空判断。
4ArrayList从上面的源码分析可以看出,它可以添加重复的元素对象,所以在添加对象的时候做好相等对象的判断。
6从上面源码可以看出,ArrayList的size和真实申请的堆内存对象容量不同,所以在使用的时候控制好ArrayList的容量使用也是很好的性能优化手段。 7ArrayList的是线程不安全的,在多线程环境下需要注意对象数据同步问题。
ArrayList、LinkedList性能对比
private void testArrayList(){
ArrayList<String> list=new ArrayList<>();
int maxTestCount=50000;
//测试添加
long start =System.currentTimeMillis();
for(int i =0;i<maxTestCount;i++){
list.add(0,String.valueOf(i));
}
long end =System.currentTimeMillis();
Log.e(TAG,"ArrayList add cost time :"+(end-start));
//测试查询
start =System.currentTimeMillis();
for(int i =0;i<maxTestCount;i++){
list.get(i);
}
end =System.currentTimeMillis();
Log.e(TAG,"ArrayList get cost time :"+(end-start));
//测试查询
start =System.currentTimeMillis();
for(int i =maxTestCount;i>0;i--){
list.remove(0);
}
end =System.currentTimeMillis();
Log.e(TAG,"ArrayList remove cost time :"+(end-start));
}
LinkedList
private void testLinkedList(){
LinkedList<String> list=new LinkedList<>();
int maxTestCount=50000;
//测试添加
long start =System.currentTimeMillis();
for(int i =0;i<maxTestCount;i++){
list.add(0,String.valueOf(i));
}
long end =System.currentTimeMillis();
Log.e(TAG,"LinkedList add cost time :"+(end-start));
//测试查询
start =System.currentTimeMillis();
for(int i =0;i<maxTestCount;i++){
list.get(i);
}
end =System.currentTimeMillis();
Log.e(TAG,"LinkedList get cost time :"+(end-start));
//测试查询
start =System.currentTimeMillis();
for(int i =maxTestCount;i>0;i--){
list.remove(0);
}
end =System.currentTimeMillis();
Log.e(TAG,"LinkedList remove cost time :"+(end-start));
}
通过上面的运行结果可以大致得出以下结论:
ArrayList插入、删除效率明显低于LinkedList
ArrayList查询效率远远高于LinkedList
通过上面的结构是不是就可以认为插入删除频繁选择LinkedList,追求查询效率就选择ArrayList呢,我们先来分析一下效率差别的原因,这个就跟数据结构有关系了,可以参考一些数据结构中链表的知识,arraylist顺序表,用数组的方式实现。想想数组要查询那个元素只给出其下标即可,所以才说arraylist随机访问多的场景比较合适。但是如果删除某个元素比如第i个元素,则要将i之后的元素都向前移一位以保证顺序表的正确,增加也是一样,要移动多个元素。要多次删除增加的话是很低效的。而LinkedList是双向链表,注意是链表。要查询只能头结点开始逐步查询,没有什么给出下标即可直接查询的便利,需要遍历。但是,如果要增加后删除一个元素的话,只需要改变其前后元素的指向即可,不需要像arraylist那样整体移动,所以才说多用于增删多的场合。
List其他知识扩展
Vector 是矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现。但是ArrayList是非线程安全的,而Vector是线程安全的。
Stack 是栈,它继承于Vector。它的特性是:先进后出(FILO, First In Last Out)。
数据量\插入位置 头部 中间 尾部 随机
百 效率持平 效率持平 效率持平 效率持平
千 LinkedList插入快 效率持平 效率持平 ArrayList插入快
万 LinkedList插入快 ArrayList插入快 效率持平 ArrayList插入快
十万 LinkedList插入快 ArrayList插入快 ArrayList插入快 ArrayList插入快
百万 LinkedList插入快 ArrayList插入快 ArrayList插入快 ArrayList插入快
在尾部插入数据,数据量较小时LinkedList比较快,因为ArrayList要频繁扩容,当数据量大时ArrayList比较快,因为ArrayList扩容是当前容量*1.5,大容量扩容一次就能提供很多空间,当ArrayList不需扩容时效率明显比LinkedList高,因为直接数组元素赋值不需new Node
在首部插入数据,LinkedList较快,因为LinkedList遍历插入位置花费时间很小,而ArrayList需要将原数组所有元素进行一次System.arraycopy
插入位置越往中间,LinkedList效率越低,因为它遍历获取插入位置是从两头往中间搜,index越往中间遍历越久,因此ArrayList的插入效率可能会比LinkedList高
插入位置越往后,ArrayList效率越高,因为数组需要复制后移的数据少了,那么System.arraycopy就快了,因此在首部插入数据LinkedList效率比ArrayList高,尾部插入数据ArrayList效率比LinkedList高
LinkedList可以实现队列,栈等数据结构,这是它的优势
=====================以上是list总结 end================================
=====================以上是set总结 start================================
Set介绍:Set相对于List、Map是最简单的一种集合。集合中的对象不按特定的方式排序,并且没有重复对象。
特点: 它不允许出现重复元素;
不保证和政集合中元素的顺序
允许包含值为null的元素,但最多只能有一个null元素
Set是一个接口,实例化Set可以采用下面的方式:
HashSet: HashSet类按照哈希算法来存取集合中的对象,存取速度比较快
TreeSet :TreeSet类实现了SortedSet接口,能够对集合中的对象进行排序
HashSet就是采用哈希算法存取对象的集合,它内部采用对某个数字n进行取余的方式对哈希码进行分组和划分对象的存储区域。Object类中定义了一个hashCode()方法来返回每个Java对象的哈希码,当从HashSet集合中查找某个对象时,Java系统首先调用对象的hashCode()方法获得该对象的哈希码,然后根据哈希码找到相应的存储区域,最后取出该存储区域内的每个元素与该对象进行equals方法比较,这样不用遍历集合中的素有元素就可以的到结论。可见,HashSet集合具有很好的对象检索性能,但是,HashSet集合存储对象的效率相对要低些,因为向HashSet集合中添加一个对象时,要先计算出对象的哈希码和根据这个哈希码确定对象在集合中的存放位置。 Set hashSet = new HashSet(); hashSet.add(3); hashSet.add(2); hashSet.add(5); hashSet.add(1); //模拟添加一个重复数据 hashSet.add(2); Log.e(TAG,"set hashSet :"+hashSet+" size : "+hashSet.size());
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(3);
treeSet.add(2);
treeSet.add(5);
treeSet.add(1);
Log.e(TAG,"set treeSet :"+treeSet +" size : "+treeSet.size());
通过运行结果可以看出,Set是不可重复的,TreeSet是有序的,HashSet是无序的。
TreeSet实现排序默认是升序,想要实现自定义排序可以通过传进去一个Comparator或者TreeSet的添加对象实现Comparator接口。
Set遍历
for循环方式
for (Integer integer :hashSet){
Log.e(TAG,"set integer :"+integer);
}
迭代器方式
Iterator<Integer> iterator =hashSet.iterator();
while (iterator.hasNext()){
Integer integer=iterator.next();
Log.e(TAG,"set integer :"+integer);
}
=====================以上是set总结 end================================
=====================以上是map总结 start================================ Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。 Map没有继承于Collection接口 从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
HashMap //Map基于散列表的实现。插入和查询“键值对”的开销是固定的。可以通过构造器设置容量capacity和负载因子load factor,以调整容器的性能。
LinkedHashMap //类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一点。而在迭代访问时发而更快,因为它使用链表维护内部次序。
TreeMap //底层是二叉树数据结构,线程不同步,可用于给Map集合中的键进行排序。
HashTable //HashMap是Hashtable的轻量级实现,非线程安全的实现他们都实现了map接口,主要区别是HashMap键值可以为空null,效率可以高于Hashtable。
ConcurrentHashMap //ConcurrentHashMap通常只被看做并发效率更高的Map,用来替换其他线程安全的Map容器,比如Hashtable和Collections.synchronizedMap。
WeakHashMap //弱键(weak key)Map,Map中使用的对象也被允许释放: 这是为解决特殊问题设计的。如果没有map之外的引用指向某个“键”,则此“键”可以被垃圾收集器回收。
IdentifyHashMap //使用==代替equals()对“键”作比较的hash map
ArrayMap //ArrayMap是一个<key,value>映射的数据结构,它设计上更多的是考虑内存的优化,内部是使用两个数组进行数据存储,一个数组记录key的hash值,另外一个数组记录Value值,它和SparseArray一样,也会对key使用二分法进行从小到大排序,在添加、删除、查找数据的时候都是先使用二分查找法得到相应的index,然后通过index来进行添加、查找、删除等操作,所以,应用场景和SparseArray的一样,如果在数据量比较大的情况下,那么它的性能将退化至少50%。
SparseArray //SparseArray比HashMap更省内存,在某些条件下性能更好,主要是因为它避免了对key的自动装箱(int转为Integer类型),它内部则是通过两个数组来进行数据存储的,一个存储key,另外一个存储value,为了优化性能,它内部对数据还采取了压缩的方式来表示稀疏数组的数据,从而节约内存空间。