每日Android面试题

1、HashMap与TreeMap、HashTable的区别及适用场景?

  1. HashMap: 非线程安全
  2. HashMap:基于哈希表(散列表)实现。
  3. 使用HashMap要求添加的键类明确定义了hashCode()和equals()
  4. [可以重写hashCode()和equals()]
  5. 为了优化HashMap空间的使用,可以调优初始容量和负载因子。
  6. 其中散列表的冲突处理主要分两种,
  7. 一种是开放定址法,另一种是链表法。HashMap的实现中采用的是链表法。
  8. TreeMap:非线程安全基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态。
  9. 适用场景分析:
  10. HashMap和HashTable:HashMap去掉了HashTable的contains方法
  11. 但是加上了containsValue()和containsKey()方法
  12. HashTable同步的,而HashMap是非同步的,效率上比HashTable要高。
  13. HashMap允许空键值,而HashTable不允许。
  14. HashMap:适用于Map中插入、删除和定位元素。
  15. Treemap:适用于按自然顺序或自定义顺序遍历键(key)。

2、concurrentHashmap原理,原子类?

  1. ConcurrentHashMap作为一种线程安全且高效的哈希表的解决方案尤其其中的"分段锁"的方
  2. 相比HashTable的全表锁在性能上的提升非常之大.
  3. ConcurrentHashMap 有 16 个 Segments
  4. 所以理论上,这个时候,最多可以同时支持 16 个线程并发写
  5. 只要它们的操作分别分布在不同的 Segment 上
  6. 这个值可以在初始化的时候设置为其他值,但是一旦初始化以后,它是不可以扩容的。
  7. 再具体到每个 Segment 内部,其实每个 Segment 很像之前介绍的 HashMap,不过它要保证线程安全,所以处理起来要麻烦些。
  8. initialCapacity:初始容量,这个值指的是整个 ConcurrentHashMap 的初始容量,实际操作的时候需要平均分给每个 Segment。
  9. loadFactor:负载因子,之前我们说了,Segment 数组不可以扩容,所以这个负载因子是给每个 Segment 内部使用的。
  10. Segment 内部是由 数组+链表 组成的。

3、HashSet与Treeset的适用场景?

  1. TreeSet 是二叉树(红黑树的树据结构)实现的
  2. Treeset中的数据是自动排好序的,不允许放入null值。
  3. HashSet 是哈希表实现的,HashSet中的数据是无序的
  4. 可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束。
  5. HashSet要求放入的对象必须实现HashCode()方法
  6. 放入的对象,是以hashcode码作为标识的
  7. 而具有相同内容的String对象,hashcode是一样,所以放入的内容不能重复
  8. 但是同一个类的对象可以放入不同的实例。
  9. 适用场景分析:
    ① HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。
    ② 为快速查找而设计的Set,我们通常都应该使用HashSet
    ③ 在我们需要排序的功能时,我们才使用TreeSet。

多线程

1、什么是并发什么是并行

  1. 并行:多个cpu实例或者多台机器同时执行一段处理逻辑
  2. 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。
  3. 并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈
  4. 我们会用TPS或者QPS来反应这个系统的处理能力。
  5. Android针对并发一般都是采用静态页面的策略

2、什么是线程安全

  1. 线程安全:在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。
  2. 这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。
  3. 线程不安全:反过来,线程不安全就意味着线程的调度顺序会影响最终结果

3、什么是同步异步?

  1. 同步:就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回
  2. 要想实现同步操作,必须要获得线程的对象锁。
  3. 获得它可以保证在同一时刻只有一个线能够进入临界区,并且在这个锁被释放之前,其他的线程都不能再进入这个临界区。
  4. 如果其他线程想要获得这个对象的锁,只能进入等待队列等待。
    只有当拥有该对象锁的线程退出临界区时,锁才会被释放,等待队列中优先级最高的线程才能获得该锁。
  5. 实现同步的方式有两种:同步方法、同步代码块。
  6. 异步:当一个异步过程调用发出后,调用者不会立刻得到结果。
  7. 实际处理这个吊用的部件是在调用发出后,通过状态、通知来通知调用者,或通过回调函数处理这个调用。
  8. 由于每个线程都包含了运行时自身所需要的数据或方法,
  9. 因此,在进行输入输出时,不必关系其他线程的状态或行为,也不必等到输入输出处理完毕才返回。
  10. 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,异步能够提高程序的效率。

4、什么是线程阻塞?

  1. 阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。
  2. 非阻塞:指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。

5、同步和阻塞有什么关系?

  1. 同步与阻塞同步是个过程
  2. 阻塞是线程的一种状态。
  3. 多个线程操作共享变量时可能会出现竞争。
  4. 这时需要同步来防止两个以上的线程同时进入临界区
  5. 在这个过程中,后进入临界区的线程将阻塞,等待先进入的线程走出临界区。