Android面试题数据结构篇,如果喜欢请持续关注和推荐。
List,Set,Map的区别
Set是最简单的一种集合。集合中的对象不按特定的方式排序,并且没有重复对象。 Set接口主要实现了两个实现类:
HashSet: HashSet类按照哈希算法来存取集合中的对象,存取速度比较快
TreeSet :TreeSet类实现了SortedSet接口,能够对集合中的对象进行排序。
List的特征是其元素以线性方式存储,集合中可以存放重复对象。
ArrayList() :代表长度可以改变得数组。可以对元素进行随机的访问,向ArrayList()中插入与删除元素的速度慢。
LinkedList():在实现中采用链表数据结构。插入和删除速度快,访问速度慢。
Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。Map没有继承于Collection接口 从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
HashMap:Map基于散列表的实现。插入和查询“键值对”的开销是固定的。可以通过构造器设置容量capacity和负载因子load factor,以调整容器的性能。
LinkedHashMap: 类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一点。而在迭代访问时发而更快,因为它使用链表维护内部次序。
TreeMap : 基于红黑树数据结构的实现。查看“键”或“键值对”时,它们会被排序(次序由Comparabel或Comparator决定)。TreeMap的特点在 于,你得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树。
WeakHashMao :弱键(weak key)Map,Map中使用的对象也被允许释放: 这是为解决特殊问题设计的。如果没有map之外的引用指向某个“键”,则此“键”可以被垃圾收集器回收。
ArrayMap和HashMap的对比
1、存储方式不同,HashMap内部有一个HashMapEntry<K,V>[]对象,每一个键值对都存储在这个对象里,当使用put方法添加键值对时,就会new一个HashMapEntry对象。
2、添加数据时扩容时的处理不一样,进行了new操作,重新创建对象,开销很大。ArrayMap用的是copy数据,所以效率相对要高。
3、ArrayMap提供了数组收缩的功能,在clear或remove后,会重新收缩数组,是否空间
4、ArrayMap采用二分法查找;
HashMap和HashTable的区别
1 HashMap不是线程安全的,效率高一点、方法不是Synchronize的要提供外同步,有containsvalue和containsKey方法。
hashtable是,线程安全,不允许有null的键和值,效率稍低,方法是是Synchronize的。有contains方法方法。Hashtable 继承于Dictionary 类
HashMap与HashSet的区别
hashMap:HashMap实现了Map接口,HashMap储存键值对,使用put()方法将元素放入map中,HashMap中使用键对象来计算hashcode值,HashMap比较快,因为是使用唯一的键来获取对象。
HashSet实现了Set接口,HashSet仅仅存储对象,使用add()方法将元素放入set中,HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false。HashSet较HashMap来说比较慢。
HashSet与HashMap怎么判断集合元素重复?
HashSet不能添加重复的元素,当调用add(Object)方法时候,首先会调用Object的hashCode方法判hashCode是否已经存在,如不存在则直接插入元素;如果已存在则调用Object对象的equals方法判断是否返回true,如果为true则说明元素已经存在,如为false则插入元素。
ArrayList和LinkedList的区别,以及应用场景
ArrayList是基于数组实现的,ArrayList线程不安全。 LinkedList是基于双链表实现的。 使用场景:
如果应用程序对各个索引位置的元素进行大量的存取或删除操作,ArrayList对象要远优于LinkedList对象;
如果应用程序主要是对列表进行循环,并且循环时候进行插入或者删除操作,LinkedList对象要远优于ArrayList对象;
数组和链表的区别
数组:是将元素在内存中连续存储的;它的优点:因为数据是连续存储的,内存地址连续,所以在查找数据的时候效率比较高;它的缺点:在存储之前,我们需要申请一块连续的内存空间,并且在编译的时候就必须确定好它的空间的大小。在运行的时候空间的大小是无法随着你的需要进行增加和减少而改变的,当数据两比较大的时候,有可能会出现越界的情况,数据比较小的时候,又有可能会浪费掉内存空间。在改变数据个数时,增加、插入、删除数据效率比较低。
链表:是动态申请内存空间,不需要像数组需要提前申请好内存的大小,链表只需在用的时候申请就可以,根据需要来动态申请或者删除内存空间,对于数据增加和删除以及插入比数组灵活。还有就是链表中数据在内存中可以在任意的位置,通过应用来关联数据(就是通过存在元素的指针来联系)
HashMap的实现原理:
HashMap是基于哈希表的map接口的非同步实现,它允许使用null值作为key和value。在Java编程语言中最基本的结构就是两种,一种是数组,另一种是模拟指针(引用)。所有的数据结构都可以用这两个基本的结构来构造,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构。即数组和链表的结合体。HashMap底层就是一个数据结构,数组中的每一项又是一个链表。
冲突:
HashMap中调用Hashcode()方法计算Hashclde值,由于Java中两个不同的对象可能有一样的Hashcode。就导致了冲突的产生。
解决:
HashMap在put时候,底层源码可以看出,当程序试图将一个key-value对象放入到HashMap中,首先根据该key的hashCode()返回值决定该Entry的存储位置,如果两个Entry的key的hashCode()方法返回值相同,那他们的存储位置相同,如果这两个Entry的key通过equals比较返回true,新添加的Entry的value将会覆盖原来的Entry的value,但是key不会被覆盖,反之,如果返回false,新添加的Entry将与集合中原有的Entry形成Entry链,新添加的位于头部,旧的位于尾部。
原理
利用key的hashCode重新hash计算出当前对象的元素在数组中的下标。
存储时如果出现hash值相同的key,分两种情况:1、如果key相同,则覆盖原来的值。2、如果key不同(出现冲突),则放在链表中。
获取时,直接找到hash值对应的下标,再进一步判断key是否相同,从而拿到对应的值。
Hashmap的核心就是使用数组进行存储,出现key冲突的时候,就存放在链表中。
ConcurrentHashMap内部实现,HashTable的实现被废弃的原因:
1.HashTable容器在竞争激烈的并发环境下表现出效率低下的原因,是因为所有访问HashTable的线程都必须竞争同一把锁,那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
2.ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构, 一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护者一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。
说说 LruCache 底层原理
LruCache 使用一个 LinkedHashMap 简单的实现内存的缓存,没有软引用,都是强引用。如果添加的数据大于设置的最大值,就删除最先缓存的数据来调整内存。
maxSize 是通过构造方法初始化的值,他表示这个缓存能缓存的最大值是多少。
size 在添加和移除缓存都被更新值,他通过safeSizeOf 这个方法更新值。safeSizeOf默认返回1,但一般我们会根据 maxSize重写这个方法,比如认为 maxSize 代表是 KB 的话,那么就以KB为单位返回该项所占的内存大小。
除异常外首先会判断size是否超过maxSize,如果超过了就取出最先插入的缓存,如果不为空就删掉,并把size减去该项所占的大小。这个操作将一直循环下去,直到 size 比 maxSize 小或者缓存为空。
最后
喜欢的话可以关注一下哦,会每天更新android相关的文章。