对Java程序开发而言,ArrayList 的使用频率是非常高的,尤其在进行 JavaWeb 开发的时候,ArrayList 和 HashMap 这两个类,相信你一定不会陌生,因为天天都在用嘛。
本系列对 ArrayList 做一个解析,同时把Java基础知识个串连进去。一开始我会对如何使用 ArrayList 做一个简要的说明,然后,我们来仿照 ArrayList 封装一个自己的集合框架MyList,通过练习,来一步一步猜想ArrayList 可能的实现方式。
最后,深入到ArrayList 的源码进行解读。
为什么要学习源码?
很简单,一个知道源码的人和一个不知道源码的人,虽然都能使用 ArrayList ,但是,他们在使用的时候,心态是完全不一样的。
只有当你深入了源码,然后你才会对它的一些细节有更充分的认识。这是一本万利的事情。
当然,对于初学者,还是尽量以使用为主,因为源码的话,毕竟有一定的难度。如果一味地追求这些东西,可能会大大降低自己的学习兴趣和热情。
1、 ArrayList 概述
副本难度:一颗星
经验值:500
首先来看一下文档,
<h5>
All Implemented Interfaces:
Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess
</h5>
<p>
从图中可以看到,ArrayList 实现了Iterable接口,这个接口表示一种迭代的能力。
</p>
既然是ArrayList,那么肯定和 List 有关,所以它果然继承了List接口。
接口的知识点在这里就用上了。
其他接口我们暂且不谈,先继续。
然后,我们看一下官方对ArrayList 做出的说明。
文档中有这么一句:
Resizable-array implementation of the List interface.
这句话是说,ArrayList 是对List接口的一个实现,实现方式是利用一个可改变尺寸的数组,也就是说,它的底层就是一个数组。而且是可改变尺寸的数组,说明这个数组是动态的。
哦,难怪它叫ArrayList ,Array就是数组的意思,那么它肯定和数组有关。
**Implements all optional list operations, and permits all elements, including null. **
ArrayList 实现了list接口的所有方法,并且允许空元素。
前半句是肯定的,因为在Java中,如果一个类实现了一个接口,那么就必须要重写该接口里所有的抽象方法。
我们知道,接口里只有方法的声明,没有方法的实现。
我对接口的理解,总结以后就只有一句话:
Java类实现接口,就是给这个类本身添加了一个新的身份。
ArrayList是一个类,那么它的身份就是ArrayList,你问他“你叫什么名字呀?”,他肯定会毫不犹豫地告诉你,“我叫ArrayList!”。
白天,我们大家都以为它是ArrayList,结果到了夜晚。
ArrayList竟然摇身一变,成为了Iterable!
原来,ArrayList实现了Iterable,所以它也就拥有了Iterable的身份。
接口其实就是这个意思。网上对接口的解释众说纷纭,反正我总结下来就是这么一句话,也不想搞那么复杂了。
ArrayList不仅有Iterable的身份,还拥有其他好几种身份,比如List,Collection等。
这个情况也叫作多态。
新建一个测试类。
public class TestArrayList1 {
public static void main(String[] args) {
}
}
现在我新建一个ArrayList类的实例
ArrayList list = new ArrayList();
这肯定是没问题的。
文档上说,ArrayList 实现了Iterable接口,那么也就是说,它的另一个身份是Iterable。
所以,我这样写是不是也没有问题啊。
Iterable iterable = new ArrayList();
就好像白天是普通的上班族,一旦到了夜晚,就。。。
总之,
Iterable iterable = new ArrayList();
这句话的含义就是说,它原来是普通的ArrayList,一旦情况需要,就变身成为Iterable。
好了,既然变成了 Iterable ,那么它是不是也就拥有了 Iterable 的能力?
我们来看一下 Iterable 有哪些方法?
去除继承自Object类的方法不谈,Iterable 只有一个方法,就是 iterator()
Paste_Image.png它返回的是一个 Iterator ,这是一个迭代器。
通过这个迭代器,我们是不是就可以遍历 ArrayList 中所有的数据了呀?
首先,我们需要一个迭代对象:
Iterable iterable = new ArrayList();
Iterator it = iterable.iterator();
当然,这个例子中,ArrayList里面没有数据。
现在思考一个问题,是不是我非得把 ArrayList 对象改变成 Iterable 身份,才可以调用 iterator() 方法呢?
当然不是了,ArrayList 有一个身份是 Iterable ,所以它具有 Iterable 的所有能力(方法),这没问题。那么难道 ArrayList 不变身,就没有 Iterable 的能力了吗?
答案自然是否定的。
一个小说家如果有一天转行去写代码了,那么你觉得他还会不会写小说呢?
肯定会嘛,这没有什么好怀疑的。
所以,代码这么写是不是也没关系?
ArrayList list = new ArrayList();
for (int i = 0; i < 100; i++) {
list.add(i);
}
Iterator it = list.iterator();
循环输出
while(it.hasNext()){
System.out.println(it.next());
}
Paste_Image.png
总结一下,ArrayList 它只要实现 Iterable 接口,那么它就必须要拥有 Iterable 规定的所有能力,也就是方法。而接口中,我们知道它里面只有方法的声明,没有方法的实现,所以 ArrayList 需要实现这些方法,实现接口就是这么个意思。
2、 ArrayList常用方法
副本难度:三颗星
经验值:800
2.1、 属性和方法的调用问题
在调用ArrayList的方法之前,我们需要先获得一下ArrayList的实例对象,除了静态方法,其他所有的方法,都只能由对象来调用。
ArrayList是一个类,我更愿意把它称为一个** 数据模板**。它只是一个模板而已,不是一个实实在在的对象,这一点首先要确定。
就好像工厂生产一个产品,首先是不是要有一个模板和设计图纸,这个模板决定了你这个产品是一种怎样的形状,以及可能会具备哪些功能?图纸则决定了功能的具体实现。
比如生成一部手机,模板开出来就是一个扁平的长方体的样子,可是光有模板还不行,你还得规定它的一些具体细节。
这些细节就好比是Java类的构造方法,以及其他的一些方法实现。
但是,你光给客户模板和图纸行吗?
一般来说是不行的。
至于静态方法,我们知道,我们调用静态方法的时候,不需要先生成一个实例,可以通过类名直接调用。
这就相当于,在弄模版的时候,这些功能就已经定制在里面了。
你买手机的时候,里面不是经常有一些内置的应用吗?有些删都删不掉,这不就相当于静态方法吗?
(我只是举一个例子啊,你不要非得较真说我可以ROOT一下啊)
如果模板里面已经有了一些做好的功能,今后任何根据这个模具生成出来的产品也自带了这些功能。
如果模板里面已经做好了一些功能,那么我的确可以使用这个模板,而不需要真正拿到一个产品。
比如生产一部手机,它的模板里面已经做好了一个手电筒的功能,那么,你即便不给我一个真正的产品,仅仅给我一个模板,我是不是也可以用它的手电筒功能呢?
这就是静态方法。
所以我们常说,静态方法和静态属性为所有实例共用,不就是这个道理吗?
所以,正常情况下,我们调用一个类的非静态方法,是不是必须要先new一个对象?
好的,我们现在来 根据 ArrayList 模板生产一个 ArrayList 产品。
这样,我们才能调用它里面所有的非私有方法。
怎么生产呢,是不是new一下就可以啦?
ArrayList arrayList = new ArrayList();
2.2、 add方法
ArrayList是一个集合,既然是一个集合,那么它肯定是可以往里头添东西的。
怎么往里面添,用add,用add方法往里面加。
arrayList.add("HelloWorld"); //添加一个字符串
arrayList.add(new Integer(100));//添加一个Integer类型的数字
add的参数就是一个object,这就是多态,多态就是多种形态,多种身份的意思。object可以有多种形态。它可以是String,也可以是Integer,还可以是用户自定义的类型。
这里是不是又多态了。。。
这就是多态的一个用法。
2.3、 get 方法
既然能够往里面添加东西,是不是肯定还要拿出来啊。
怎么拿出来,用get方法拿出来,而且一次只能拿一个。不要多拿哦。
get 方法需要传入一个 int 类型的数字,这个数字就是元素对应的下标。
我们刚才第一个放进去的是 "HelloWorld" ,一个字符串。那么对应的下标就是0 。
第二个放进去的是new Integer(100),这是一个Integer对象,是一个实实在在的东西了。那么对应的下标就是1 。
现在,我取出第1个元素,应该是100。
试试看
Integer i = arrayList.get(1);
Paste_Image.png
报错了,因为 get 方法返回的是一个Object对象,而我们拿Integer 去接,就出问题了。
这是咋回事呢?
很简单,比如张三是一个医生,同时他还拥有一个人类的身份,可并不是所有的人类都是医生啊?
注意我下面分析的用词,能帮助你理解。
同理,Integer 是一个整数类型,同时它还拥有一个Object的身份,可并不是所有的 Object 都是Integer 啊?
同接口一样,A继承B,可以看成A同时拥有了B的身份。
如果A继续保持A的身份,那么它不仅拥有自己本身的能力,也拥有B的能力。只要A愿意,他完全可以展现B的能力。
如果A变身成为了B,那么它肯定不希望别人知道他是A。
就好比虽然他知道他可以变身成为奥特曼,但是一般情况下,他都不愿意曝露自己的身份。
所以如果A变身成为了B,那么A就只会使用B的能力,同时隐藏他本身的能力。
如果你是初学者,请仔细体会一下这其中的韵味。慢慢地,你就会对多态有一个更深入的理解。一段时间后,你再重新去看以前写的代码,会有不一样的感觉。
再举一个例子,帮助你理解。
我 new 一个 ArrayList :(在eclipse中)
ArrayList arrayList = new ArrayList();
换行,写上arrayList 。
我直接在arrayList 右边加一个点,然后会有提示:
Paste_Image.png这些都是它可以调用的方法和属性,哇,这么多。
如果我这样写呢?
Object arrayList = new ArrayList();
我也在arrayList 右边加一个点
Paste_Image.png不好意思,你现在只能调用 Object 类的属性和方法了。
嗯,再体会一下。
初学者在面向对象方面的理解总是会走弯路,如果你能把这些东西理清,对今后的学习会有巨大的好处。
继续。
我们这里就强转一下吧,因为我们知道 index 为1的元素是一个Integer类型的。
Integer i = (Integer) arrayList.get(1);
System.out.println(i);
结果
Paste_Image.png好的,是正确的。
2.4、 remove 方法
remove 方法可以删除集合中的元素,ArrayList给我们提供了很多删除元素的方法。
Paste_Image.png我们这里先看一下第一个方法。
这是通过数组下标来删除某一个特定的元素,我们刚才给ArrayList添加了两个元素,下标分别为 0,1 ,那么,如果我删除第0个元素,会怎么样呢?
首先,ArrayList的列表长度会不会改变?
我们可以通过size()方法来获取ArrayList当前的列表长度。
测试:
arrayList.remove(0);
System.out.println(arrayList.size());
打印出来是1,看来的确是影响长度了。
2.5、 toArray 方法
这个方法可以将ArrayList转换成一个Object数组。
例:
Object[] objs = arrayList.toArray();
for (int i = 0; i < objs.length; i++) {
System.out.println(objs[i].toString());
}
需要注意的是,哪怕你给ArrayList全部添加Integer类型的元素,也不能采用这样的代码:
Integer[] objs = (Integer[]) arrayList.toArray();
这是错误的,虽然编译的时候不会报错,但是运行无法通过。
因为ArrayList的add方法可以添加任意类型的参数,Java运行机制无法获知ArrayList中的元素是否可以都强制转换为你指定的类型。所以这种写法是不被允许的。
网友评论