集合框架
在数组的使用过程中可以看到,想要向数组中插入元素和删除元素非常麻烦,而且数组的长度是无法改变的。java为我们提供了批量存储数据更加方便的容器,就是集合。集合和数组的作用一样,都是为了使用一个变量来存储一批数据的;但集合使用起来更加方便,而且集合的长度是可以变化的。
List接口
List集合可以存储有序的,可重复的数据;常用的子类是ArrayList和LinkedList两个类
ArrayList类
这是一个底层由数组实现的集合类,是对数组进行了封装。实例:package collection.list.arrayList;
import java.util.ArrayList;
/**
* ArrayListDemo类
* 演示ArrayList类中常用方法的使用
* @author 学霸联盟 - 赵灿
*/
public class ArrayListDemo {
public static void main(String[] args) {
//当创建ArrayList对象时,底层会创建一个空的Object类型的数组
ArrayList arrayList = new ArrayList();
//循环存入数据
for (int element = 1; element <= 11; element++) {
/*
* 通过arrayList的add方法向集合中添加数据
* 任何类型的数据都会被转换成Object类型加入集合
* 当加入集合的数据为基本数据类型的数据时
* 会先将基本数据类型的数据转换成对应的包装类类型
* 例如:int类型的数据会先转换成Integer类型
* 然后再将Integer类型转换成Object类型
* 从基本数据类型转换成包装类类型的过程叫做自动装箱
* 这个转换过程由系统自动处理完成
*
* 添加第一个数据时,底层会重新创建长度为10的Object类型的数组
* 这里的10一般认为是ArrayList集合的初始长度
* 并把第一个数据存储到底层Object类型数组下标为0的位置上;
* 当底层数组存满后,再向集合中添加元素时,
* 底层会创建一个长度为原数组长度1.5倍的新数组
* 这里的1.5叫做集合的增长因子
* 并将原数组中的所有元素复制到新数组中
* 然后在新数组下标为原数组长度的位置上新增当前元素
*
* 例如:本例中第一次增长在存入第11个元素时,即element==11时
* 此时原数组的长度为10(最大下标为9)
* 底层会新建长度为15(10的1.5倍)的Object类型的数组
* 然后将原数组中所有数据复制到新数组中
* 然后在新数组下标为10的位置上存入元素11
* 注意:这里的element不是集合的下标,而是存入集合的元素(数据)
* 理论最大存储元素个数为int类型的最大值2147483647 [0x7fffffff]
*/
arrayList.add(element);
}
/*
* 循环取出数据
* 这里的index代表集合的下标
* 集合的长度使用size方法获取,而不是length属性,也不是length方法
* 获取数组长度使用的length属性
* 获取String长度使用的是length方法
*/
for (int index = 0; index < arrayList.size(); index++) {
// 使用get方法获取集合中的数据,获取出来的数据类型为Object
Object element = arrayList.get(index);
// 强制类型转换
Integer data = (Integer)element;
/*
* Integer是引用类型
* 那么问题来了,基本数据类型的变量e为什么可以赋引用类型的值呢?
* 因为Integer是int的包装类
* 从包装类型转换成基本数据类型的过程,叫做自动拆箱
* 这个转换过程也由系统自动处理完成
*/
int e = data;
//每次循环均输出变量e的值和两个空格
System.out.print(e + " ");
}
}
}
运行结果:1 2 3 4 5 6 7 8 9 10 11
小结:
1、集合的初始长度为10,增长因子为1.52、理论最大存储长度为int类型的最大值2147483647 [0x7fffffff]
3、存入基本数据类型数据时存在自动装箱
4、未使用泛型时,取出数据的数据类型是Object
5、获取集合长度使用size方法
6、通过结果可以看到,取出的顺序和存入的顺序是一致的,所以它是有序的集合
7、底层由数组实现,其内存空间是连续的,所以查询元素速度较快
8、插入和删除速度较慢,因为要将插入位置之后的元素依次后移,删除位置之后的元素依次前移
实例:演示ArrayList类中的常用方法的使用package collection.list.arrayList;
import java.util.ArrayList;
import java.util.Arrays;
/**
* ArrayListDemo类
* 演示ArrayList类中常用方法的使用
* @author 学霸联盟 - 赵灿
*/
public class ArrayListDemo2 {
public static void main(String[] args) {
//创建ArrayList对象
ArrayList arrayList1 = new ArrayList();
ArrayList arrayList2 = new ArrayList();
//循环存入数据
for (int element = 1; element <= 3; element++) {
//循环结束后两个集合中都有1,2,3三个元素
arrayList1.add(element);
arrayList2.add(element);
}
//遍历集合;运行结果:1 2 3
ergodicArrayList(arrayList1);
/*
* 插入数据,向集合下标为1的位置,插入数据10
* 插入的下标值必须小于等于集合长度
* 即:插入下标<=集合.size();
* 如果插入下标大于集合长度的位置,会出现IndexOutOfBoundsException
*/
arrayList1.add(1,10);
//遍历插入数据后的集合;运行结果:1 10 2 3
ergodicArrayList(arrayList1);
//依次向集合arrayList1中加入arrayList2集合中的每个元素
arrayList1.addAll(arrayList2);
//遍历插入数据后的集合;运行结果:1 10 2 3 1 2 3
ergodicArrayList(arrayList1);
//contains方法,判断集合中是否包含某个元素
boolean isContains = arrayList1.contains(10);
//输出结果:集合arrayList1是否包含10:true
System.out.println("集合arrayList1是否包含10:" + isContains);
//输出结果:集合arrayList1是否包含11:false
System.out.println("集合arrayList1是否包含11:" + arrayList1.contains(11));
//indexOf方法,获取某个元素在集合中左边开始第一次出现时的下标
//输出结果:集合arrayList1中第一次出现2的下标为:2
System.out.println("集合arrayList1中第一次出现2的下标为:" + arrayList1.indexOf(2));
//lastIndexOf方法,获取某个元素在集合中最后一次(右边第一次)出现时的下标
//输出结果:集合arrayList1中最后一次出现2的下标为:5
System.out.println("集合arrayList1中最后一次出现2的下标为:" + arrayList1.lastIndexOf(2));
//remove方法,删除对应下标的元素
//如果传入的实参类型是byte、short、int和char类型时,根据下标移除
arrayList1.remove(0);
//运行结果:10 2 3 1 2 3
ergodicArrayList(arrayList1);
//移除arrayList1集合中的元素3,只会移除左边第一个匹配上的数据
arrayList1.remove((Integer)3);
//运行结果:10 2 1 2 3
ergodicArrayList(arrayList1);
//set方法,更新对应下标的元素
arrayList1.set(3,1000);
//运行结果:10 2 1 1000 3
ergodicArrayList(arrayList1);
//toArray方法,将集合转换成Object类型的数组
Object[] array = arrayList1.toArray();
//将数组转换成字符串形式
String str = Arrays.toString(array);
//运行结果:[10, 2, 1, 1000, 3]
System.out.println(str);
//清空集合中的所有元素
arrayList1.clear();
//isEmpty方法,判断集合是否为空,当且仅当size方法获得结果为0时,返回true
//运行结果:集合arrayList1是否为空:true
System.out.println("集合arrayList1是否为空:" + arrayList1.isEmpty());
}
/**
* 遍历集合
*/
private static void ergodicArrayList(ArrayList arrayList){
//循环取出元素
for (int index = 0; index < arrayList.size(); index++) {
// 使用get方法获取集合中的数据,获取出来的数据类型为Object
Object element = arrayList.get(index);
/*
* 当使用System.out.println()方法直接输出引用类型的对象时
* 输出的是对象.toString()方法的返回值
* 例如:
* System.out.println(element);和
* System.out.println(element.toString());是一样的
*/
System.out.print(element);
//输出两个空格
System.out.print(" ");
}
//换行
System.out.print("\n");
}
}
运行结果:
1 2 3
1 10 2 3
1 10 2 3 1 2 3
集合arrayList1是否包含10:true
集合arrayList1是否包含11:false
集合arrayList1中第一次出现2的下标为:2
集合arrayList1中最后一次出现2的下标为:5
10 2 3 1 2 3
10 2 1 2 3
10 2 1 1000 3
[10, 2, 1, 1000, 3]
集合arrayList1是否为空:true
另有Vector集合类,和ArrayList的区别:ArrayList是线程异步的(线程不安全的),Vector是线程同步的(线程安全的),因此导致Vector集合的性能非常低下,已基本不用,有兴趣的同学可以了解即可
既然ArrayList集合插入和删除效率较低,那么有没有比它更快的呢?
接下来看LinkedList集合类
LinkedList类
和ArrayList类基本相同,ArrayList有的方法LinkedList都有;但他们的实现方式不同,LinkedList采用双向链表的数据结构实现,当向集合中添加元素时,集合会创建一个节点Node,这个节点中会存储当前添加的元素,还会存储上一个节点和下一个节点,这样就形成了双向链表这一数据结构。而这样的结构修改和删除效率非常高效,但这个结构中节点与节点之间,在内存空间上是不连续的;所以查询效率要比ArrayList集合的查询效率低
实例:演示LinkedList部分特有方法package collection.list.linkedList;
import java.util.LinkedList;
/**
* LinkedListDemo类
* 演示LinkedListDemo类中常用方法的使用
* @author 学霸联盟 - 赵灿
*/
public class LinkedListDemo {
public static void main(String[] args) {
//创建LinkedList对象
LinkedList linkedList = new LinkedList();
for (int i = 0; i < 4; i++) {
//循环向集合中添加数据
linkedList.add(i+1);
}
//运行结果:1 2 3 4
ergodicLinkedList(linkedList);
//获取集合中第一个位置的元素
Object first = linkedList.getFirst();
//运行结果:集合中第一个元素为:1
System.out.println("集合中第一个元素为:" + first);
//获取集合中最后一个位置的元素
Object last = linkedList.getLast();
//运行结果:集合中最后一个元素为:4
System.out.println("集合中最后一个元素为:" + last);
//在集合的第一个位置插入元素
linkedList.addFirst(0);
//在集合的最后一个位置插入元素
linkedList.addLast(6);
//运行结果:0 1 2 3 4 6
ergodicLinkedList(linkedList);
//移除集合中第一个位置的元素
linkedList.removeFirst();
//移除集合中最后一个位置的元素
linkedList.removeLast();
//运行结果:1 2 3 4
ergodicLinkedList(linkedList);
}
/**
* 遍历集合
*/
private static void ergodicLinkedList(LinkedList linkedList){
//循环取出元素
for (int index = 0; index < linkedList.size(); index++) {
// 使用get方法获取集合中的数据,获取出来的数据类型为Object
Object element = linkedList.get(index);
/*
* 当使用System.out.println()方法直接输出引用类型的对象时
* 输出的是对象.toString()方法的返回值
* 例如:
* System.out.println(element);和
* System.out.println(element.toString());是一样的
*/
System.out.print(element);
//输出两个空格
System.out.print(" ");
}
//换行
System.out.print("\n");
}
}
运行结果:
1 2 3 4
集合中第一个元素为:1
集合中最后一个元素为:4
0 1 2 3 4 6
1 2 3 4
拓展阅读: 《I学霸官方免费教程三十六:Java数据结构之双向链表结构》
总结:
以上我们提到的两种集合都是继承自List接口的,他们具有共同的特性,存入的值都是有顺序的,可以存入重复的值,可以使用下标访问元素不同点
ArrayList:底层由数组实现,初始长度为10,增长因子1.5
缺点:插入和删除效率较低
优点:修改和查询效率较高
LinkedList:底层由双向链表实现,没有初始长度和增长因子
优点:插入和删除效率较高
缺点:查询和修改效率较低