Java单列集合——List
目录一. 集合的分类二. 集合的家族体系三. 不同集合的特点四. 集合的通用方法及源码分析五. 遍历集合的三种方式5.1 迭代器遍历5.2 增强for循环5.3 lambda表达式遍历六. List 集合的特殊方法七. ArrayList 集合详解八. LinkedList 集合详解一. 集合的分类Java中的集合是一个庞大的家族而其中集合主要非为两类第一类Collection 单列集合一次只能存取一个元素。第二类Map 双列集合一次存取两个元素以 KV方式存取数据。二. 集合的家族体系第一类Collection集合家族图如下图所示其中红色的是接口蓝色的是实现类。从图中可以看出Collection 又分为两类分别是List和Set在这里就需要说一下List集合和Set集合有什么区别了。三. 不同集合的特点List集合的特点添加的元素 有序可重复有索引。这里有序是指的是存和取是有序的而不是指从大到小排序可重复也很好理解就是允许有相同元素出现有索引是指每个元素都有对应的索引我们可以通过索引获取指定位置的数据。Set集合的特点添加的元素 无序不可重复无索引。这里的无序是指存入的顺序和取出的顺序有可能是不一样的不可重复就是指不能有相同的元素出现如果要添加的元素Set中已经存在的话会添加失败无索引就是说不能根据索引找到指定位置的元素。四. 集合的通用方法及源码分析我们接下来先说Collection集合Collection集合是单列集合的祖宗接口它的功能单列集合都可以继承使用如下图所示在这里有几点要注意1如果创建的是 Collection 类型对象remove() 方法只能使用对象的方式进行删除因为通过索引删除只能使用有序集合 List 时才可以使用Collection 本身是没有索引的在无序集合 Set 时也不可使用添加同理。2contains() 方法其实底层调用的是 equals() 作比较我们看 contains() 方法的源码即可得知如下图所示首先contains()方法调用了indexOf()方法然后indexOf()方法又调用了indexOfRange()方法从 indexOfRange() 方法体中可以看出判断集合中是否包含某个元素是采用for循环将集合中的所有元素都拿出来挨个使用equals()作比较。这里也是面试官可能会问到一个小细节要格外注意在这里我就要多提一句如果集合中存放的是String类型无需做什么如果存放的是自定义类型例如某个类的对象那么一定要在该类中重写equals()方法因为如果不在自定义类中重写equals()方法那么就会采用默认的比较内存地址我们都知道每个对象的内存地址都不一样所以就算我们两个对象存放的内容一样程序也会认为它们时不相等的如果我们重写了 equals()方法我们再去进行 for 循环遍历作比较比较的就会使内容而不是内存地址程序就会知道他们是相等就能得到我们想要的结果。若不重写 equals() 方法就无法得到我们想要的结果。3判断集合是否为空的方法其实底层是去判断数组的长度是否为空。五. 遍历集合的三种方式此外Collection集合有三种遍历方式第一种迭代器遍历第二种增强for遍历第三种Lambda表达式遍历5.1迭代器遍历使用迭代器遍历首先要获取到迭代器如下代码所示第一行代码我们可以理解成创建指针这个指针默认指向集合0索引的位置。第二行代码判断当前位置是否有元素。第三行代码创建对应类型的参数变量接受当前元素这里是string类型。第四行代码打印输出获取到的元素。上述操作只获取到了一个元素入股我们要获取集合中的所有元素该怎么做呢很简单只要在外边套一层while循环即可如下图所示可以看出while循环当it.hasNext() false时循环结束当集合中没有元素时刚好it.hasNext() false,遍历完成。这里有几个细节点需要注意1. 如果已经遍历结束让想继续获取会爆出NoSuchElementException异常。2. 在遍历结束后指针不会恢复到默认位置。3. 循环中只能使用一次next()方法。4. 迭代器遍历时不能用集合的方法进行增加或者删除。5.2 增强for循环1. 增强for循环底层就是迭代器为了简化迭代器的代码书写。2. 所有的单列集合和数组才可以使用增强for进行遍历。增强for循环书写格式如下代码举例如下5.3 lambda表达式遍历lambda表达式利用匿名内部类的形式他的底层原理其实也是采用了for循环遍历得到集合中的每一个元素。lambda表达式的格式为 - {} 其中中代表的是匿名内部类的形参列表{}中代表的是内部类的方法体。书写格式如下参数类型可以省略若参数只有一个(s)外边的小括号可以省略若方法体只有一行外边的大括号{}可以省略化简之后就可以写成coll.forEach(s - System.out.println(s));六. List 集合的特殊方法OK,上面我们说完了Collection集合之后我们再来说一说 List 集合因为 List 是 Collection 的子接口所以 Collection 接口中的方法 List 中都有此外因为 List 集合有索引所以多了很多关于索引的方法如下所示在这里关于 remove() 方法有一个点需要注意一下如下图所示可以看到此时集合中有三个元素分别是123这时如果我调用 remove(1)会发生什么现象呢各位认为这个方式是会删除元素为1的元素呢还是会删除索引为1的元素呢答案是会删除索引为1的元素这里要知道在Java中如果调用方法发生了重载那么会优先调用实参跟形参类型一样的方法。因为传入的1是int类型而根据索引删除也是int类型根据元素内容删除是Object类型所以会优先调用根据索引删除的方法如果想根据元素类型删除那么就需要把1先装箱成Integer类型这样就会调用根据元素内容删除的方法。List集合的遍历方式有五种除了Collection中的三种之外还有普通for循环遍历和列表迭代器遍历在这里普通for循环遍历就不再多说说一下列表迭代器便利列表迭代器和普通的迭代器差不多但是多了几个方法其中比较重要的就是add()和remove()这两个方法是可以在列表迭代时添加或者删除元素。七. ArrayList 集合详解在看这篇文章之前相比一些小伙伴可能对ArrayList集合多多少少有些了解听说过下面两句话。1ArrayList底层是数据结构数组默认长度为10.2当数组加满了之后会自动扩容至原来的1.5倍。但其实从严和意义上来讲这两句话并不完全对只有ArrayList底层数据结构是数组说对了为什么是这样呢且看我给你一一解释。当我们使用空参的方法创建了一个数组时在底层其实是创建了一个长度为0的数组你没有听错是长度为0而不是10底层数组的名字叫“elementData”而且有一个变量size记录它的长度初始size也为0。当我们添加第一个元素的时候底层就会创建一个新的长度为10的数组默认初始化值都为null如下图所示当我们向集合中加入第一个元素时size如下size这里有两层含义第一指当前数组中元素的个数第二下次要存放元素的位置。初始时size指向0当添加了元素 “a” 之后size向后移动一位后续添加元素同理。当我们的数组存满了10个元素之后再想存入元素时它就要进行扩容了创建一个新的数组为原数组长度的1.5倍再将原数组中的数据拷贝至新数组中去。这里要注意还有另外一种情况如果一次添加多个元素扩容至1.5倍之后仍然放不下那么创建的新数组的长度就会以实际的为准。我们接下来一起看看源码吧上图就是Java中ArrayList集合的空参构造方法只有一行代码我们点进elementData跟进可以看到这里面可以看到Java底层中ArrayList的名字就叫elementData我们再看看是谁赋值给了elementData如下还定义了size初始化值为0。所以我们可以得出结论当我们使用空参构造创建ArrayList数组时底层创建了一个长度为0而且名字为elementData的数组。我们来看ArrayList数组的 add() 方法的源码如下首先add里面又调用了重载调用了另一个add()方法如下这个add()方法里面做了判断判断是否需要扩容需要扩容调用了grow()方法方法源码如下这个grow()方法又调用了有参grow()方法源码如下这个grow()方法里面点用了newLength()方法去创建一个新的数组经过层层方法最终会得到新的扩容后的数组的长度并进行返回以上就是关于ArrayList集合的扩容全过程。八. LinkedList 集合详解LinkedList集合它的底层数据结构是双向链表链表最显著的特点就是查询慢增删快但是如果查询的是首尾元素查询效率也很快。当然了因为它是链表所以也添加了特有的API如下图所示在看LinkenList源码之前我们先来看一个它的内部类其中Node表示链表中的一个个节点item表示要存的元素next表示下一个结点的位置prev表示上一个结点的位置。而且LinkedList类内部还维护了三个变量size指链表的长度first指链表的头节点last指链表的尾节点。链表添加元素调用方法如下图当添加第一个元素时first和last都会指向添加的元素而当添加第二个元素时first仍会指向第一个元素而last会指向第二个元素然后第一个元素和第二个元素互相指向后续添加元素同理。另一篇单列集合文章如下Java单列集合——Set_java 单列-CSDN博客
