java容器类1:Collection,List,ArrayList,LinkedList深入解读
1、 Iterable 与 Iterator
Iterable 是个接口,实现此接口使集合对象可以通过迭代器遍历自身元素.
public interface Iterable<T>
修饰符和返回值 | 方法名 | 描述 |
Iterator<T> | iterator() | 返回一个内部元素为T类型的迭代器 |
default void | forEach(Consumer<? super T> action) | 对内部元素进行遍历,并对元素进行指定的操作 |
default Spliterator<T> | spliterator() | 创建并返回一个可分割迭代器 |
第一个接口iterator()是jdk1.5引入的,需要子类实现一个内部迭代器Iterator遍历元素。
后两个接口是JDK1.8后新添加的,forEach(Consumer action)是为了方便遍历操作集合内的元素,spliterator()则提供了一个可以并行遍历元素的迭代器,以适应现在cpu多核时代并行遍历的需求.
其中我们可以看下default修饰符,这也是Java 8后新出现的,我们知道,如果我们给一个接口新添加一个方法,那么所有他的具体子类都必须实现此方法,为了能给接口拓展新功能,而又不必每个子类都要实现此方法,Java 8新加了default关键字,被其修饰的方法可以不必由子类实现,并且由dafault修饰的方法在接口中有方法体,这打破了Java之前对接口方法的规范.
Iterator 为迭代器类,用于遍历容器。
public interface Iterator<E>
修饰符和返回值 | 方法名 | 描述 |
boolean | hasNext() | 判断迭代器所指元素是否为最后一个 |
E | next() | 下一个元素 |
default Void | remove() | 移除迭代器只想的当前元素 |
default Void | forEachRemaining(Consumer<? super E> action) | 遍历迭代器指向元素后面的所有元素 |
AbstartList中实现了Iterator类作为List内部的迭代器,用于访问内部元素,其代码如下:
- private class Itr implements Iterator<E> {
- /**
- * Index of element to be returned by subsequent call to next.
- */
- int cursor = 0;
- /**
- * Index of element returned by most recent call to next or
- * previous. Reset to -1 if this element is deleted by a call
- * to remove.
- */
- int lastRet = -1;
- /**
- * The modCount value that the iterator believes that the backing
- * List should have. If this expectation is violated, the iterator
- * has detected concurrent modification.
- */
- int expectedModCount = modCount;
- public boolean hasNext() {
- return cursor != size();
- }
- public E next() {
- checkForComodification();
- try {
- int i = cursor;
- E next = get(i);
- lastRet = i;
- cursor = i + 1;
- return next;
- } catch (IndexOutOfBoundsException e) {
- checkForComodification();
- throw new NoSuchElementException();
- }
- }
- public void remove() {
- if (lastRet < 0)
- throw new IllegalStateException();
- checkForComodification();
- try {
- AbstractList.this.remove(lastRet);
- if (lastRet < cursor)
- cursor--;
- lastRet = -1;
- expectedModCount = modCount;
- } catch (IndexOutOfBoundsException e) {
- throw new ConcurrentModificationException();
- }
- }
- final void checkForComodification() {
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- }
- }
2、Collection
Collection 是容器类的接口,里面可以存放很多Elements,很多容器都实现了该接口。
public interface Collection<E> extends Iterable<E>
修饰符和返回值 | 方法名 | 描述 |
int | size() | 判断容器的大小 |
boolean | isEmpty() | 判空 |
boolean | contains(Object o) | 是否包含某元素 |
Object[] | toArray() | 转化为数组 |
<T> T[] | toArray(T[] a) | 将容器中所有元素拷贝到a中,如果a不够大,则拷贝到返回的数组中。(见AbstactCollection中实现) |
boolean | add(E e) | 添加元素 |
boolean | remove(Object o) | 移除容器中第一个出现Object对象,如果Object为null,则移除第一个出现的null。如果没有Object对象返回false |
boolean | containsAll(Collection<?> c) | 包含c中所有元素 |
boolean | addAll(Collection<? extends E> c); | 将c中所有元素添加到容器中 |
boolean | removeAll(Collection<?> c) | 如果容器中包含c中的元素,删除。(调用remove(Object)) |
default boolean | removeIf(Predicate<? super E> filter) | 移除所有符合条件的元素(JDK1.8中引入) |
boolean | retainAll(Collection<?> c) | 保留在c中出现的元素,其它全部移除 |
boolean | clear() | |
boolean | equals(Object o) | 与o中所有元素都相等则相等 |
int | hashCode() | c1.equals(c2) 那么他们的hashCode()一定相等,反之不成立 |
default Spliterator<E> | spliterator() | 在所有元素之上创建一个Spliterator |
default Stream<E> | stream() | |
default Stream<E> | parallelStream() |
上述很多函数的实现可以参考 AbstactList中的实现。
3. List
List是一个接口,继承了Collection中的所有接口,并且添加了自身相关接口和具体实现。
由上图可以看出,Collection分成了三个分支,List就是其中一个,下面我们具体分析一下增加的接口。
public interface List<E> extends Collection<E>
修饰符和返回值 | 方法名 | 描述 |
Collection中的所有接口 | ||
default void | replaceAll(UnaryOperator<E> operator) | 将运算操作后的结果替换List中原有元素 |
default void | sort(Comparator<? super E> c) | 将List中所有元素排序 |
E | get(int index); | |
E | set(int index, E element) | |
E | remove(int index) | |
int | indexOf(Object o) | o在List中第一次出现的位置 |
int | lastIndexOf(Object o) | o在List中最后一次出现的位置 |
ListIterator<E> | listIterator() | 获取List的迭代器 |
ListIterator<E> | listIterator(int index) | 获取从某个位置开始的迭代器,index为迭代器起始位置 |
List<E> | subList(int fromIndex, int toIndex) | 子List |
上表可以看出,增加了 和index相关的接口,根据index对List进行的get、set、remove等操作。另一类添加的接口是ListIteator相关的,获取List的迭代器。
3.1 ListIterator
public interface ListIterator<E> extends Iterator<E>
ListIterator 不光包含Iterator中的三个接口还增加了作为一个List迭代器应该有的接口,如 next(),previous()等
修饰符和返回值 | 方法名 | 描述 |
Iterator中的所有接口 | ||
boolean | hasNext() | |
E | next() | |
boolean | hasPrevious() | |
E | previous() | |
int | nextIndex() | |
int | previousIndex() | |
void | remove() | 删除next()返回元素 |
void | set(E e) | 替换next()返回的元素 |
void | add(E e) | 在nextIndex()后插入元素 |
首先需要明确ListIterator迭代器的作用:
- 允许我们向前、向后两个方向遍历 List;
- 在遍历时修改 List 的元素;
- 遍历时获取迭代器当前游标所在位置。
注意,迭代器 没有当前所在元素一说,它只有一个游标( cursor )的概念,这个游标总是在元素之间,比如这样:
迭代器的初始位置:
调用next()后迭代器的位置
调用 previous() 游标就会回到之前位置。当向后遍历完元素,游标就会在元素 N 的后面
也就是说长度为 N 的集合会有 N+1 个游标的位置。
3.2 AbstractCollection / AbstractList
在上面的继承关系图中并没有显示出AbstractCollect和AbstractList的存在,但是在阅读源码的时候经常遇到这两个类,下面讲一下这两个类的作用。
首先需要明确的一点是AbstractCollect和AbstractList都是抽象类而不是接口,它们分别实现了Collection和List接口中的部分函数。这样继承者直接继承AbstractXXXX 而不需要自己重复实现里面的所有接口。这两个抽象类抽离了公共部分,进行了实现,减少后续类的工作量。
ArrayList 和LinkedList都直接或者间接继承了AbstartList类。下图为这两个类的继承关系:
具体这两个类里面实现了哪些接口,请自己阅读源码不再讲解。
LinkedList中modCount和expectedModCount作用
在AbstractList中可以看到这两个变量,它们用于表示List被修改的次数,这其中包括了调用集合本身的add等方法等修改方法时进行的修改和调用集合迭代器的修改方法进行的修改。而expectedModCount则是表示迭代器对集合进行修改的次数。
那么知道这两个值有什么作用呢?
如果创建多个迭代器对一个集合对象进行修改的话,那么就会有一个modCount和多个expectedModCount,且modCount的值之间也会不一样,这就导致了moCount和expectedModCount的值不一致,从而产生异常。
https://www.cnblogs.com/zhangcaiwang/p/7131035.html
3.3 ArrayList
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable我们看到ArrayList的定义的时候很可能会出现一个疑问,为什么它已经继承了AbstractList了还需要去实现List<E>接口? 答案是,后面的List<E>可以去掉,这里只是让阅读者明白ArrayList是个List。 终于好不容易到了开发者常用的类了,有点窃喜:transient Object[] elementData;这个很重要的成员变量elementData数组用来存放ArrayList中的元素,当第一个元素添加进来后,它的默认长度变为10。(java中“transient”关键字修饰的成员变量在类序列化的过程中不会被持久化到文件中,保证该成员变量保存信息的安全。)
ArrayList的构造函数中可以直接指定elementData数组的长度,那么问题来了,当数组已经完全被占用再向ArrayList中添加元素时,如何再分配更大长度的数组?如何把旧数据拷贝到新数组中?答案见下面这段源码
- private void grow(int minCapacity) { //最少需要的容量
- // overflow-conscious code
- int oldCapacity = elementData.length;
- int newCapacity = oldCapacity + (oldCapacity >> 1); // 分配最小容量的 1.5倍
- if (newCapacity - minCapacity < 0)
- newCapacity = minCapacity;
- if (newCapacity - MAX_ARRAY_SIZE > 0)
- newCapacity = hugeCapacity(minCapacity); // 最大容量比较(Inter.MAX_VALUE)
- // minCapacity is usually close to size, so this is a win:
- elementData = Arrays.copyOf(elementData, newCapacity); // 将旧数据拷贝到新数组中
- }
其它接口不再细说。
3.4 LinkedList
- public class LinkedList<E>
- extends
AbstractSequentialList<E>
- implements List<E>, Deque<E>, Cloneable, java.io.Serializable
作为一个链表类型,首先需要熟悉一下节点类:Node(LinkedList静态内部类)
- private static class Node<E> {
- E item;
- Node<E> next;
- Node<E> prev;
- Node(Node<E> prev, E element, Node<E> next) {
- this.item = element;
- this.next = next;
- this.prev = prev;
- }
- }
在LinkedList中有三个主要成员变量:
- transient int size = 0; // 链表长度
- transient Node<E> first; // 链表的首节点
- transient Node<E> last; // 链表的末节点
LinkedList构造方法:
- /**
- * Constructs an empty list.
- */
- public LinkedList() {
- }
- /**
- * Constructs a list containing the elements of the specified
- * collection, in the order they are returned by the collection's
- * iterator.
- *
- * @param c the collection whose elements are to be placed into this list
- * @throws NullPointerException if the specified collection is null
- */
- public LinkedList(Collection<? extends E> c) {
- this();
- addAll(c);
- }
clear()方法:遍历LinkedList 置null
- public void clear() {
- // Clearing all of the links between nodes is "unnecessary", but:
- // - helps a generational GC if the discarded nodes inhabit
- // more than one generation
- // - is sure to free memory even if there is a reachable Iterator
- for (Node<E> x = first; x != null; ) {
- Node<E> next = x.next;
- x.item = null;
- x.next = null;
- x.prev = null;
- x = next;
- }
- first = last = null;
- size = 0;
- modCount++;
- }
public E set(int index, E element):将某个Index指向的Node置变为element
- public E set(int index, E element) {
- checkElementIndex(index);
- Node<E> x = node(index); //node(Int i) 遍历LinkedList查找第i个Node
- E oldVal = x.item;
- x.item = element;
- return oldVal;
- }
public void add(int index, E element):在链表的index位置添加一个节点
这里还有众多的插入删除等函数,其主要思想还是对链表的操作(插入删除等),详情阅读源码。
3.5 ArrayList 与LinkedList 性能比较
两种List的存储结构决定了两者的性能:ArrayList以数组形式存储,LinkedList以链表形式存储
ArrayList是以数组的形式存储的,所以他的遍历很快,可以根据index很快找到对应元素。但是,ArrayList的插入和删除操作较慢,如果需要在数组中插入一个元素或者删除一个元素较麻烦。
LinkedList的索引操作较慢(需要遍历列表才能找到对应索引元素),但是它的插入和删除操作效率高(只需要控制前后指针就能实现插入和删除)。
参考:
https://www.cnblogs.com/bushi/p/6647006.html
https://www.jianshu.com/p/047e33fdefd2
java容器类1:Collection,List,ArrayList,LinkedList深入解读的更多相关文章
- java容器类3:set/HastSet/MapSet深入解读
介绍 Set:集合,是一个不包含重复数据的集合.(A collection that contains no duplicate elements. ) set中最多包含一个null元素,否者包含了两 ...
- 【转】java 容器类使用 Collection,Map,HashMap,hashTable,TreeMap,List,Vector,ArrayList的区别
原文网址:http://www.360doc.com/content/15/0427/22/1709014_466468021.shtml java 容器类使用 Collection,Map,Hash ...
- java集合框架collection(2)ArrayList和LinkedList
ArrayList是基于动态数组实现的list,而LinkedList是基于链表实现的list.所以,ArrayList拥有着数组的特性,LinkedList拥有着链表的特性. 优缺点 ArrayLi ...
- Java 集合的简单实现 (ArrayList & LinkedList & Queue & Stack)
ArrayList 就是数组实现的啦,没什么好说的,如果数组不够了就扩容到原来的1.5倍 实现了迭代器 package com.wenr.collection; import java.io.Seri ...
- Java总结 - List实现类ArrayList&LinkedList
本文是根据源码进行学习的,如果我有什么理解不对的地方请多指正,谢谢您 上面基本就是List集合类的类图关系了,图中省略掉了比如Cloneable等标记接口,那么List分别具体的主要实现类有:Arra ...
- Java集合框架Collection(1)ArrayList的三种遍历方法
ArrayList是java最重要的数据结构之一,日常工作中经常用到的就是ArrayList的遍历,经过总结,发现大致有三种,上代码: package com.company; import java ...
- java 容器(collection)--ArrayList 常用方法分析 源码分析
ArrayList 介绍 打开jdk源码看看官方文档的介绍 粗糙的翻译下大致意思是: List接口的可调整大小的数组实现.实现了所有可选的列表操作,并允许所有元素,包括 null .除了实现List接 ...
- java容器类2:Map及HashMap深入解读
Java的编程过程中经常会和Map打交道,现在我们来一起了解一下Map的底层实现,其中的思想结构对我们平时接口设计和编程也有一定借鉴作用.(以下接口分析都是以jdk1.8源码为参考依据) 1. Map ...
- 深入理解JAVA集合系列四:ArrayList源码解读
在开始本章内容之前,这里先简单介绍下List的相关内容. List的简单介绍 有序的collection,用户可以对列表中每个元素的插入位置进行精确的控制.用户可以根据元素的整数索引(在列表中的位置) ...
随机推荐
- Linux 基础教程 37-进程命令
pidof 我们知道每个小孩一出生就会一个全国唯一的编号来对其进行标识,用于以后上学,办社保等,就是我们的身份证号.那么在Linux系统中,用来管理运行程序的标识叫做PID,就是大家熟知的进程 ...
- 无线破解那点事(PJ)
有一段时间没有写博客了.只能说苦逼学生党伤不起啊,还好没挂-废话不说了,近期将会讲讲无线PJ那点事,也不是啥干货,就一些先前的笔记分享把. 0.无线网卡 想要提高破解效率,一块猛一点的USB无线网卡是 ...
- shell 脚本 随机抽取班上学生
#!/bin/bash # jw=('王浩' '谢云生' '黄科杨' '何星宇' '张宸兵' '邓培林' '刘桃' '杨沛东' '楚齐文' '咸鱼' '杨东' '>黄庭辉' '郑少文' '师靖' ...
- 两段 PHP 代码比较优劣
// 代码一 public function getPCA($level = false) { $results = array(); $where = $level ? " where f ...
- Java Web系列:JAAS认证和授权基础
1.认证和授权概述 (1)认证:对用户的身份进行验证. .NET基于的RBS(参考1)的认证和授权相关的核心是2个接口System.Security.Principal.IPrincipal和Syst ...
- 指定的 LINQ 表达式包含对与不同上下文关联的查询的引用
解决方法是分两次查询. 报错的原因是在涉及到内存中的对象与EF里的对象混合查询时,内存中的对象要是基元类型. 第一次查询实际上会因为EF的延时加载,不会立即将数据查询到内存中. 解决方法是对第一次查询 ...
- windows服务安装记录
首先打开cmd. 进入这个地址 C:\Windows\Microsoft.NET\Framework\v4.0.30319 执行操作 InstallUtil.exe E:\QueueWinServi ...
- ASP.NET MVC学习目录
一.ASP.NET MVC原理详解 1.了解MVC架构模式 3.学习ASP.NET MVC的必备语言知识 4.MVC中的razor语法详解 5.ASP.NET MVC路由系统机制详细讲解 6.ASP. ...
- cesium编程入门(九)实体 Entity
cesium编程入门(九)实体 Entity 在cesium编程入门(五)绘制形状提到过添加实体的方法,这一节聊一聊实体相关的一些内容: 先来看 Entity 的各个属性 id 唯一标志,如果没设置, ...
- [Cocos2d-x for WP8学习笔记] 获取系统字体
在Cocos2d-x for WP8较新的版本中,获取字体这一块,在wp8下默认返回了null,只能内嵌字体文件解决. 其实可以通过下面的方法获取系统的字体文件 CCFreeTypeFont::loa ...