Java数据结构:ArrayList与顺序表2
前一篇文章中,我们学习了如何模拟实现ArrayList类中的增删查改方法等,以及在使用ArrayList数组时必须实例化对象,因为ArrayList类是一个泛型类,需要明确传递的参数类型。
现在接着学习ArrayList类中的细节。
四、ArrayList的使用
看看ArrayList的源码中的一些成员变量:
ArrayList的构造方法
方法 | 解释 |
ArrayList() | 无参构造 |
ArrayList(int initialCapacity) | 指定顺序表初始容量 |
ArrayList(Collection<? extends E> c) | 利用其他 Collection 构建 ArrayList |
带参数的构造方法
1.ArrayList(int initialCapacity)
观看这个构造方法的源码:这个构造方法初始化了顺序表的容量
示例:构造一个具有10个容量的顺序表
在容量范围内,可以往数组中新增元素,但是ArrayList泛型类中已经规定了可以传递的参数类型,是Integer类型的参数,因此,只能传递整型类型的元素,而不能传递String类型的元素
2.ArrayList(Collection<? extends E> c)
观看这个构造方法的源码,它的参数是一个Collection接口类型的参数,表示只要实现了这个接口的,都是可以传参的;
?:表示通配符,表示的是要传递的参数类型,这个通配符以继承 E 类为上界,即必须是 E 类或者 E 类的子类。
示例:
将list引用作为构造方法参数,而list是ArrayList<Integer>类型,即作为参数传递后,向上转型编变成了 Collection<? extends E>类型,根据类型推导,E就是Integer类型,?就是Integer,而在list引用中,本身就初始化了一些元素,那么当它作为参数传递给另一个构造方法时,会将这个元素也默认传递过去。
看运行结果,它会将自身增加的和其他的类的原来就有的元素打印出来:
如果这个构造方法参数不是继承于Integer类的,会编译报错:
无参数构造方法
观看无参数构造方法的源码:也就是没有初始化数组容量的构造方法,此时的数组是一个空数组,即长度为0,源码中也是将该无参构造方法赋值为一个空数组:
示例:
问题:这个无参构造方法,其实并没有给分配内存,那么为什么还可以add新增数组元素呢?
解决这个问题,我们先看看add方法的源码:
总结来说,就是在第一次add的时候,分配内存空间大小默认是10,如果后边add的时候,发现满了,那么就是在原始内存空间大小的基础上1.5倍进行扩容,即在原始内存空间大小的基础上增加5个内存空间;即顺序表会自动扩容。
五、ArrayLIst的常见操作
方法 | 解释 |
boolean add(E e) | 尾插 e |
void add(int index,E element) | 将 e 插入到 index 位置 |
boolean addAll(Collection<? extends E> c) | 尾插 c 中的元素 |
E remove(int index) | 删除 index 位置元素 |
boolean remove(Object o) | 删除遇到的第一个 o |
E get(int index) | 获取下标 index 位置元素 |
E set(int index,E element) | 将下标 index 位置元素设置为 element |
void clear() | 清空 |
boolean contains(Object o) | 判断 o 是否在线性表中/线性表中是否包含 o |
int indexOf(Object o) | 返回第一个 o 所在下标 |
int lastindexOf(Object o) | 返回最后一个 o 的下标 |
List<E> sublist(int fromIndex,int toIndex) | 截取部分 list |
boolean addAll(Collection<? extends E> c)尾插 c 中的元素
示例:
运行结果:
boolean remove(Object o)删除遇到的第一个 o
示例:
运行结果:
为什么上述的代码中会出现一道红色的线呢?—— 其实这代表这个方法现在已经很少去使用了。
List<E> sublist(int fromIndex,int toIndex)截取部分 list
示例:
运行结果:
思考:使用sublist方法,是在原顺序表上进行截取呢,还是会重新创建一个顺序表,然后再在这个顺序表上截取呢?
我们可以用代码来验证,使用set方法在test中增加一个元素,然后再次打印一下list的内容,看看截取到的内容还是不是[1,2,3]
我们看运行的结果:
这就说明,使用sublist方法截取是在原来的顺序表中进行截取的。
即 subList()返回的是原列表指定范围的视图,它们共享底层数据存储。
六、ArrayList的遍历
ArrayList 可以使用三种方式遍历:for循环+下标、for-each、使用迭代器
1. for循环
需要使用到size()、get()方法
运行结果:
2. for-each遍历
运行结果:
3. 使用 iterator 迭代器遍历
我们看看iterator的源码:它的返回值是一个泛型类
如果直接打印,会是一串地址:
那么,如何打印内容呢?
—— 需要用到 hasNext方法和 next方法。
hasNext() 和 next()
这两个方法的作用:
比如,有一组数据 1 2 3 4 5,当使用 hasNext 方法检测到下一个位置有元素时,再使用 next 方法可以返回这个 下一个位置元素 ,一直循环访问,知道结束。
运行结果:
4. 使用 ListIterator 迭代器遍历
运行结果:
使用 LIstIterator 迭代器扩展
hasPrevious() 和 previous()
这两个方法的作用:
注意:是从列表的最后一个元素的后一个位置开始遍历的,也就是反向遍历。
运行结果:
一道练习题
题目:删除第一个字符串中出现的所有第二个字符串中出现的字符
使用 列表list 的方式解决。
示例:输入: s1 = hello world s2 = eord
输出: list = hll wl
思路:创建一个列表,使用 contains 方法,它的作用是判断该元素是否在线性表中/线性表中是否包含该元素。
如果第二个字符串中包含第一个字符串中的字符,就不add放入如列表中,否则,放入列表中,然后返回列表。
注意:contains方法的返回值是String类型,ArrayList类的参数是Character字符类型(因为列表中存储的是一个个字符)
运行结果:
如果运行结果不想要有 ,的出现,可以使用size和get方法将列表中的内容一个个打印出来:
运行结果: