第一讲    集合入门

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的长度小于集合长度,则生成数组长度等于集合长度!