概述
- 集合类的由来:- 对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定。就使用集合容器进行存储。
 
- 集合特点:- 用于存储对象的容器。
- 集合的长度是可变的。
- 集合中不可以存储基本数据类型值。
 
体系
- 集合容器因为内部的数据结构不同,有多种具体容器。不断的向上抽取,就形成了集合框架。
- 框架的顶层就是Collection接口:- List:有序(存入和取出的顺序一致),元素都有索引(角标),元素可以重复。
- Set:元素不能重复,无序。
 
功能(Collection接口)
- 添加 - 1 
 2- boolean add(E e); 
 boolean addAll(Collection coll);
- 判断 - 1 
 2
 3- boolean remove(object obj): 
 boolean removeAll(Collection coll);
 void clear();
- 删除 - 1 
 2
 3- boolean contains(object obj): 
 boolean containsAll(Colllection coll);
 boolean isEmpty();//判断集合中是否有元素。
- 获取 - 1 
 2
 3
 4
 5
 6
 7
 8
 9- int size(): 
 Iterator iterator():取出元素的方式:迭代器。
 该对象必须依赖于具体容器,因为每一个容器的数据结构都不同。
 所以该迭代器对象是在容器中进行内部实现的。
 对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,
 也就是iterator方法。
 Iterator接口就是对所有的Collection容器进行元素取出的公共接口。
 其实就是抓娃娃游戏机中的夹子!
- 其他 - 1 
 2- boolean retainAll(Collection coll);//取交集。 
 Object[] toArray();//将集合转成数组。-  
方法演示
- 展示一下如何使用这些方法 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72- public class CollectionDemo { 
 /**
 * @param args
 */
 public static void main(String[] args) {
 Collection coll = new ArrayList();
 
 // show(coll);
 
 Collection c1 = new ArrayList();
 Collection c2 = new ArrayList();
 show(c1,c2);
 
 }
 
 public static void show(Collection coll){
 
 //1,添加元素。add.
 coll.add("abc1");
 coll.add("abc2");
 coll.add("abc3");
 System.out.println(coll);
 
 //2,删除元素。remove
 coll.remove("abc2");//会改变集合的长度
 
 //清空集合.
 //coll.clear();
 
 System.out.println(coll.contains("abc3"));
 System.out.println(coll);
 
 }
 
 public static void show(Collection c1,Collection c2){
 
 //给c1添加元素。
 c1.add("abc1");
 c1.add("abc2");
 c1.add("abc3");
 c1.add("abc4");
 
 //给c2添加元素。
 c2.add("abc1");
 c2.add("abc2");
 c2.add("abc3");
 c2.add("abc4");
 c2.add("abc5");
 
 System.out.println("c1:"+c1);
 System.out.println("c2:"+c2);
 
 //演示addAll
 // c1.addAll(c2);//将c2中的元素添加到c1中。
 
 //演示removeAll
 // boolean b = c1.removeAll(c2);//将两个集合中的相同元素从调用removeAll的集合中删除。
 // System.out.println("removeAll:"+b);
 
 //演示containsAll
 // boolean b = c1.containsAll(c2);
 // System.out.println("containsAll:"+b);
 
 //演示retainAll
 boolean b = c1.retainAll(c2);//取交集,保留和指定的集合相同的元素,而删除不同的元素。
 //和removeAll功能相反 。
 System.out.println("retainAll:"+b);
 System.out.println("c1:"+c1);
 }
 
 }-  
迭代器
- 演示迭代器方法: - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23- public static void main(String[] args) { 
 Collection coll = new ArrayList();
 coll.add("abc1");
 coll.add("abc2");
 coll.add("abc3");
 coll.add("abc4");
 
 // System.out.println(coll);
 
 // 使用了Collection中的iterator()方法。 调用集合中的迭代器方法,是为了获取集合中的迭代器对象。
 // Iterator it = coll.iterator();
 // while(it.hasNext()){
 // System.out.println(it.next());
 // }
 
 //这样写也可以
 for(Iterator it = coll.iterator(); it.hasNext(); ){
 System.out.println(it.next());
 }
 
 }
 }
- 迭代器图解 

List
- List:有序(存入和取出的顺序一致),元素都有索引(角标),元素可以重复。 
- 特有的常见方法:有一个共性特点就是都可以操作角标。 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15- 1,添加 
 void add(index,element);
 void add(index,collection);
 2,删除;
 Object remove(index):
 3,修改:
 Object set(index,element);
 4,获取:
 Object get(index);
 int indexOf(object);
 int lastIndexOf(object);
 List subList(from,to);
- list集合是可以完成对元素的增删改查。 
ListIterator
- 在迭代器过程中,不要使用集合操作元素,容易出现异常。 
- 否则会报错(java.util.ConcurrentModificationException) 
- 可以使用Iterator接口的子接口ListIterator来完成在迭代中对元素进行更多的操作。 
- 它可以实现在迭代过程中完成对元素的增删改查。 
- 注意:只有list集合具备该迭代功能 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27- public static void main(String[] args) { 
 List list = new ArrayList();
 
 list.add("abc1");
 list.add("abc2");
 list.add("abc3");
 
 System.out.println("list:"+list);
 ListIterator it = list.listIterator();
 
 while(it.hasNext()){
 
 Object obj = it.next();
 if(obj.equals("abc2")){
 it.set("abc9");
 }
 }
 System.out.println("hasNext:"+it.hasNext());
 //判断是否有上一个数据
 System.out.println("hasPrevious:"+it.hasPrevious());
 //反向迭代数据
 while(it.hasPrevious()){
 System.out.println("previous:"+it.previous());
 }
 System.out.println("list:"+list);
 }
List常用子类特点
- Vector:内部是数组数据结构,是同步的。增删,查询都很慢!
- ArrayList:内部是数组数据结构,是不同步的。替代了Vector。查询的速度快。
- LinkedList:内部是链表数据结构,是不同步的。增删元素的速度很快。
- Vector 的Enumeration方法和迭代器功能相同,但名字过长
LinkedList
- jdk1.6的方法更新 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11- getFirst();.//获取但不移除,如果链表为空,抛出NoSuchElementException. 
 getLast();
 JDK1.6
 peekFirst();//获取但不移除,如果链表为空,返回null.
 peekLast():
 removeFirst();//获取并移除,如果链表为空,抛出NoSuchElementException.
 removeLast();
 JDK1.6
 pollFirst();//获取并移除,如果链表为空,返回null.
 pollLast();
存储自定义对象
| 1 | public class ArrayListTest { | 
链表练习
- 用链表简单模拟一个堆栈及队列的输入输出过程 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33- import java.util.Iterator; 
 import java.util.LinkedList;
 public class LinkedListDemo {
 public static void main(String[] args) {
 LinkedList link = new LinkedList();
 
 link.addFirst("abc1");
 link.addFirst("abc2");
 link.addFirst("abc3");
 link.addFirst("abc4");
 // System.out.println(link);
 // System.out.println(link.getFirst());//获取第一个但不删除。
 // System.out.println(link.getFirst());
 
 // System.out.println(link.removeFirst());//获取元素但是会删除。
 // System.out.println(link.removeFirst());
 
 while(!link.isEmpty()){
 System.out.println(link.removeLast());
 }
 
 
 System.out.println(link);
 // Iterator it = link.iterator();
 // while(it.hasNext()){
 // System.out.println(it.next());
 // }
 }
 }
Set
- Set:元素不可以重复,是无序。Set接口中的方法和Collection一致。
HashSet
- HashSet: 内部数据结构是哈希表 ,是不同步的。 
- 如何保证该集合的元素唯一性呢? - 是通过对象的hashCode和equals方法来完成对象唯一性的!
- 如果对象的hashCode值不同,那么不用判断equals方法,就直接存储到哈希表中。
- 如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true。
- 如果为true,视为相同元素,不存。如果为false,那么视为不同元素,就进行存储。
 
- 记住:如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。一般情况下,如果定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals,hashCode方法。建立对象判断是否相同的依据。 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16- //Person类有name,年龄 
 //重写hashCode,equals
 public int hashCode() {
 return name.hashCode()+age*27;
 return 100;
 }
 public boolean equals(Object obj) {
 //判断是否是同一个对象
 if(this == obj)
 return true;
 //判断是否属于Person类
 if(!(obj instanceof Person))
 throw new ClassCastException("类型错误");
 Person p = (Person)obj;
 return this.name.equals(p.name) && this.age == p.age;
 }
练习
- 将Person类中去除相同元素 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20- //前提:Person中 
 public static ArrayList getSingleElement(ArrayList al) {
 
 //1,定义一个临时容器。
 ArrayList temp = new ArrayList();
 //2,迭代al集合。
 Iterator it = al.iterator();
 while(it.hasNext()){
 Object obj = it.next();
 //3,判断被迭代到的元素是否在临时容器存在。
 if(!temp.contains(obj)){
 temp.add(obj);
 }
 }
 return temp;
 }
LinkedHashSet
- 与HashSet区别就是HashSet无序,LinkedHashSet有序,
TreeSet
- TreeSet可以对Set集合中的元素进行排序。是不同步的。 
- 判断元素唯一性的方式:就是根据比较方法的返回结果是否是0,是0,就是相同元素,不存。 
- TreeSet对元素进行排序的方式一: 
- 让元素自身具备比较功能,就需要实现Comparable接口。覆盖compareTo方法。 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19- //Person中的方法 
 public int compareTo(Object o) {
 //向下转型前最好进行健壮性增强
 Person p = (Person)o;
 int temp = this.age-p.age;
 return temp==0?this.name.compareTo(p.name):temp;
 /*
 if(this.age>p.age)
 return 1;
 if(this.age<p.age)
 return -1;
 else{
 return this.name.compareTo(p.name);
 }
 */
 }
- 如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。怎么办? 
- 可以使用TreeSet集合第二种排序方式二: 
- 让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。 
- 将该类对象作为参数传递给TreeSet集合的构造函数。 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16- /** 
 * 创建了一个根据Person类的name进行排序的比较器。
 */
 public class ComparatorByName implements Comparator {
 
 public int compare(Object o1, Object o2) {
 Person p1 = (Person)o1;
 Person p2 = (Person)o2;
 
 int temp = p1.getName().compareTo(p2.getName());
 return temp==0?p1.getAge()-p2.getAge(): temp;
 //return 1; 这样写的话,注销前面的就意味着按输入顺序来
 }
 }
- 内部实现用的二叉树,打个比方,返回正数为右子树,负数为左子树 
泛型
- jdk1.5出现的安全机制。
- 好处:- 1、将运行时期的问题ClassCastException转到了编译时期。
- 2、避免了强制转换的麻烦。
 
- <>:什么时候用?- 当操作的引用数据类型不确定的时候。就使用<>。将要操作的引用数据类型传入即可.
- 其实<>就是一个用于接收具体引用数据类型的参数范围。
 
- 在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型 。
- 泛型技术是给编译器使用的技术,用于编译时期。确保了类型的安全。
擦除补偿
- 运行时,会将泛型去掉,生成的class文件中是不带泛型的,这个称为泛型的擦除。
- 为什么擦除呢?- 因为为了兼容运行的类加载器。
 
- 泛型的补偿:- 在运行时,通过获取元素的类型进行转换动作。不用使用者在强制转换了。
 
泛型类
- 在jdk1.5后,使用泛型来接收类中要操作的引用数据类型。 
- 泛型类。什么时候用? - 当类中的操作的引用数据类型不确定的时候,就使用泛型来表示。
 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12- //主类调用此类时,需为其赋上数据类型 
 public class Tool<QQ>{
 private QQ q;
 public QQ getObject() {
 return q;
 }
 public void setObject(QQ object) {
 this.q = object;
 }
 }
泛型方法
- 泛型方法的使用 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32- public class Tool<QQ>{ 
 private QQ q;
 public QQ getObject() {
 return q;
 }
 public void setObject(QQ object) {
 this.q = object;
 }
 
 
 /**
 * 将泛型定义在方法上。
 * @param str
 */
 public <W> void show(W str){
 System.out.println("show : "+str.toString());
 }
 public void print(QQ str){
 System.out.println("print : "+str);
 }
 
 /**
 * 当方法静态时,不能访问类上定义的泛型。如果静态方法使用泛型,
 * 只能将泛型定义在方法上。
 * @param obj
 */
 public static <Y> void method(Y obj){
 System.out.println("method:"+obj);
 }
 }
通配符
- 泛型的通配符:? 未知类型。 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23- public static void main(String[] args) { 
 ArrayList<String> al = new ArrayList<String>();
 al.add("abc");
 al.add("hehe");
 ArrayList<Integer> al2 = new ArrayList<Integer>();
 al2.add(5);
 al2.add(67);
 printCollection(al);
 printCollection(al2);
 }
 /**
 * 迭代并打印集合中元素。
 * @param al
 */
 public static void printCollection(Collection<?> al) {
 Iterator<?> it = al.iterator();
 while(it.hasNext()){
 System.out.println(it.next().toString());
 }
 }
泛型的限定
- 上限 ? extends E - 接收E类型或者E的子类型对象
- 一般存储对象的时候用。比如 添加元素 addAll.
 
- 下限 ? super E - 接收E类型或者E的父类型对象。
- 一般取出对象的时候用。比如比较器。
 - 1 
 2
 3
 4
 5
 6
 7
 8
 9- public static void printCollection(Collection<? extends Person> al) { 
 
 Iterator<? extends Person> it = al.iterator();
 
 while(it.hasNext()){
 Person p = it.next();
 System.out.println(p.getName()+":"+p.getAge());
 }
 }
- 什么时候用上限? - 一般在存储元素的时候都是用上限,因为这样取出都是按照上限类型来运算的。不会出现类型安全隐患。
 
- 什么时候用下限呢? - 通常对集合中的元素进行取出操作时,可以使用下限。如TreeSet的比较器
 
集合选择技巧
需要唯一吗??
- 需要:Set- 需要制定顺序: - 需要: TreeSet
- 不需要:HashSet
- 但是想要一个和存储一致的顺序(有序):LinkedHashSet
 
 
- 需要制定顺序: 
- 不需要:List- 需要频繁增删吗?- 需要:LinkedList
- 不需要:ArrayList
 
 
- 需要频繁增删吗?
如何记录各容器的结构和所属体系呢??
- List- ArrayList
- LinkedList
 
- Set- HashSet
- TreeSet
 
- 后缀名就是该集合所属的体系,前缀名就是该集合的数据结构。
 看到array:就要想到数组,就要想到查询快,有角标.
- 看到Link:就要想到链表,就要想到增删快,就要想要 add get remove方法及头插尾插法
- 看到hash:就要想到哈希表,就要想到唯一性,就要想到元素需要覆盖hashCode方法和equals方法。
- 看到tree:就要想到二叉树,就要想要排序,就要想到两个接口Comparable,Comparator 。
- 而且通常这些常用的集合容器都是不同步的。
Map
- 一次添加一对元素。Collection 一次添加一个元素。
- Map也称为双列集合,Collection集合称为单列集合。
- 其实map集合中存储的就是键值对。
- map集合中必须保证键的唯一性。
常用方法
| 1 | 1,添加。 | 
重点方法
- 取出map中所有元素
| 1 | //取出map中的所有元素。 | 

- 取出map中所有元素–>第二种方法
| 1 | /* | 

常用子类
- Hashtable :内部结构是哈希表,是同步的。不允许null作为键,null作为值。- Properties:用来存储键值对型的配置文件的信息,可以和IO技术相结合
 。
 
- Properties:用来存储键值对型的配置文件的信息,可以和IO技术相结合
- |–HashMap : 内部结构是哈希表,不是同步的。允许null作为键,null作为值。
- |–TreeMap : 内部结构是二叉树,不是同步的。可以对Map集合中的键进行排序。 -  
练习一
- 将学生对象和学生的归属地通过键与值存储到map集合中。
| 1 | public static void hashMapMethod(){ | 
- 如果需要进行排序的话,使用TreeMap
| 1 | public static void treeMapDemo() { | 
- 如果需要按输入顺序存储的话,LinkedHashMap
练习二
| 1 | 
 | 
工具类
Collections
- Collections是集合框架的工具类, 里面的方法都是静态的。
- 对list集合进行指定顺序的排列
| 1 | //因为这个排序方法是基于比较器Comparable的,所以使用泛型 | 
- 折半查找 binarySearch
- 最值 max
- 逆序
- 替换 replace
- 全部替换 fill
- 将list随机洗牌 shuffle
给非同步集合加锁
- 同步方法怎么实现的那?
| 1 | class MyCollections{ | 
- 那么该如何调用这个方法那?
| 1 | List list = new ArrayList();//非同步的。 | 
Arrays
toString
- toString 经典实现
| 1 | //toString的经典实现。 | 
asList
- asList 数组转 List 集合
| 1 | public static void demo_1() { | 
- toArray 集合转数组
| 1 | /* | 
JDK5 特性
ForEach
概述
| 1 | foreach语句: | 
- 代码演示
| 1 | public static void main(String[] args){ | 
传统for、高级for区别
- 传统for可以完成对语句执行很多次,因为可以定义控制循环的增量和条件。
- 高级for是一种简化形式。- 它必须有被遍历的目标。该目标要是数组,要么是Collection单列集合。
 
- 对数数组的遍历如果仅仅是获取数组中的元素,可以使用高级for。
- 如果要对数组的角标进行操作建议使用传统for。
注意事项
- 可以使用高级for遍历map集合吗?
- 不能直接用,但是可以将map转成单列的set,就可以用了。
| 1 | Map<Integer,String> map = new HashMap<Integer,String>(); | 
函数可变参数
- 函数的可变参数。- 其实就是一个数组,但是接收的是数组的元素。
- 自动将这些元素封装成数组。简化了调用者的书写。
- 注意:可变参数类型,必须定义在参数列表的结尾。
 
| 1 | public static void main(String[] args) { | 
静态导入
| 1 | import java.util.ArrayList; |