视频链接:https://www.bilibili.com/video/BV1Rx411876f?p=1
视频范围P660 - P693
 
目录描述
- 1.集合概述
- 2.Collection和terator
- 3.List接口
- 4.泛型机制
- 5.foreach
1.集合概述
- 数组其实就是一个集合
- 集合实际上就是一个容器,是一个载体,可以来容纳其它类型的数据(可以一次容纳多个对象)
 举例:在实际开发中,假设连接数据库,数据库当中有10条记录,那么假设把这10条记录查询出来,在java程序中会将10条数据封装成10个java对象,然后将10个java对象放到某一个集合当中,将集合传到前端,然后遍历集合,将一个数据一个数据展现出来。
- 集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,集合当中存储的都是java对象的内存地址(或者说集合中存储的是引用)
list.add(100);//自动装箱Integer
//注意:集合在java中本身是一个容器,是一个对象
//集合中任何时候存储的都是“引用”

 4. 在java中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构(数据存储的结构)当中。
 注意:不同的数据结构,数据存储的方式不同,例如:数组、二叉树、链表、哈希表等等
//使用不同的集合等于使用了不同的数据结构
new ArrayList();//创建一个集合,底层是数组
new LinkedList();//创建一个集合,底层是链表
new TreeSet();//创建一个集合,底层是二叉树
- 集合在java JDK中java.util.;包下【所有的集合类和集合接口都在java.util.;包下】
- java中集合分为两大类:
| 集合 | 备注 | 
|---|---|
| 单个方式存储元素 | 这一类集合中超级父接口: java.util.Collection; | 
| 以键值对的方式存储元素 | 这一类集合中超级父接口: java.util.Map; | 
- 集合的继承结构图
  
  
- Map集合的继承结构图
  
- 总结
| 实现类 | 备注 | 
|---|---|
| ArrayList | 底层是数组 | 
| LinkedList | 底层是双向链表 | 
| Vector | 底层是数组,线程安全的,效率较低,使用较少 | 
| HashSet | 底层是HashMap,放到HashSet集合中的元素等同于放到HashMap集合key部分了 | 
| TreeSet | 底层是TreeMap,放到TreeSet集合中的元素等同于放到TreeMap集合key部分了 | 
| HashMap | 底层是哈希表 | 
| Hashtable | 底层也是哈希表,只不过线程安全的,效率较低,使用较少 | 
| Properties | 是线程安全的,并且key和value只能存储字符串String | 
| TreeMap | 底层是二叉树,TreeMap集合的key可以自动按照大小顺序排序 | 
List集合存储元素的特点:
- 有序可重复
- 有序:存进去的顺序和取出的顺序相同,每一个元素都有下标
- 可重复:存进去1,可以再存储一个1
Set(Map)集合存储元素的特点:
- 无序不可重复
- 无序:存进去的顺序和取出的顺序不一定相同,另外Set集合中元素没有下标
- 不可重复:存进去1,不能再存储1了
SortedSet(SortedMap)集合存储元素特点:
- 首先是无序不可重复的,但是SortedSet集合中的元素是可排序的
- 无序:存进去的顺序和取出的顺序不一定相同,另外Set集合中元素没有下标
- 不可重复,存进去1,不能再存储1了
- 可排序:可以按照大小顺序排列
注意: Map集合的key,就是一个Set集合,往Set集合中放数据,实际上放到了Map集合的key部分
2.Collection和terator
2.1 Collection接口中常用的方法
- 没有使用“泛型”之前Collection中可以存储Object的所有子类型
- 使用“泛型”之后Collection中只能存储O某个具体的类型
| 方法 | 作用 | 
|---|---|
| boolean add(Object e) | 向集合中添加元素 | 
| int size() | 获取集合中元素的个数 | 
| void clear() | 清空集合 | 
| boolean contains(Object o) | 判断当前集合中是否包含元素o,包含返回true,不包含返回false | 
| boolean remove(Object o) | 删除集合中的某个元素 | 
| boolean isEmpty() | 判断该集合中元素的个数是否为0 | 
| Object[ ] toArray() | 把集合转换为数组 | 
package Collectice;
import java.util.ArrayList;
import java.util.Collection;
public class CollecticeTest01 {
    public static void main(String[] args) {
        //创建一个集合对象
        //Collection c = new Collection;//接口是抽象的,无法实例化
        //多态
        Collection c = new ArrayList();
        //测试Collection接口中的常用方法
        c.add(1200);//自动装箱(java5的新特性),实际上是放进去了一个对象的内存地址
                    //如:Integer x = new Integer(1200);
        c.add(3.14);//自动装箱
        c.add(new Object());
        c.add(new Student());
        c.add(true);//自动装箱
        //获取集合中元素的个数
        System.out.println("集合中元素个数是:" + c.size());//输出为:集合中元素个数是:5
        //清空集合
        c.clear();
        System.out.println("集合中元素个数是:" + c.size());//输出为:集合中元素个数是:0
        //再向集合中添加元素
        c.add("hello");
        c.add("world");
        c.add("浩克");
        c.add("绿巨人");
        //判断集合中是否包含 绿巨人
        boolean flag = c.contains("绿巨人");
        System.out.println(flag);//输出为:true
        //删除集合中某个元素
        c.remove("绿巨人");
        System.out.println("集合中元素个数是:" + c.size());//输出为:集合中元素个数是:3
        //判断集合中是否为空(集合中是否存在元素)
        System.out.println(c.isEmpty());//输出为:false
        //转换成数组
        Object[] objs = c.toArray();
        for (int i = 0; i < objs.length; i++) {
            
            //遍历数组
            Object o = objs[i];
            System.out.println(o);
        }
    }
}
class  Student{
}
2.2 集合遍历/迭代专题一
以下讲解的遍历方式/迭代方式,是所有Collection通用的一种方式,在Map集合中不能用,在所有的Collection以及子类中使用
package Collectice;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollecticeTest02 {
    public static void main(String[] args) {
        //创建集合对象
        //后面的集合无所谓,主要看前面的Collection接口,怎么遍历/迭代
        Collection c = new ArrayList(); 
        //添加元素
        c.add("abc");
        c.add("def");
        c.add(100);
        c.add(new Object());
        //对集合Collection遍历/迭代
        //第一步:获取集合对象的迭代器对象Iterator
        Iterator it = c.iterator();
        //第二步:通过以上获取的迭代器对象开始迭代/遍历集合
        /*
        以下两个方法是迭代器对Iterator中的方法:
        boolean hasNext()如果仍有元素可以迭代,则返回true
        Object next()返回迭代的下一个元素
         */
        while (it.hasNext()){
            Object obj = it.next();
            System.out.println(obj);
        }
    }
}
运行结果:

 内存分析图:

2.3 集合遍历/迭代专题二
package Collectice;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class CollecticeTest03 {
    public static void main(String[] args) {
        //创建集合对象
        Collection c1 = new ArrayList();//ArrayList集合:有序可重复
        //添加元素
        c1.add(1);
        c1.add(2);
        c1.add(3);
        c1.add(4);
        c1.add(5);
        //迭代集合
        Iterator it = c1.iterator();
        while (it.hasNext()){
            //存进去是什么类型,取出来还是什么类型
            Object obj = it.next();
            if(obj instanceof Integer){
                System.out.println("Integer类型");
            }
            //只不过在输出的时候会转换成字符串,因为这里的println会调用toString()方法
            System.out.println(obj);
        }
        //HashSet集合:无序不可重复
        //无序:存进去和取出的顺序不一定相同
        //不可重复:存储100,不能再存储100
        Collection c2 = new HashSet();
        c2.add(100);
        c2.add(200);
        c2.add(300);
        c2.add(100);
        Iterator it2 = c2.iterator();
        while (it2.hasNext()){
            System.out.println(it2.next());
        }
    }
}
运行结果:

2.4 contains方法解析
boolean contains(Object o) :判断当前集合中是否包含元素o,包含返回true,不包含返回false
contains方法是用来判断集合中是否包含某个元素的方法,底层判断集合中是否包含某个元素:
- 调用equals方法进行比对;
- equals方法返回true,就表示包含这个元素
,代码实例一:
package Collectice;
import java.util.ArrayList;
import java.util.Collection;
public class CollecticeTest04 {
    public static void main(String[] args) {
        //创建集合对象
        Collection c = new ArrayList();
        //向集合中存储元素
        String s1 = new String("abc");
        c.add(s1);
        String s2 = new String("def");
        c.add(s2);
        //集合中元素的个数
        System.out.println("元素的个数为:" + c.size());//输出为:元素的个数为:2
        //新建的对象String
        String x = new String("abc");
        //c集合中是否x?
        System.out.println(c.contains(x));//输出为:true
    }
}
内存分析图:

 ,代码实例二:
 存放在一个集合中的类型,一定要重写equals方法
package Collectice;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Objects;
public class CollecticeTest05 {
    public static void main(String[] args) {
        //创建集合对象
        Collection c = new ArrayList();
        //创建用户对象
        User u1 = new User("jack");
        //加入集合
        c.add(u1);
        //判断集合中是否包含u2
        User u2 = new User("jack");
        //没有重写equals之前:这个结果是false
        //System.out.println(c.contains(u2));//输出为:false
        //重写equals之后:这个结果是true
        System.out.println(c.contains(u2));//输出为:true
    }
}
class User{
    private String name;
    public User() {
    }
    public User(String name) {
        this.name = name;
    }
    //重写equals方法
    //将来调用equals方法的时候,一定是调用这个重写的equals方法
    public boolean equals(Object o) {
        if (o == null || !(o instanceof User)) return  false;
        if (o == this) return true;
        User u = (User)o;
        //如果名字一样表示同一个人(不再比较对象的内存地址了,比较内容)
        return u.name.equals(this.name);
    }
}
2.5 remove方法解析
代码实例一:
package Collectice;
import java.util.ArrayList;
import java.util.Collection;
public class CollecticeTest06 {
    public static void main(String[] args) {
        //创建集合对象
        Collection cc = new ArrayList();
        //创建字符串对象
        String s1 = new String("hello");
        //加进去
        cc.add(s1);
        //创建一个新的字符串对象
        String s2 = new String("hello");
        //删除s2
        cc.remove(s2);
        //集合中元素个数
        System.out.println(cc.size());//输出为:0
    }
}
代码实例二:
package Collectice;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollecticeTest07 {
    public static void main(String[] args) {
        //创建集合
        Collection c = new ArrayList();
        //添加元素
        c.add(1); //Integer类型
        c.add(2);
        c.add(3);
        //获取迭代器
        Iterator it = c.iterator();
        while (it.hasNext()){
            //编写代码时next()方法返回值类型必须是object
            Object obj = it.next();
            System.out.println(obj);
        }
    }
}
运行结果:

 代码实例三:
 重点:当集合的结构发生改变时,迭代器必须重新获取,如果还是用以前老的迭代器,会出现异常:java.util.ConcurrentModificationException
package Collectice;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollecticeTest07 {
    public static void main(String[] args) {
        //创建集合
        Collection c = new ArrayList();
        //注意:此时获取的迭代器,指向的是那是集合中没有元素状态下的迭代器
        //一定要注意:集合结构只要发生改变,迭代器必须重新获取
        //当集合结构发生了改变,迭代器没有重新获取时
        //调用next()方法时:java.util.ConcurrentModificationException
        Iterator it = c.iterator();
        //添加元素
        c.add(1); //Integer类型
        c.add(2);
        c.add(3);
        //获取迭代器
        //Iterator it = c.iterator();
        while (it.hasNext()){
            //编写代码时next()方法返回值类型必须是object
            Object obj = it.next();
            System.out.println(obj);
        }
    }
}
运行结果:

 代码实例四:
 重点:在迭代集合元素的过程中,不能调用集合对象的remove方法,删除元素会出现:java.util.ConcurrentModificationException
package Collectice;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollecticeTest07 {
    public static void main(String[] args) {
        //创建集合
        Collection c = new ArrayList();
        //添加元素
        c.add(1); //Integer类型
        c.add(2);
        c.add(3);
        //获取迭代器
        Iterator it = c.iterator();
        while (it.hasNext()){
            Object obj = it.next();
            //删除元素
            //删除元素之后,集合的结构发生了变化,应该重新去获取迭代器
            //但是,循环下一次的时候并没有重新获取迭代器
            //所以会出现异常:java.util.ConcurrentModificationException
            //出异常根本原因:集合中元素删除了,但是没有更新迭代器(迭代器不知道集合变化了)
            //直接通过集合去删除元素,没有通知迭代器(导致迭代器的快照和原集合快照不同)
            c.remove(obj);
            System.out.println(obj);
        }
    }
}
运行结果:

 代码实例五:
 重点:在迭代元素的过程当中,一定要使用迭代器Iterator的remove方法,删除元素,不要使用集合自带的remove方法删除元素
package Collectice;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollecticeTest07 {
    public static void main(String[] args) {
        //创建集合
        Collection c = new ArrayList();
        //添加元素
        c.add(1); //Integer类型
        c.add(2);
        c.add(3);
        //获取迭代器
        Iterator it = c.iterator();
        while (it.hasNext()){
            Object obj = it.next();
            //使用迭代器删除,删除的一定是迭代器指向的当前元素
            //迭代器去删除时,会自动更新迭代器,并且更新集合(删除集合中的元素)
            it.remove();
            System.out.println(obj);
        }
        System.out.println(c.size());//输出为:0
    }
}
运行结果:

3.List接口
3.1 List接口方法
- List集合存储元素特点:有序可重复
 有序:List集合中的元素有下标。从0开始,以1递增
 可重复:存储一个1,还可以再存储1
- List接口自己特色方法:
| 特色方法 | 
|---|
| void add(int index,Object element) | 
| Object get(int index) | 
| int indexOf(Object o) | 
| int LastIndexOf(Object o) | 
| Object remove(int index) | 
| Object set(int index ,Object element) | 
3.1.1 void add(int index,Object element)
这个方法使用不多,因为对于ArrayList集合来说效率比较低
package Collectice;
import java.util.*;
public class ListTest01 {
    public static void main(String[] args) {
        //创建list类型的集合
        //List mylist = new LinkedList();
        //List mylist = new Vector();
        List mylist = new ArrayList();
        //添加元素
        mylist.add("A");//默认都向集合末尾添加元素
        mylist.add("B");
        mylist.add("C");
        mylist.add("D");
        //在列表的指定位置插入指定元素(第一个参数是下标)
        //这个方法使用不多,因为对于ArrayList集合来说效率比较低
        mylist.add(1,"king");
        //迭代
        Iterator it = mylist.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
    }
}
3.1.2 Object get(int index)
根据下标获取元素
package Collectice;
import java.util.*;
public class ListTest01 {
    public static void main(String[] args) {
        //创建list类型的集合
        //List mylist = new LinkedList();
        //List mylist = new Vector();
        List mylist = new ArrayList();
        //添加元素
        mylist.add("A");
        mylist.add("B");
        mylist.add("C");
        mylist.add("D");
        //根据下标获取元素
        Object firstObj = mylist.get(0);
        System.out.println(firstObj);//输出为:A
        //因为有下标,所以list集合有自己比较特殊的遍历方法
        //通过下标遍历【list集合特有的方式,Set没有】
        for (int i = 0; i < mylist.size(); i++) {
            Object obj = mylist.get(i);
            System.out.println(obj);
        }
    }
}
3.1.3 int indexOf(Object o)
获取指定对象第一次出现处的索引
package Collectice;
import java.util.*;
public class ListTest01 {
    public static void main(String[] args) {
        //创建list类型的集合
        //List mylist = new LinkedList();
        //List mylist = new Vector();
        List mylist = new ArrayList();
        //添加元素
        mylist.add("A");
        mylist.add("B");
        mylist.add("C");
        mylist.add("D");
        //获取指定对象第一次出现处的索引
        System.out.println(mylist.indexOf("B"));//输出为:1
    }
}
3.1.4 int LastIndexOf(Object o)
获取指定对象最后一次出现处的索引
package Collectice;
import java.util.*;
public class ListTest01 {
    public static void main(String[] args) {
        //创建list类型的集合
        //List mylist = new LinkedList();
        //List mylist = new Vector();
        List mylist = new ArrayList();
        //添加元素
        mylist.add("A");
        mylist.add("B");
        mylist.add("C");
        mylist.add("C");
        mylist.add("D");
        //获取指定对象最后一次出现处的索引
        System.out.println(mylist.lastIndexOf("C"));//输出为:3
    }
}
3.1.5 Object remove(int index)
删除指定下标位置的元素
package Collectice;
import java.util.*;
public class ListTest01 {
    public static void main(String[] args) {
        //创建list类型的集合
        //List mylist = new LinkedList();
        //List mylist = new Vector();
        List mylist = new ArrayList();
        //添加元素
        mylist.add("A");
        mylist.add("B");
        mylist.add("C");
        mylist.add("D");
        //删除指定下标位置的元素
        //删除下标为0的元素
        mylist.remove(0);
        System.out.println(mylist.get(0));//输出为:B
    }
}
3.1.6 Object set(int index ,Object element)
package Collectice;
import java.util.*;
public class ListTest01 {
    public static void main(String[] args) {
        //创建list类型的集合
        //List mylist = new LinkedList();
        //List mylist = new Vector();
        List mylist = new ArrayList();
        //添加元素
        mylist.add("A");
        mylist.add("B");
        mylist.add("C");
        mylist.add("D");
        //修改指定位置的元素
        mylist.set(0,"F");
        System.out.println(mylist.get(0));//输出为:F
    }
}
3.2 ArrayList
- ArrayList集合初始化容量是10【底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量10】
- ArrayList集合底层是Obeject类型的数组Object[ ]
- 构造方法:new ArrayList()或者new ArrayList(20)
package Collectice;
import java.util.ArrayList;
import java.util.List;
public class ArrayListTest01 {
    public static void main(String[] args) {
        //默认初始化容量是10
        //数组的长度是10
        List list1 = new ArrayList();
        //集合的size()方法是获取当前集合中元素的个数,不是获取集合的容量
        System.out.println(list1.size());//输出为:0
        //默认初始化容量是20
        //数组的长度是20
        List list2 = new ArrayList(20);
        System.out.println(list2.size());//输出为:0
    }
}
- ArrayList集合的扩容:增长到原容量的1.5倍
 ArrayList集合底层是数组
 优化方式:尽可能少的扩容,因为数组扩容效率比较低,建议在使用ArrayList集合的时候预估计元素的个数,给定一个初始化容量
- 数组优点:检索效率比较高【每个元素占用空间大小相同,内存地址是连续的,知道首元素内存地址,然后知道下标,通过数学表达式计算出元素的内存地址,所以检索效率最高。】;向数组末尾添加元素,效率很高,不受影响
 数组缺点:随机增删元素效率比较低;数组无法存储大数据量【很难找到一块非常巨大的连续的内存空间】
- 面试问题:这么多的集合中,哪个集合用的最多?
 答:ArrayList集合,因为往数组末尾添加元素,效率不受影响,另外,检索/查找某个元素的操作比较多
- ArrayList集合是非线程安全的(不是线程安全的集合)
3.2.1 位运算符
package Collectice;
public class BinaryTest {
    public static void main(String[] args) {
        // >> 1 二进制右移1位
        // >> 2 二进制右移2位
        //10的二进制位是:00001010 【10】
        //10的二进制右移1位是:00000101 【5】
        System.out.println(10 >> 1);//输出为:5
        //二进制左移1位
        //10的二进制位是:00001010 【10】
        //10的二进制左移1位是:00010100 【20】
        System.out.println(10 << 1);//输出为:20
    }
}
3.2.2 ArrayList构造方法
package Collectice;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
public class ArrayListTest02 {
    public static void main(String[] args) {
        //默认初始化容量10
        List myList1 = new ArrayList();
        //指定初始化容量100
        List myList2 = new ArrayList(100);
        //创建了HashSet集合
        Collection c = new HashSet();
        //添加元素到set集合
        c.add(100);
        c.add(200);
        c.add(900);
        c.add(500);
        //通过这个构造方法就可以将HashSet集合转换成List集合
        List myList3 = new ArrayList(c);
        for (int i = 0; i < myList3.size(); i++) {
            System.out.println(myList3.get(i));
        }
    }
}
运行结果:

3.3 LinkedList
- LinkedList集合是双向链表(看源码)
- 对于链表数据结构来说,随机增删效率较高,检索效率较低
- 链表中的元素在空间存储上,内存地址不连续
3.3.1 单向链表结构
| 链表优缺点 | 备注 | 
|---|---|
| 优点 | 由于链表上的元素在空间存储上内存地址不连续。所以随机增删元素的时候不会有大量元素位移,因此随机增删效率较高。在以后的开发中,如果遇到随机增删集合中元素的业务比较多时,建议使用LinkedList。 | 
| 缺点 | 不能通过数学表达式计算被查找元素的内存地址,每一次查找都是从头节点开始遍历,直到找到为止。所以LinkedList集合检索/查找的效率较低。 | 
ArrayList:把检索发挥到极致(末尾添加元素效率还是很高的)
 LinkedList:把随机增删发挥搭配极致
 加元素都是往末尾添加,所以ArrayList用的比LinkedList多
单链表中的节点:
 节点是单向链表中基本的单元,每一个节点Node都有两个属性:
- 属性1:是存储的数据
- 属性2:是下一个节点的内存地址
package danlink;
public class Node {
    //存储的数据
    Object element;
    //下一个节点的内存地址
    Node next;
    public Node() {
    }
    public Node(Object element, Node next) {
        this.element = element;
        this.next = next;
    }
}
链表:
package danlink;
public class Link {
    //头节点
    Node header;
    int size = 0;
    public int size(){
        return size;
    }
    //向链表中添加元素的方法(向某尾部添加)
    public void add(Object data) {
        //创建一个新的节点对象
        //让之前的单链表的某尾节点next指向新节点对象
        //有可能这个元素是第一个,也可能是第二个
        if (header == null){
            //说明还没有节点
            //new一个新的节点对象,作为头节点对象
            //这个时候的头节点既是一个头节点,又是一个末尾节点
            header = new Node(data,null);
        }else {
            //说明头不是空,头节点已有
            //找出当前某尾节点,让当前末尾节点的next是新节点
            Node currentLastNode = findLast(header);
            currentLastNode.next = new Node(data,null);
        }
        size++;
    }
    /**
     * 专门查找末尾节点的方法
     * @param
     * @return
     */
    private Node findLast(Node node) {
        if (node.next == null){
            //如果一个节点的next是null
            //说明这个节点就是末尾节点
            return node;
        }
        //程序能够到这里说明:node不是末尾节点
        return findLast(node.next);//递归算法!
    }
    //删除链表中某个数据的方法
    public void remove(Object obj) {
    }
    //修改链表中某个数据的方法
    public void modify(Object newObj) {
    }
    public int find(Object obj) {
        return 1;
    }
}
测试类:
package danlink;
public class Test {
    public static void main(String[] args) {
        //创建了一个集合对象
        Link link = new Link();
        //往集合中添加元素
        link.add(100);
        link.add(200);
        link.add(300);
        link.add(400);
        //获取元素个数
        System.out.println(link.size());//输出为:4
    }
}
注意:
- LinkedList集合底层也是有下标的
- ArrayList之所以检索效率比较高,不是单纯因为下标的原因,是因为底层数组发挥的作用
- LinkedList集合照样有下标,但是检索/查找某个元素的时候效率比较低,因为只能从头节点开始一个一个遍历
3.3.2 双向链表结构
内存演示图:

package danlink;
public class Test {
    public static void main(String[] args) {
        //创建了一个集合对象
        List list = new LinkedList();
        //往集合中添加元素
        list.add("a");
        list.add("b");
        list.add("c");
        //获取元素个数
        for(int i = 0;i < list.size();i++){
            Object obj = list.get(i);
            System.out.println(obj);
        }
    }
}
内存演示图:

 方法执行后内存释放:

 注意:LinkedList集合没有容量初始化;最初这个链表中没有任何元素,first和last引用都是null;
3.4 Vector
- 底层也是一个数组
- 初始化容量:10
- 扩容之后是原容量的2倍:10–>20–>40–>80
 ArrayList集合扩容特点:10–>15–>15*1.5
- Vector中所有的方法都是线程同步的,都带有synchronized关键字,是线程安全的,效率比较低,使用较少了。
- 将一个线程不安全的ArrayList集合转换成线程安全的:
 使用集合工具类:java.util.Collections;
 注意:
 java.util.Collection 是集合接口
 java.util.Collections 是集合工具类
import java.util.*;
public class VectorTest {
    public static void main(String[] args) {
        //创建一个Vector集合
        Vector vector = new Vector();
        //添加元素
        //默认容量10个
        vector.add(1);
        vector.add(2);
        vector.add(3);
        vector.add(4);
        vector.add(5);
        vector.add(6);
        vector.add(7);
        vector.add(8);
        vector.add(9);
        vector.add(10);
        //满了之后扩容
        vector.add(11);
        Iterator it = vector.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
        //以后使用
        List myList = new ArrayList();//非线程安全的
        //变成线程安全的
        Collections.synchronizedList(myList);//学会多线程后可看效果
        //myList集合就是线程安全的
        myList.add("111");
        myList.add("222");
        myList.add("333");
    }
}
4.泛型机制
- JDK5.0之后推出的新特性:泛型
- 泛型这种语法机制,只在程序编译阶段起作用,只是给编译器参考的(运行阶段泛型没用!)
- 泛型好处:
 第一:集合中存储的元素类型统一了
 第二:从集合中取出的元素类型是泛型指定的类型,不需要进行大量的“向下转型”!
- 泛型缺点:导致集合中存储的元素缺乏多样性!
- 大多数业务中,集合中元素的类型还是统一的,所以这种泛型特性被大家所认可
4.1 不使用泛型
package Collectice;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericTest01 {
    public static void main(String[] args) {
        //不使用泛型机制,分析程序存在缺点
        //创建集合
        List myList = new ArrayList();
        //准备对象
        Cat c = new Cat();
        Bird b = new Bird();
        //将对象添加到集合当中
        myList.add(c);
        myList.add(b);
        //遍历集合,取出每个Animal,让它move
        Iterator it = myList.iterator();
        while (it.hasNext()){
            //没有这个语法,通过迭代器取出的就是Object
            //Animal a = it.next();
            Object obj = it.next();
            //obj中没有move方法,无法调用,需要向下转型!、
            if (obj instanceof Animal){
                Animal a = (Animal) obj;
                a.move();
            }
        }
    }
}
class Animal{
    //父类自带方法
    public void move(){
        System.out.println("动物在移动!");
    }
}
class Cat extends Animal{
    //特有方法
    public void catchMouse(){
        System.out.println("猫抓老鼠!");
    }
}
class Bird extends Animal{
    //特有方法
    public void fly(){
        System.out.println("鸟儿在飞翔!");
    }
}
运行结果:

4.2 使用泛型
package Collectice;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericTest02 {
    public static void main(String[] args) {
        //使用JDK5之后的泛型机制
        //使用泛型List<Animal>之后,表示List集合只运行存储Animal类型的数据
        //用泛型来指定集合中存储的数据类型
        List<Animal> myList = new ArrayList<Animal>();
        //指定List集合中只能存储Animal,那么存储String就编译报错了
        //这样用了泛型之后,集合中元素的数据类型更加统一了
        //myList.add("abc");
        //准备对象
        Cat c = new Cat();
        Bird b = new Bird();
        myList.add(c);
        myList.add(b);
        //获取迭代器
        //这个表示迭代器迭代的是Animal类型
        Iterator<Animal> it = myList.iterator();
        while (it.hasNext()){
            //使用泛型之后,每一次迭代返回的数据都是Animal类型
            Animal a = it.next();
            //这里不需要进行强制类型转换了,直接调用
            a.move();
            //调用子类型特有的方法还说需要向下转型的
            Animal e = it.next();
            if (e instanceof  Cat){
                Cat x = (Cat) e;
                x.catchMouse();
            }
            if (e instanceof  Bird){
                Bird y = (Bird) e;
                y.fly();
            }
        }
    }
}
class Animal{
    //父类自带方法
    public void move(){
        System.out.println("动物在移动!");
    }
}
class Cat extends Animal{
    //特有方法
    public void catchMouse(){
        System.out.println("猫抓老鼠!");
    }
}
class Bird extends Animal{
    //特有方法
    public void fly(){
        System.out.println("鸟儿在飞翔!");
    }
}
4.3 自动类型推断机制(钻石表达式)
package Collectice;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericTest03 {
    public static void main(String[] args) {
        //ArrayList<这里的类型会自动推断>() 前提是JDK8之后才允许的
        //自动类型推断,钻石表达式!
        List<Animal> myList = new ArrayList<>();
        myList.add(new Animal());
        myList.add(new Cat());
        myList.add(new Bird());
        //遍历
        Iterator<Animal> it = myList.iterator();
        while (it.hasNext()){
            Animal a = it.next();
            a.move();
        }
    }
}
class Animal{
    //父类自带方法
    public void move(){
        System.out.println("动物在移动!");
    }
}
class Cat extends Animal{
    //特有方法
    public void catchMouse(){
        System.out.println("猫抓老鼠!");
    }
}
class Bird extends Animal{
    //特有方法
    public void fly(){
        System.out.println("鸟儿在飞翔!");
    }
}
4.4 自定义泛型
- 自定义泛型的时候,<>尖括号中的是一个标识符,随便写
- 用泛型就被固定了,不用泛型就是Object类型
- java源代码中经常出现的是:< E >和< T >
 E是Element单词首字母;T是Type单词首字母
package Collectice;
public class GenericTest04 <标识符随便写>{
    public void doSome(标识符随便写 o){
        System.out.println(o);
    }
    public static void main(String[] args) {
        //new对象的时候指定了泛型是:String类型
        GenericTest04<String> gt = new GenericTest04<>();
        //类型不匹配
        //gt.doSome(100);
        gt.doSome("abc");
    }
}
5.foreach
- JDK5.0之后推出了一个新特性:叫做增强for循环,或者叫做foreach
- 语法格式:
for(元素类型 变量名 : 数组或集合){
  System.out.println(变量名);
}
- foreach缺点:没有下标
- 在需要使用下标的循环中,不建议使用增强for循环
5.1 foreach正常案例
package Collectice;
public class ForEachTest01 {
    public static void main(String[] args) {
        //int类型数组
        int[] arr = {432,4,65,45,76,11,2};
        //遍历数组(普通for循环)
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
        System.out.println("========================");
        //foreach方法
        for (int data : arr) {
            //data就是数组中的元素(数组中的每一个元素)
            System.out.println(data);
        }
    }
}
运行结果:

5.2 foreach在集合中的使用
package Collectice;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ForEachTest02 {
    public static void main(String[] args) {
        //创建List集合
        List<String> strList = new ArrayList<>();
        //添加元素
        strList.add("hello!");
        strList.add("world!");
        strList.add("kitty!");
        //遍历,使用迭代器方式
        Iterator<String> it = strList.iterator();
        while (it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
        //使用下标方式(只针对于有下标的集合)
        for (int i = 0; i < strList.size(); i++) {
            System.out.println(strList.get(i));
        }
        //使用foreach
        for(String s : strList){//因为泛型使用的是String类型,所以是:String s
            System.out.println(s);
        }
    }
}








