集合中用到的数据结构有以下几种:

  • 数组:最常用的数据结构之一。数组的特点是长度固定,可以用下标索引,并且所有的元素的类型都是一致的。使用时尽量把数组封装在一个类里,防止数据被错误的操作弄乱。
  • 链表:是一种由多个节点组成的数据结构,并且每个节点包含有数据以及指向下一个节点的引用,在双向链表里,还会有一个指向前一个节点的引用。例如,可以用单向链表和双向链表来实现堆栈和队列,因为链表的两端都是可以进行插入和删除的动作的。当然,也会有在链表的中间频繁插入和删除节点的场景。
  • 树:是一种由节点组成的数据结构,每个节点都包含数据元素,并且有一个或多个子节点,每个子节点指向一个父节点可以表示层级关系或者数据元素的顺序关系。如果树的每个子节点最多有两个叶子节点,那么这种树被称为二叉树。二叉树是一种非常常用的树形结构, 因为它的这种结构使得节点的插入和删除都非常高效。树的边表示从一个节点到另外一个节点的快捷路径。
  • 堆栈:只允许对最后插入的元素进行操作(也就是后进先出,Last In First Out – LIFO)。如果你移除了栈顶的元素,那么你可以操作倒数第二个元素,依次类推。这种后进先出的方式是通过仅有的peek(),push()和pop()这几个方法的强制性限制达到的。这种结构在很多场景下都非常实用,例如解析像(4+2)*3这样的数学表达式,把源码中的方法和异常按照他们出现的顺序放到堆栈中,检查你的代码看看小括号和花括号是不是匹配的,等等。
  • 队列:和堆栈有些相似,不同之处在于在队列里第一个插入的元素也是第一个被删除的元素(即是先进先出)。这种先进先出的结构是通过只提供peek(),offer()和poll()这几个方法来访问数据进行限制来达到的。例如,排队等待公交车,银行或者超市里的等待列队等等,都是可以用队列来表示。

LinkedHashMap继承自HashMap实现了Map接口。基本实现同HashMap一样,不同之处在于LinkedHashMap保证了迭代的有序性。其内部维护了一个双向链表,解决了 HashMap不能随时保持遍历顺序和插入顺序一致的问题。除此之外,LinkedHashMap对访问顺序也提供了相关支持。在一些场景下,该特性很有用,比如缓存。

在实现上,LinkedHashMap很多方法直接继承自HashMap,仅为维护双向链表覆写了部分方法。所以,要看懂 LinkedHashMap 的源码,需要先看懂 HashMap 的源码。

默认情况下,LinkedHashMap的迭代顺序是按照插入节点的顺序。也可以通过改变accessOrder参数的值,使得其遍历顺序按照访问顺序输出。

这里我们只讨论LinkedHashMap和HashMap的不同之处,LinkedHashMap的其他操作和特性具体请参考HashMap

我们先来看下两者的区别:

  • 在Java.util包下
  • 继承自AbstractSequentialList
  • 实现 List 接口,能对它进行队列操作。
  • 实现 Deque 接口,即能将LinkedList当作双端队列使用。
  • 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
  • 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
  • 允许包含null值
  • 迭代器可以快速报错
  • 非线程安全的,如果在多线程中使用(修改),需要在外部作同步处理。

LinkedList是一种可以在任何位置进行高效地插入和移除操作的有序序列,它是基于双向链表实现的。内部有三个变量,size表示链表中元素的个数, first指向链表头部,last指向链表尾部。 结构图如下图所示

Vector,一个可变长的数组,底层实现与 ArrayList 大同小异,但Vector是同步的(线程安全),Vector的很多方法之前都加了关键字synchronized,所以是线程安全的。

由于Vector的实现和ArrayList的实现大同小异,这里就不再逐一分析Vector中的方法,主要分析一下和ArrayList不同的方法。

首先我们还是来看以下Vector中定义的变量

ArrayList是List接口的 可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。ArrayList继承自 AbstractList,这是一个抽象类对一些基础的list操作做了一些封装.实现了RandomAccess 标记接口,表明可以实现快速随机访问.实现了Cloneable接口的实现表示该容器具有Clone函数操作,Serializable是序列化。

每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的大小,就可在构造ArrayList实例时指定其容量。

在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。

注意,此实现不是同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。

ArrayList这个数据结构比较简单,总体来说,ArrayList底层结构是数组,他的很多方法都是从数组上面演变而来的。

下面我们先来看一下ArrayList中的一些初始值

通过上一篇 IOC & AOP 详解 我们了解了 IOC 和 AOP 这两个思想,下面我们先不去考虑Spring是如何实现这两个思想的,先通过一个银行转账的案例,分析一下该案例在代码层面存在什么问题?分析之后使用我们已有的知识来解决这些问题(痛点)。

其实这个过程就是在一步步分析并手动实现 IOC 和 AOP 。

动态代理的常用实现方式是反射。反射机制是 Java 语言提供的一种基础功能,赋予程序在运行时自省(introspect,官方用语)的能力。通过反射我们可以直接操作类或者对象,比如获取某个对象的类定义,获取类声明的属性和方法,调用方法或者构造对象,甚至可以运行时修改类定义。

动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,很多场景都是利用类似机制做到的,比如用来包装 RPC 调用、面向切面的编程(AOP)。

JDK 自身提供的动态代理,就是主要利用了上面提到的反射机制。但动态代理不止有反射一种实现方式,还有其他的实现方式,比如利用传说中更高性能的字节码操作机制,类似 ASM、cglib(基于 ASM,一个 Java 字节码操作框架)、Javassist 等。简单来说,动态代理是一种行为方式,而反射或 ASM 只是它的一种实现手段而已。

JDK Proxy 和 CGLib 的区别主要体现在以下几个方面:

  • JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现;
  • Java 对 JDK Proxy 提供了稳定的支持,并且会持续的升级和更新 JDK Proxy,例如 Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;
  • JDK Proxy 是通过拦截器加反射的方式实现的;
  • JDK Proxy 只能代理继承接口的类;
  • JDK Proxy 实现和调用起来比较简单;
  • CGLib 是第三方提供的工具,基于 ASM 实现的,性能比较高;
  • CGLib 无需通过接口来实现,它是通过实现子类的方式来完成调用的。

settings.xml 的文件名就可以看出,它是用来设置 maven 参数的配置文件。settings.xml 中包含类似本地仓储位置、修改远程仓储服务器、认证信息等配置。

  • settings.xml 是maven的全局配置文件
  • pom.xml 文件是本地项目配置文件