1. 集合介绍
集合是java中提供的一种容器,可以用来存储多个数据。由java基础语法中得知,如果数据多了,可以使用数组或者ArrayList集合来存放。
数组和集合的区别:
数组的长度是固定的,集合的长度是可变的
集合中存储的元素必须是引用类型
集合学习的目标:
集合本身是一个存储的容器:
必须能用集合存储对象
能够增加元素、遍历集合
能够掌握不同集合的特性
2. 回顾ArrayList集合
ArrayList比数组的好处是,存储的元素长度可变。
ArrayList原则上不存储基本类型,如果是基本类型,是自动装箱存储。
2.1 对基本类型的操作
public static void addShowArrList() {
ArrayListarr = new ArrayList();
arr.add(11); arr.add(22);
arr.add(33); arr.add(44);
arr.add(55); arr.add(66);
for(int i = 0; i < arr.size(); i++) {
System.out.println(arr.get(i));
}
arr.remove(2);
for(int i = 0; i < arr.size(); i++) {
System.out.println(arr.get(i));
}
}
2.2 对引用类型的操作
定义一个Persion类:
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
Super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
测试代码:
public static void addShowArrList2() {
ArrayListarrPerson = new ArrayList();
arrPerson.add(new Person("aaa", 20));
arrPerson.add(new Person("bbb", 25));
arrPerson.add(new Person("ccc", 30));
for (int i = 0; i < arrPerson.size(); i++) {
System.out.println(arrPerson.get(i)); //这里因为Person中重写了toString方法,所以不是打印对象地址
}
}
3. 集合的继承关系
查阅API手册,发现ArrayList类它继承了抽象类AbstractList,同时实现接口List,而List接口又继承了Collection接口。所以Collection接口为最顶层的集合接口了
interface List extends Collection{
}
public class ArrayList extends AbstractList implements List{
}
根据源码片段,说明使用ArrayList类时,该类已经把所有抽象方法进行了重写。那么,实现Collection接口的所有子类都会进行方法重写。
Collection接口常用的子接口有:List接口、Set接口
List接口常用的子类有:ArrayList类、LinkedList类
Set接口常用的子类有:HashSet类、LinkedHashSet类
Collection接口是集合中的顶层接口,它中定义的所有功能子类都可以使用。
Collection表示一组对象,这些对象也称为Collection的元素。
List派系的Collection允许有重复的元素,而Set派系的则不允许有重复元素。
List派系的Collection是有序的,而Set派系的则是无序的集合
4. 集合Collection
4.1 Collection的add, clear方法
add, clear是Collection类的抽象方法,ArrayList最终将他们实现了,用ArrayList举例
Collection接口中的方法是集合中所有实现类必须拥有的方法
List extends Collection
ArrayList implements List
4.2 contains、size方法
private static void testFunc2() {
Collectioncoll = new ArrayList();
coll.add("abc");
coll.add("def");
coll.add("ghi");
System.out.println(coll);
System.out.println(coll.contains("def"));
System.out.println(coll.contains("acb"));
System.out.println(coll.size());
}
4.3 引申获取长度的方式
Java中获取长度的三种方式,都返回int型:
数组.length
字符串.length()
集合.size()
private static void testFunc3() {
int[] arr = new int[3];
System.out.println(arr.length);
String str = "kasdlfjklsdfj";
System.out.println(str.length());
Collectioncoll = new ArrayList();
coll.add("abc");
coll.add("def");
coll.add("ghi");
System.out.println(coll);
System.out.println(coll.size());
}
4.3 toArray方法
把集合转成数组, 返回是一个存储对象的数组
private static void testFunc3() {
Collectioncoll = new ArrayList();
coll.add("abc");
coll.add("def");
coll.add("ghi");
System.out.println(coll);
Object[] obj = coll.toArray();
for(int i = 0; i < obj.length; i++) {
System.out.println(obj[i]);
}
}
将集合转为数组,这样数组中的集合信息就不能被修改
4.4 remove方法
删除集合中的指定参数
private static void testFunc4() {
Collectioncoll = new ArrayList();
coll.add("abc");
coll.add("def");
coll.add("ghi");
System.out.println(coll);
System.out.println(coll.remove("def"));
System.out.println(coll);
}
特别注意:因为ArrayList类是List派系的,是允许有重复元素的,在remove的时候实际上会删除第一个集合中的元素
5. Iterator接口:迭代器,获取集合元素
由于Java中有多种集合,它们在存储元素时,采用的存储方式不同,我们要取出这些集合中的元素,可通过一种通用的获取方式来完成。
屏蔽了不同集合获取元素的不同
5.1 Iterator实现原理
迭代器的运用,使用了面向接口的编程思想:
Iterator是一个接口,使用它必须找到实现类
在Collection接口中有一个iterator方法, 返回Iterator:Iterator
iterator() 子类ArrayList实现了iterator方法
ArrayList的iterator()方法,返回的是是Iterator接口的实现类的对象
Iterator it = array.iterator(), it就是Iterator接口的实现类,是由ArrayList对象array的iterator方法返回的
5.2 Iterator代码实现
引申1:如果将ArrayList改为HashSet,意思为使用HashSet集合来添加元素,使用Iterator取出HashSet中的元素。取出时,由于HashSet是Set派系的,Set派系是无须集合,所以Iterator取出时是无序的,且在添加元素时是不能重复的:
引申2:使用for循环也能使用迭代器
使用for的唯一好处是由于it是在for中定义的,因此退出for之后,it的存储空间立即被释放,比while节约空间
for(Iterator it = coll.iterator(); it.hasNext(); ) {
System.out.println(it.next());
}
5.3 Iterator执行过程
5.4 集合迭代中的转型(不建议这样使用,尽量使用泛型)
集合可以存任意类型的对象
集合中,不指定存储的数据类型,因此它什么都可以存
特别注意:如果要使用默认类型的特有方法,必须进行向下转型
6. 增强型for循环
参考:https://www.jianshu.com/p/856ec374ff4e
也是三种遍历数组的方式之一
从JDK1.5开始Collection强行继承了Iterable接口,只有一个目的,就是要让集合使用foreach方法
6.1 foreach遍历数组
优势:简化代码,方便遍历容器
劣势:int a只是一个变量,arr中的元素赋值给a,arr内存中的数据无法被改变
6.2 foreach遍历集合
Person类:
7. 泛型
7.1 泛型概述
在5.4中,由于定义集合Collection时没有声明元素类型,因此同一个集合中可以add任何类型的数据,虽然遍历时打印元素不会出现问题,但要使用到强转类型时就会报错:Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
在JDK1.5开始就引入了泛型这种安全机制,保证程序的安全性
泛型:指明了集合中存储的数据类型,用一对<>表明:<数据类型>
这样,如果添加了非泛型的数据类型,编译器是会报错的
有了泛型,再使用泛型数据类型的特有方法时,也不需要进行类型强转了
引申:
JAVA的泛型是伪泛型,使用了泛型<数据类型>只是针对编译器生效。在编译生成的class文件中,其实是没有泛型的表现的(可以通过java反编译成源码查看,反编译出来的代码没有泛型),但这样也足够安全了。
据说C++的泛型是真泛型
7.2 带有泛型的类
使用过的ArrayList是我们最熟悉的带有泛型的类
public class ArrayList
其中E是Element的简写,理解为一个变量类型,E是什么类型,ArrayList集合中就存什么类型,而且其成员方法都带有
public boolean add(E e)
Iterator
it = arr.iterator();
例子见Iterator
7.3 带泛型的方法
看一段ArrayList类的add方法:
7.4 带泛型的接口
List接口就是一个带泛型的接口
可见,我们在项目中定义类的时候,可以先实现接口,不理会泛型,这样,调用者在new对象的时候再来指定类型
7.5 使用泛型的好处总结
解决了强转时的安全问题
将错误从运行时提前到了编译时
增加了开发和调用者的灵活性
泛型的出现,带来了增强型for循环foreach的出现
7.6 泛型通配符 ?
例:定义一个函数,可以同时迭代不同泛型类型的集合
可见,由于ArrayList是List派系的,HashSet是Set派系的,因此参数必须使用List和Set派系的父类接口Collection。总之需要向上找相同的父类。
由于?通配符不知道什么类型,因此不能做强转
7.7 泛型的限定
略