第一讲 集合入门
1. 集合的由来
① 集合出现以前的数组长度是固定的,当长度超过了数组的长度,就需要对数组进行重新定义,而集合能存储任意长度,且长度可以改变
② 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,需要对对象进行存储,集合就是存储对象最常用的一种方式。
而在此之前的数组只支持存储基本数据类型
2. 集合与数组的区别
区别1:数组的长度是固定的,而集合长度能随着元素的增加而增长
区别2:数组中可以存储基本数据类型,又可以存储引用数据类型,基本数据类型存储的是值,引用数据类型存储的是地址值
集合只能存储对象。这意外着集合在存储基本数据类型时,但是在存储的时候会自动装箱变成对象
3. 数组和集合什么时候用
1 如果元素个数是固定的推荐用数组
2如果元素个数不是固定的推荐用集合
4. 集合继承体系图
可以看到集合框架由四个接口组成,下面分别讲解
2. collection接口
Collection是集合框架中的常用接口。其下有两个子接口:List(列表),Set(集)。
所属关系:
Collection
|--List//元素是有序的,元素可以重复。因为该集合体系有索引。
|--Set//元素是无序的,元素不可以重复。无索引
注意:集合中实际上存储的对象的地址引用,所以基本数据类型在集合中会先被封装成对象,这叫做自动装箱
1. Collection接口中的常见操作
1. 添加元素
① boolean add(Object obj) 添加元素
添加一个元素,返回值为添加是否成功,因为List中允许存储重复值,所以永远返回true,在set中如果要添加的数在set中已经存在,则返回false
2. 删除元素
① boolean remove(Object obj) 删除指定的元素,如果为List接口,且有重复值,则移除第一个满足的值
② boolean clear(); 清空集合
3. 判断元素
boolean contains(Object obj); 判断是否存在obj这个元素
isEmpty() 判断集合是否为空
4. 获取个数,长度
size()
5. 带All的方法
① boolean addAll(Collection c) 把集合c中的所有元素添加到调用者集合中
注意:与add(Collection c)不同,add方法将c当作一个元素加到调用者集合中
② boolean removeAll(Collection c) 调用者集合中删除交集集合
③ boolean containsAll(Collection c) 判断调用集合是否包含传入的集合
④ boolean retainAll(Collection c) 调用者集合只保留两集合的共性元素,如果调用集合未改变返回false,调用集合改变就返回true
注:数组和集合中存储引用类型对象(对象)的都是存储地址值
2. Collection集合的遍历
① 转数组遍历,利用Collection.toArray()方法,返回值为object[]
Collection coll = new ArrayList();
coll.add(new Student("张三",23)); //Object obj = new Student("张三",23);
coll.add(new Student("李四",24));
coll.add(new Student("王五",25));
coll.add(new Student("赵六",26));
Object[] arr = coll.toArray(); //将集合转换成数组
for (int i = 0; i < arr.length; i++) {
Student s = (Student)arr[i]; //强转成Student
System.out.println(s.getName() + "," + s.getAge());
}
解析:上例中coll中存储的是虽然是student对象,但是在调用toArray()方法时返回的是object数组,所以对单个来说相当于
ayy[1] a = new Student("张三",23)也就是虽然存储的是student对象,但是已经被提升为object对象,父类对象不能使用子类特有的方法
因此要向下转型Student s = (Student)arr[i]; //强转成Student
② 迭代遍历(重要!!)
1. 概述
迭代是取出集合中元素的另一种方式。
我们知道Collection接口下Set和List的数据结构不同,所以取出的动作细节也不一样。但是都具有共性内容: 判断和取出。那么就可以将这些共性抽取。
这些规则的共性可以封装成Iterator。
每个Collection中有iterator方法,所以每一个子类集合对象都具备迭代器。Collection.iterator得到迭代器
2. iterator迭代器的常用方法
① boolean hasNext() 判断是否有下一个元素,如果有返回真
② Object next() 取出下一个元素
③ remove() 从迭代器指向的 collection 中移除迭代器返回的最后一个元素,执行完一次next()调用,且只能调用一次
3. 使用实例
Iteratoriter = a.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
4. 注意事项
① 迭代器的next方法返回值类型是Object,所以要记得类型转换。
② 迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException,所以要进行hasNext()判断
③ 迭代器在Collcection接口中是通用的,具体在各collection接口中由具体实现的iterator()
5. 原理分析
A:迭代器原理
* 迭代器原理:迭代器是对集合进行遍历,而每一个集合内部的存储结构都是不同的,所以每一个集合存和取都是不一样,那么就需要在每一个类中定义hasNext()和next()方法,这样做是可以的,但是会让整个集合体系过于臃肿,迭代器是将这样的方法向上抽取出接口,然后在每个类的内部,定义自己迭代方式,这样做的好处有二,第一规定了整个集合体系的遍历方式都是hasNext()和next()方法,第二,代码有底层内部实现,使用者不用管怎么实现的,会用即可
* B:迭代器源码解析
* 1,在eclipse中ctrl + shift + t找到ArrayList类
* 2,ctrl+o查找iterator()方法
* 3,查看返回值类型是new Itr(),说明Itr这个类实现Iterator接口
* 4,查找Itr这个内部类,发现重写了Iterator中的所有抽象方法
第三讲 List接口
1. 组成
List:元素是有序的,元素可以重复。因为该集合体系有索引。
List底层由两种底层实现方式:数组和链表
① 数组
查询快,修改也快
增删慢
② 链表
查询慢,修改也慢
增删快
|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。
|--LinkedList:底层使用的是链表数据结构。特点:增删速度很快,查询稍慢。
|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。
2. List特有的方法
①增
void add(index,element); //指定位置添加元素
boolean addAll(index,Collection); //在指定位置增加给定集合中的所有元素,若省略位置参数,则在当前集合的后面依次添加元素,集合变化则返回true
②删
E remove(int index); //删除指定位置的元素,返回被删除的元素
boolean remove(Object obj) // 删除集合中第一个出现的出现的指定元素(如果存在),如果列表包含指定的元素,则返回 true
③改
set(int index,element) //修改指定位置的元素
④查
E get(int index); //通过角标获取元素
List<E> subList(int from,int to); //获取部分对象元素
⑤其他
listIterator(); //List特有的迭代器
indexOf(obj); //获取元素第一次出现的位置,如果没有则返回-1
注意①: List的角标从0开始计算!!!!
注意②:List集合判断元素是否相同,移除等操作,依据的是元素的equals方法
3. List框架下并发修改产生的问题及修改
需求:我有一个集合,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现。
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("world");
list.add("d");
list.add("e");
/*Iterator it = list.iterator();
while(it.hasNext()) {
String str = (String)it.next();
if(str.equals("world")) {
list.add("javaee"); //这里会抛出ConcurrentModificationException并发修改异常
}
}*/
如果在list.iterator()得到集合的迭代器之后,迭代器已经默认了集合的size()为5,如果在迭代的过程中再添加一个元素
这个时候就会发生ConcurrentModificationException并发修改异常
解决方案:使用List特有的迭代器ListIterator,里面有特有方法ListIterator.add()
ListIterator lit = list.listIterator(); //如果想在遍历的过程中添加元素,可以用ListIterator中的add方法
while(lit.hasNext()) {
String str = (String)lit.next();
if(str.equals("world")) {
lit.add("javaee");
//list.add("javaee");
}
}
4. List框架下的ListIterator迭代器
1. 概述
ListIterator是List集合特有的迭代器,是Iterator的子接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。
所以在迭代器时,只能用迭代器的方法操作元素。可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作。如果想要其他的操作,如添加、修改等,就需要使用其子接口:ListIterrator。
该接口只能通过List集合的ListIterator方法获取。
2. 特有方法:
add(obj);//增加
set(obj);//修改为obj
hasPrevious();//判断前面有没有元素
previous();//取前一个元素
5. List框架下的第一个实现类 Vector
Vector是最早期出现在List框架下的实现类,但是由于功能不突出,已经逐渐被底层同为数组方式实现的ArryList所替代
1. Vector类特有功能
* public void addElement(E obj)
* public E elementAt(int index)
* public Enumeration elements() // 得到Vector特有的迭代方式—枚举
2. Vector特有的迭代方式—枚举
Vector v = new Vector(); //创建集合对象,List的子类
v.addElement("a");
v.addElement("b");
v.addElement("c");
v.addElement("d");
//Vector迭代
Enumeration en = v.elements(); //获取枚举
while(en.hasMoreElements()) { //判断集合中是否有元素
System.out.println(en.nextElement());//获取集合中的元素
}
6. List框架下的第二个实现类 ArrayList
① 案例演示 ArrayList中去除重复元素
ArrayList newList = new ArrayList(); //创建一个新集合
Iterator it = list.iterator(); //获取迭代器
while(it.hasNext()) { //判断老集合中是否有元素
String temp = (String)it.next(); //将每一个元素临时记录住
if(!newList.contains(temp)) { //如果新集合中不包含该元素
newList.add(temp); //将该元素添加到新集合中
}
return newList;
② 案例演示 ArrayList去除重复的自定义对象(该例中的Student对象)
1. 首先在ArrayList中添加Student对象,并去除
ArrayList c = new ArrayList<>();
c.add(new Student("张三","13"));
c.add(new Student("张三","13"));
c.add(new Student("李四","14"));
c.add(new Student("李四","14"));ArrayList a = getSingle(c);
Iterator it = a.iterator();
while(it.hasNext()) {
Student s = (Student) it.next();
System.out.println(s);
}
getSingle方法如下
public static ArrayList getSingle(ArrayList list) {
ArrayList newList = new ArrayList(); //创建一个新集合
Iterator it = list.iterator(); //获取迭代器
while(it.hasNext()) { //判断老集合中是否有元素
Student temp = (Student)it.next(); //将每一个元素临时记录住
if(!newList.contains(temp)) { //如果新集合中不包含该元素
newList.add(temp); //将该元素添加到新集合中
}
}
return newList; //将新集合返回
}
结果发现并没有去除,依然是四个元素
问题分析:
contains()方法源代码中使用的是equals方法判断是否相等,而student对象在集合中存储的是地址值,显示都是不一样
所以我们需要重写student类的equals方法才能达成目的
注意: 集合框架下的contains和remove方法判断都是传入的参数与this是否equals,如果是对象的话,因为存储的是
地址值所以一般需要重写equals方法
7. List框架下的第三个实现类 LinkedList
LinkedList:底层使用的是链表数据结构。特点:增删速度很快,查询稍慢。
① LinkedList特有的方法
1. 增加
public void addFirst(E e)及addLast(E e)
2. 删除
public E removeFirst()及public E removeLast() //获取元素,并删除元素。如果集合中没有元素,会出现NoSuchElementException
3. 获取
public E getFirst()及getLast() /获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException
public E get(int index)
注意:get(int index)底层源码依然是依据链表从头或从尾一个一个向下找
② 利用LinkList实现栈和队列
/**
* 栈数据结构,后进先出
* @author Administrator
*
*/
static class stack{
private LinkedList list = new LinkedList(); //创建LinkedList对象
/**
* 进栈
* @param obj
*/
public void in(Object obj) {
list.addLast(obj);
}
/**
* 出栈
* @return
*/
public Object out() {
return list.removeLast();
}
public boolean isEmpty() {
return list.isEmpty();
}
}
8. List的三个子类的特点
ArrayList:
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高。
Vector:
底层数据结构是数组,查询快,增删慢。
线程安全,效率低。
LinkedList:
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高。
Vector相对ArrayList查询慢(线程安全的)
Vector相对LinkedList增删慢(数组结构)
考点1:Vector和ArrayList的联系
区别: Vector是线程安全的,效率低,ArrayList是线程不安全的,效率高
共同点:都是数组实现的
考点2: ArrayList和LinkedList的联系
区别: ArrayList底层是数组结果,查询和修改快
LinkedList底层是链表结构的,增和删比较快,查询和修改比较慢
公共点:都是线程不安全
② Collection的三个儿子都很有特点,那么怎么用呢
查询多用ArrayList
增删多用LinkedList
如果都多用ArrayList
为什么不用Vector?速度慢,而且有工具类可以使其他儿子也变得线程安全
第四讲 JDK1.5新特性
JDK1.5后的新特性对数组和集合提供了一些有利的帮助,所以有必要好好学一学
1. 增加for循环
①. JDK1.5以后的新特性,用于简化数组和Collection集合的遍历
②. 格式
for(元素数据类型 变量 : 数组或者Collection集合) {
使用变量即可,该变量就是元素
}
③ 利用增强for循环实现ArrayList的遍历
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for(String s : list) {
System.out.println(s);
}
增强for循环底层依赖的是迭代器Iterator
④ 三种迭代方式对ArrayList的删除方式
① 普通for循环删除
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");for(int i=0;i<list.size();i++){
if("b".equals(list.get(i))){
list.remove(i--);
}
}
由于ArrayList底层数据结构是数组,所以删除之后后面的元素默认向前移动一位,所以删除之后下标i要减去1,否则会出错
② 迭代器删除
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");for(Iterator<String> it = list.Iterator;it.hasNext();){
if(b.equals(it.next())){
it.remove; // 不能用list的remove,会发生并发修改异常
}
}
③ 增强for循环
因为增强for循环底层采用迭代器遍历,又无法取得迭代器对象,所以不能删除,只能进行遍历
2. 静态导入
通过导入类中的静态方法名,进而可以直接使用类中静态方法
没有什么实用价值
3. 可变参数
① 概述
定义方法的时候不知道该定义几个参数,可以使用可变参数,可变参数是一个数组哦@
② 格式
public void 方法名(数据类型... 变量名)
③ 注意事项
1. 这里的变量是一个数组
2. 一个可变参数必须在所有参数的最后一个
4. 集合与数组的相互转化
① 数组转集合
利用Arrays类中的静态方法 Arrays.asList(array a[])
好处:不能增加和删除元素,但是可以利用集合的思想,也就是说可以调用集合中的其他方法
注意:当数组中的元素为基本数据类型时,调用asList方法时,由于集合中存储的引用数据类型,也就是一个地址值所以会将整个数组
当作集合的一个元素,这个时候转化成的集合就只有一个元素。
解决方法时:别把数组中元素定义成int基本数据类型,而是定义成包装类Integer引用数据类型
实例:
int[] a = {11,12};
List l = Arrays.asList(a);
这两句话会将a数组转化为List中的一个元素
而 Integer[] a = {11,12} 这样定义就能完美解决
② 集合转数组
Collection中toArray(T[] a) 泛型版的集合转数组
如果T的长度小于集合长度,则生成数组长度等于集合长度!