引入集合
集合和数组的比较
集合的使用场合:
举例:新闻列表、就业喜报、就业明星、邮件列表、购物车;当我们需要将一些相同结构的个体整合在一起时,就可以考虑使用集合了 。
为什么使用集合而不是数组?
集合和数组的相似点:都可以存储多个对象,对外都作为一个整体存在。
数组的缺点:
- 长度必须在初始化时指定,且固定不变。
- 数组采用连续存储空间,删除和添加效率低下。
- 数组无法直接保存映射关系。
- 数组缺乏封装,操作繁琐。
集合框架
Java集合框架提供了一套性能优良、使用方便的接口和类,它们位于java.util包中。存放在集合中的数据,被称为元素。
集合架构
- Collection接口存储一组不唯一、无序的对象。
- List接口存储一组不唯一,有序(索引顺序)的对象。
- Set接口存储一组唯一、无序的对象。
- Map接口存储一组键值对象,提供key到value的映射:
key:唯一、无序。
value:不唯一、无序。
List的主要实现类
- List
特点:有序、不唯一(可重复)。 - ArrayList
1.在内存中分配连续的空间,实现了长度可变的数组。
2.优点:遍历元素和随机访问元素的效率较高。
3.缺点:添加和删除需要大量移动元素,按照内容查询效率低。 - LinkedList
1.采用双向链表的存储方式。
2.缺点:遍历元素和随机访问元素的效率较低。
3.优点:插入、删除元素效率比较高(但是前提也是必须先低效率查询才可。如果插入删除发生在头尾可以减少查询次数)。
ArrayList
示例1:使用ArrayList存储多个学生的分数:
package com.bjsxt.list;
import java.util.ArrayList;
import java.util.Iterator;
public class TestArrayList1 {
public static void main(String[] args) {
//1.创建一个ArrayList对象,用来存储多个分数
ArrayList<Integer> list = new ArrayList();
//2.向ArrayList中添加分数
list.add(99);
list.add(92);
list.add(56);
list.add(89);
list.add(90);
//3.输出集合中有多少对象
System.out.println(list.size());
System.out.println(list);//[99, 92, 56, 89, 90]
System.out.println(list.toString());//[99, 92, 56, 89, 90]
// System.out.println(list.toArray());
// System.out.println(list.stream().toArray());
//4.在集合指定位置添加分数
list.add(2,88);
//5.删除指定位置的对象
list.remove(1);
System.out.println(list);
//普通for循环
System.out.println("---------------遍历1-----------------");
for (int i = 0; i < list.size(); i++) {
int elem = (int) list.get(i);
System.out.println(elem);
}
//使用增强for循环
System.out.println("---------------遍历2-----------------");
for (Object elem:list
) {
Integer i=(Integer) elem;
System.out.println(i);
}
//使用iterator迭代器
System.out.println("---------------遍历3-----------------");
Iterator iterator=list.iterator();
while (iterator.hasNext()) {
int elem= (int) iterator.next();
System.out.println(elem);
}
}
}
示例2:使用泛型保证集合操作的安全和简便
package com.bjsxt.list;
import java.util.ArrayList;
import java.util.Iterator;
/*
在集合中使用泛型
目前代码的缺点:
1.不安全 list.add("zhang");
2.繁琐,需要强制类型转换 int elem = (int) list.get(i);
解决:泛型
1.安全
2.不需要强制类型转换
3.JDK1.5提供
*/
public class TestArrayList2 {
public static void main(String[] args) {
//1.创建一个ArrayList对象,用来存储多个分数
ArrayList<Integer> list = new ArrayList<Integer>();
//2.向ArrayList中添加分数
list.add(99);
list.add(92);
list.add(56);
list.add(89);
list.add(90);
// list.add("zhang");
//3.输出集合中有多少对象
System.out.println(list.size());
System.out.println(list);//[99, 92, 56, 89, 90]
System.out.println(list.toString());//[99, 92, 56, 89, 90]
// System.out.println(list.toArray());
// System.out.println(list.stream().toArray());
//4.在集合指定位置添加分数
list.add(2,88);
//5.删除指定位置的对象
list.remove(1);
System.out.println(list);
//普通for循环
System.out.println("---------------遍历1-----------------");
for (int i = 0; i < list.size(); i++) {
int elem = list.get(i);
System.out.println(elem);
}
//使用增强for循环
System.out.println("---------------遍历2-----------------");
for (int elem:list
) {
Integer i=(Integer) elem;
System.out.println(i);
}
//使用iterator迭代器
System.out.println("---------------遍历3-----------------");
Iterator iterator=list.iterator();
while (iterator.hasNext()) {
int elem= (int) iterator.next();
System.out.println(elem);
}
}
}
示例3:ArrayList的更多方法
package com.bjsxt.list;
import java.util.ArrayList;
import java.util.Iterator;
/*
ArrayList的其他方法:
添加
list.add();
list.addAll(list2); 将list2中的所有元素添加到list的结尾
list.addAll(1,list2); 将list2中的所有元素添加到list的指定位置。
删除
list.remove(new Integer(99)); 按内容删除
list.remove(1); 按索引删除
list.removeAll(list2); 删除list中与list2的公共元素
list.retainAll(list2); 保留list和list2的公共元素
list.clear(); 清空
修改
list.set(1, 50);
查询
list.contains(92)
*/
public class TestArrayList3 {
public static void main(String[] args) {
//1.创建一个ArrayList对象,用来存储多个分数
ArrayList<Integer> list = new ArrayList<Integer>();
ArrayList<Integer> list1 = new ArrayList<Integer>();
list1.add(33);
list1.add(77);
list.addAll(list1);
//2.向ArrayList中添加分数
list.add(99);
list.add(92);
list.add(56);
list.add(89);
list.add(90);
// list.add("zhang");
//3.删除
list.remove(new Integer(99));//按内容删除
list.remove(1);//按索引删除
// list.clear(); //清空
//4.修改
list.set(1, 50);
//3.输出集合中有多少对象
System.out.println(list.size());
System.out.println(list);//[99, 92, 56, 89, 90]
System.out.println(list.toString());//[99, 92, 56, 89, 90]
// System.out.println(list.toArray());
// System.out.println(list.stream().toArray());
System.out.println(list.contains(92));
System.out.println(list.isEmpty());
}
}
理解ArrayList的源码
- ArrayList底层是一个长度可以动态增长的Object数组(StringBuilder底层是一个长度可以动态增长的char数组);
public class ArrayList<E> extends AbstractList<E> implements
List<E>, RandomAccess, Cloneable, Serializable{
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {
};
private static final Object[]
DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
};
transient Object[] elementData;
private int size;
}
-
接口是可以一个方法也不提供的,比如 RandomAccess, Cloneable, java.io.Serializable。
-
JDK1.7中,使用无参数构造方法创建ArrayList对象时,默认底层数组长度是10。JDK1.8中,使用无参数构造方法创建ArrayList对象时,默认底层数组长度是0;第一次添加元素,容量不足就要进行扩容了。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
- 容量不足时进行扩容,默认扩容50%。如果扩容50%还不足容纳新增元素,就扩容为能容纳新增元素的最小数量。
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//右移一位相当于除2
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
- ArrayList中提供了一个内部类Itr,实现了Iterator接口,实现对集合元素的遍历。
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
}