目录
一、线性表
二、顺序表
(1)顺序表概念
(2)接口的实现
三、ArrayList介绍
四、ArrayList使用
(1)ArrayList的构造
(2)ArrayList的常见方法
1.public int size()
2.public boolean add(Long e)
3.public void add(int index, Long e)
4.public Long remove(int index)
5.public boolean remove(Long e)
6.public Long get(int index)
7.public Long set(int index, Long e)
8.public int indexOf(Long e)
9.public int lastIndexOf(Long e)
10.public boolean contains(Long e)
11.public void clear()
12.public boolean isEmpty()
(3)ArrayList的遍历
1.for循环下标
2.foreach
3.迭代器
(4)ArrayList的扩容机制
(5)自定义ArrayList的完整代码及测试过程
五、实例演示
(1)扑克牌
(2)杨辉三角
线性表(Linear list)是n个具有相同特性的数据元素的有限序列,线性表是一种在实际生活中应用广泛的数据结构。
常见的线性表:顺序表、链表、栈、队列。
线性表在逻辑上是线性结构,但物理上不一定是连续的。线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
自定义的线性表(以接口的形式体现)
1.元素类型 Long
1)int 下标 long 元素
2)包装类:体现对象的形式而不是基本类型
2.准备线性表的规定
3.教学过程中体现(在Java真实的设计中没有这步要求):
有元素的位置!=null没有元素的位置==null
注解小贴士:
@return | 对方法返回值的说明,如果方法的返回值类型是 void ,则不能使用该注解。 |
@param | 表示标签,可以归档方法或构造器的某个单一参数,或者归档类、接口以及泛型方法的类型参数。在使用@ param标签时,我们应该针对方法的每一个参数都使用一个该标签。每个段落的第一个词会被当作参数名,而余下的部分则会被当作是对它的描述。 |
@override | 当前的方法定义将覆盖超类中的方法, 限定必须重写父类方法 |
public interface MyList {
/**
* 返回线性表中的元素个数
* @return
*/
int size();
/**
* 将 e 尾插到线性表中,一定返回 true
* @param e
* @return
*/
boolean add(Long e);
/**
* 将 e 插入到线性表的 index 位置,从 [index, size()) 向后移
* index 的合法下标 [0, size()]
* 如果下标不合法:抛出一个 ArrayIndexOutOfBoundsException
* @param index
* @param e
*/
void add(int index, Long e);
/**
* 删除 index 位置的元素
* index 的合法下标:[0, size())
* 如果下标不合法:抛出一个 ArrayIndexOutOfBoundsException
* @param index
* @return 从线性表中删除掉的元素
*/
Long remove(int index);
/**
* 从前到后,删除第一个遇到的 e( equals() == true)
* @param e
* @return 删除成功:true,没有该元素:false
*/
boolean remove(Long e);
/**
* 直接返回 index 位置的元素
* index: [0, size())
* @param index
* @return
*/
Long get(int index);
/**
* 使用 e 替换 index 位置的元素
* @param index [0, size())
* @param e
* @return 原来 index 位置的元素
*/
Long set(int index, Long e);
/**
* 返回第一次遇到 e 的下标(equals() == true)
* @param e
* @return 如果没有找到,返回 -1
*/
int indexOf(Long e);
/**
* 从后往前,返回第一次遇到 e 的下标(equals() == true)
* @param e
* @return 如果没有找到,返回 -1
*/
int lastIndexOf(Long e);
/**
* 线性表中是否包含 e(equals)
* @param e
* @return
*/
boolean contains(Long e);
/**
* 清空线性表
*/
void clear();
/**
* 判断线性表是否是空的(empty) 等价于一个元素都没有
* @return
*/
boolean isEmpty();
}
在集合框架中,ArrayList是一个普通的类,实现了List接口
1. ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
2. ArrayList实现了Cloneable接口,表明ArrayList是可以clone的
3. ArrayList实现了Serializable接口,表明ArrayList是支持序列化的
4. 和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或 者CopyOnWriteArrayList
5. ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表
< ?>是泛型,泛型详情请见http://t.csdn.cn/p05n6
方法 | 解释 |
ArrayList() | 无参构造 |
ArrayList(Collectionc) | 利用Collection构建ArrayList |
ArrayList(int initialCapacity) | 指定顺序表初始容量 |
ps:对于初学者来说掌握一、三两种就可以了
public class MyArrayList implements MyList {
// 定义属性
private Long[] array; // array.length 就是容量(capacity)
private int size; // 保存元素的个数
// 构造方法
public MyArrayList() {
// 容量没有规定,自行定义为 7 个
this.array = new Long[7];
// 这一步不是必须的,把数组中的每个位置都初始化成 null
for (int i = 0; i < array.length; i++) {
array[i] = null;
}
// 元素个数 = 0
this.size = 0;
}
public MyArrayList(int initialCapacity) {
this.array = new Long[initialCapacity];
// 这一步不是必须的,把数组中的每个位置都初始化成 null
for (int i = 0; i < array.length; i++) {
array[i] = null;
}
// 元素个数 = 0
this.size = 0;
}
}
方法 | 解释 |
boolean add() | 尾插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 是否在线性表中 |
int indexOf(Object o) | 返回第一个 o 所在下标 |
int lastIndexOf(Object o) | 返回最后一个 o 的下标 |
List |
截取部分 list |
常见方法的实现:
@Override
public int size() {
return size;
}
// 时间复杂度是O(1)
@Override
public boolean add(Long e) {
// 为 size 赋予一个新的逻辑含义 —— 尾插时的元素的位置
array[size] = e;
// 让元素个数的记录 + 1
size = size + 1;
// 返回 true 表示插入成功
return true;
}
// 时间复杂度:O(n)
@Override
public void add(int index, Long e) {
if (index < 0 || index > size) {
// 下标不合法
throw new ArrayIndexOutOfBoundsException("下标不合法");
}
// 此时下标一定是合法的
// 将每个结点往后移位
// i: 移位时的出发点
for (int i = size - 1; i >= index; i--) {
// array[i] 的元素搬到 array[i + 1]
array[i + 1] = array[i];
}
// 将 e 放到 index 位置
array[index] = e;
// 元素个数增加了 1
size = size + 1;
}
// 时间复杂度: O(n)
@Override
public Long remove(int index) {
// 下标合法性检查
if (index < 0 || index >= size) {
// 下标不合法
throw new ArrayIndexOutOfBoundsException("下标不合法");
}
// 先把要删除的元素保存起来
Long e = array[index];
// i 代表搬元素时的起始位置 [index + 1, size)
for (int i = index + 1; i < size; i++) {
array[i - 1] = array[i];
}
// 把不存在的元素置为 null
// 真实中是否要置 null,需要根据性能考虑
array[size - 1] = null;
// 元素个数减一
size = size - 1;
// 返回被删除的元素
return e;
}
// 时间复杂度是 O(n)
@Override
public boolean remove(Long e) {
// 按照从前到后的顺序,找到第一个遇到的 e
for (int i = 0; i < size; i++) {
// 毕竟 array 的值 是否 和 e 相等(equals)
if (array[i].equals(e)) {
// 删除 [i] 位置的元素
remove(i); // O(n)
return true;
}
}
// 说明没有找到 e
return false;
}
//时间复杂度是 O(1)
@Override
public Long get(int index) {// 下标合法性检查
if (index < 0 || index >= size) {
// 下标不合法
throw new ArrayIndexOutOfBoundsException("下标不合法");
}
return array[index];
}
// 时间复杂度是O(1)
@Override
public Long set(int index, Long e) {
// 下标合法性检查
if (index < 0 || index >= size) {
// 下标不合法
throw new ArrayIndexOutOfBoundsException("下标不合法");
}
Long tmp = array[index];
array[index] = e;
return tmp;
}
// 时间复杂度是O(n)
@Override
public int indexOf(Long e) {
for (int i = 0; i < size; i++) {
if(array[i].equals(e)){
return i;
}
}
return -1;
}
// 时间复杂度是O(n)
@Override
public int lastIndexOf(Long e) {
for (int i = size - 1; i >= 0; i--) {
if (array[i].equals(e)) {
return i;
}
}
return -1;
}
//时间复杂度是O(n)
@Override
public boolean contains(Long e) {
for (int i = 0; i < size; i++) {
if(array[i].equals(e)){
return true;
}
}
return false;
}
// 时间复杂度是O(n)
@Override
public boolean contains(Long e) {
return indexOf(e) != -1;
}
//时间复杂度是O(n)
@Override
public void clear() {
for (int i = 0; i
//时间复杂度是O(1)
@Override
public boolean isEmpty() {
return size == 0;
}
public class MyArrayList {
public static void main(String[] args) {
Listlist=new ArrayList<>();
list.add("小");
list.add("熊");
list.add("努");
list.add("力");
list.add("写");
list.add("代");
list.add("码");
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i));
}
}
}
public class MyArrayList {
public static void main(String[] args) {
Listlist=new ArrayList<>();
list.add("小");
list.add("熊");
list.add("努");
list.add("力");
list.add("写");
list.add("代");
list.add("码");
for (String str:
list) {
System.out.print(str);
}
}
}
public class MyArrayList {
public static void main(String[] args) {
Listlist=new ArrayList<>();
list.add("小");
list.add("熊");
list.add("努");
list.add("力");
list.add("写");
list.add("代");
list.add("码");
IteratorstringIterator=list.listIterator();
while(stringIterator.hasNext()){
System.out.print(stringIterator.next());
}
}
}
在Java中ArrayList是一个动态类型的顺序表,即在插入元素的过程中会自动扩容。
// 时间复杂度:数据规模是 size,时间复杂度是 O(1)
// 最坏情况,发生扩容的情况: O(n)
// 我们认为扩容的发生是小概率事件
// 平均事件复杂度:O(1)
private void ensureCapacity() {
// 此时不需要扩容
if (this.size < this.array.length) {
return;
}
// 需要扩容
// 1) 申请新数组,容量是原来的 2 倍,可以根据性能需要自定义
int newLength = this.array.length * 2;
Long[] newArray = new Long[newLength];
// 2) 将数据搬到新数组中
for (int i = 0; i < this.size; i++) {
newArray[i] = this.array[i];
}
// 3) 让顺序表的 array 引用指向新数组
this.array = newArray;
}
相应的add方法也要稍加修改:
public boolean add(Long e) { ensureCapacity(); array[size] = e; size = size + 1; return true; } public void add(int index, Long e) { ensureCapacity(); if (index < 0 || index > size) { throw new ArrayIndexOutOfBoundsException("下标不合法"); } for (int i = size - 1; i >= index; i--) { array[i + 1] = array[i]; } array[index] = e; size = size + 1; }
http://t.csdn.cn/XNIxK
http://t.csdn.cn/h9vZQ
http://t.csdn.cn/prEys