八、集合类Vector、ArrayList、LinkedList

Verctor 是 Java 早期提供的线程安全动态数组,若是不须要线程安全,并不建议选择,毕竟同步是有额外开销的.Vector是基于synchronized实现的线程安全的ArrayList。Vector 内部是使用对象数组来保存数据,能够根据须要自动的增长容量,当数组已满时,会建立新的数组,并拷贝原有数组数据。Vector,默认建立一个大小为10的Object数组,并将capacityIncrement设置为0;当插入元素数组大小不够时,若是capacityIncrement>0,则将Object数组的大小扩大为现有size+capacityIncrement;若是capacityIncrement<=0,则将Object数组的大小扩大为现有大小的2倍java

ArrayList 是应用更加普遍的动态数组实现,它自己不是线程安全的,因此性能要好不少。与 Vector 近似,ArrayList 也是能够根据须要调整容量,不过二者的调整逻辑有所区别,Vector 在扩容时会提升 1 倍,而 ArrayList 则是增长 50%。ArrayList在执行插入元素是超过当前数组预约义的最大值时,数组须要扩容,扩容过程须要调用底层System.arraycopy()方法进行大量的数组复制操做;在删除元素时并不会减小数组的容量(若是须要缩小数组容量,能够调用trimToSize()方法);在查找元素时要遍历数组,对于非null的元素采起equals的方式寻找。面试

LinkedList 顾名思义是 Java 提供的双向链表,因此它不须要像上面两种那样调整容量,它也不是线程安全的。它也能够被看成堆栈、队列或双端队列进行操做。算法

 

Vector 和 ArrayList 做为动态数组,其内部元素以数组形式顺序存储的,因此很是适合随机访问的场合。除了尾部插入和删除元素,每每性能会相对较差,好比咱们在中间位置插入一个元素,须要移动后续全部元素。而 LinkedList 以链表形式存储的,进行节点插入、删除却要高效得多,可是随机访问性能则要比动态数组慢。编程

 

排序算法须要熟知:数组

内部排序,至少掌握基础算法如归并排序、交换排序(冒泡、快排)、选择排序、插入排序等。安全

外部排序,掌握利用内存和外部存储处理超大数据集,至少要理解过程和思路。并发

考察算法不只仅是如何简单实现,面试官每每会刨根问底,好比哪些是排序是不稳定的呢(快排、堆排),或者思考稳定意味着什么;对不一样数据集,各类排序的最好或最差状况;从某个角度如何进一步优化(好比空间占用,假设业务场景须要最小辅助空间,这个角度堆排序就比归并优异)等,从简单的了解,到进一步的思考,面试官一般还会观察面试者处理问题和沟通时的思路。框架

 

Java 的集合框架,Collection 接口是全部集合的根,而后扩展开提供了三大类集合,分别是:函数

  • List,也就是咱们前面介绍最多的有序集合,它提供了方便的访问、插入、删除等操做。工具

  • Set,Set 是不容许重复元素的,这是和 List 最明显的区别,也就是不存在两个对象 equals 返回 true。咱们在平常开发中有不少须要保证元素惟一性的场合。

  • Queue/Deque,则是 Java 提供的标准队列结构的实现,除了集合的基本功能,它还支持相似先入先出(FIFO, First-in-First-Out)或者后入先出(LIFO,Last-In-First-Out)等特定行为。这里不包括 BlockingQueue,由于一般是并发编程场合,因此被放置在并发包里。

 每种集合的通用逻辑,都被抽象到相应的抽象类之中,好比 AbstractList 就集中了各类 List 操做的通用部分。这些集合不是彻底孤立的,好比,LinkedList 自己,既是 List,也是 Deque 。

 TreeSet 代码里实际默认是利用 TreeMap 实现的,Java 类库建立了一个 Dummy 对象“PRESENT”做为 value,而后全部插入的元素实际上是以键的形式放入了 TreeMap 里面;同理,HashSet 其实也是以 HashMap 为基础实现的,原来他们只是 Map 类的马甲!

TreeSet 支持天然顺序访问,可是添加、删除、包含等操做要相对低效(log(n) 时间)。

HashSet 则是利用哈希算法,理想状况下,若是哈希散列正常,能够提供常数时间的添加、删除、包含等操做,可是它不保证有序。

LinkedHashSet,内部构建了一个记录插入顺序的双向链表,所以提供了按照插入顺序遍历的能力,与此同时,也保证了常数时间的添加、删除、包含等操做,这些操做性能略低于 HashSet,由于须要维护链表的开销。

在遍历元素时,HashSet 性能受自身容量影响,因此初始化时,除非有必要,否则不要将其背后的 HashMap 容量设置过大。而对于 LinkedHashSet,因为其内部链表提供的方便,遍历性能只和元素多少有关系。

 

 

除了 java.util.concurrent 里面的线程安全容器,在 Collections 工具类中,提供了一系列的 synchronized 方法返回线程安全的同步列表对象,如:

List list = Collections.synchronizedList(new ArrayList());

它的实现,基本就是将每一个基本方法,好比 get、set、add 之类,都经过 synchronizd 添加基本的同步支持,很是简单粗暴,但也很是实用。注意这些方法建立的线程安全集合,都符合迭代时 fail-fast 行为,当发生意外的并发修改时,尽早抛出 ConcurrentModificationException 异常,以免不可预计的行为。

 

 Java 提供的默认排序算法:

 须要区分是 Arrays.sort() 仍是 Collections.sort() (底层是调用 Arrays.sort());什么数据类型;多大的数据集(过小的数据集,复杂排序是不必的,Java 会直接进行二分插入排序)等。

对于基本数据类型,目前使用的是所谓双轴快速排序(Dual-Pivot QuickSort),是一种改进的快速排序算法,早期版本是相对传统的快速排序。

对于引用数据类型,目前则是使用TimSort,思想上也是一种归并和二分插入排序(binarySort)结合的优化排序算法。TimSort 并非 Java 的首创,简单说它的思路是查找数据集中已经排好序的分区(这里叫 run),而后合并这些分区来达到排序的目的。

Java 8 引入了并行排序算法(直接使用 parallelSort 方法),这是为了充分利用现代多核处理器的计算能力,底层实现基于 fork-join 框架,当处理的数据集比较小的时候,差距不明显,甚至还表现差一点;可是,当数据集增加到数万或百万以上时,提升就很是大了,具体仍是取决于处理器和系统环境。

排序算法仍然在不断改进,最近双轴快速排序实现的做者提交了一个更进一步的改进,历时多年的研究,目前正在审核和验证阶段。根据做者的性能测试对比,相比于基于归并排序的实现,新改进能够提升随机数据排序速度提升 10%~20%。

在 Java 8 之中,Java 平台支持了 Lambda 和 Stream,相应的 Java 集合框架也进行了大范围的加强,以支持相似为集合建立相应 stream 或者 parallelStream 的方法实现,咱们能够很是方便的实现函数式代码。阅读 Java 源代码,你会发现,这些 API 的设计和实现比较独特,它们并非实如今抽象类里面,而是以默认方法的形式实如今 Collection 这样的接口里!这是 Java 8 在语言层面的新特性,容许接口实现默认方法,理论上来讲,咱们原来实如今相似 Collections 这种工具类中的方法,大多能够转换到相应的接口上。

在 Java 9 中,Java 标准类库提供了一系列的静态工厂方法,好比,List.of()、Set.of(),大大简化了构建小的容器实例的代码量。而且集合实例都是容量很是有限的,并且在生命周期中并不会进行修改。

之前这么写:

ArrayList<String>  list = new ArrayList<>();
list.add("Hello");
list.add("World");

利用新的容器静态工厂方法,一句代码就够了,而且保证了不可变性。如今能够这么写:

List<String> simpleList = List.of("Hello","world");

更进一步,经过各类 of 静态工厂方法建立的实例,还应用了一些咱们所谓的最佳实践,好比,它是不可变的,符合咱们对线程安全的需求;它由于不须要考虑扩容,因此空间上更加紧凑等。

 

 

java堆结构PriorityQueue彻底解析:

PriorityQueue队列,是基于最小堆原理实现。http://www.noobyard.com/article/p-hmygphim-ne.html

PriorityQueue队列不适合进场出队入队的频繁操做,可是他的优先级特性很是适合一些对顺序有要求的数据处理场合。且非线程安全的。