文章目录
1. 什么是集合 ?
- 可以把它比作一种容器,也是用来存储数据的。可以存储,不同类型的数据。
- 不能存储基本类型。 , 可以存储基本类型的包装类。
- 集合的长度不是固定的,是可以改变的,随着元素的增长而增长。
- 删除,插入,非常方便。
- 它是怎么产生的!? 从之前学过的知识点分析,肯定是为了解决一些情况而产生的。
- 如,在同一个集合中存储不同的数据类型。(注意:但是一般不会这么干!)
- 数组和现在的集合对比区别!? 还记得数组的特点么!?
1.2. Collection
1.2.1 集合的关系“全家福”
- 集合,在语言设计上是 面向对象的,通过 继承,实现 等设计思想来完成。
- 在集合上也是找到相同关系的子类进行向上抽取共同的方法,使结构变得简单,使用不同的子类来处理不同的场景。
- 清爽的简化过程图:
1.2.2 Collenction结构
-
API原话直译第一行 :集合表示一组被称为其元素的对象。
- 一些集合允许重复元素,而其他集合不允许。 JDK不提供此接口的任何直接实现:它提供了更具体的子接口的实现,如Set和List 。 该界面通常用于传递集合,并在需要最大的通用性的情况下对其进行操作。
-
结构介绍:
1.Collenction 接口:
-- 1.1 List 接口: 数据有序 (有下标), 可以重复。
-- ArrayList 实现子类:
-- LinkedList 实现子类:
-- 1.2 Set 接口: 数据无序 (无下标),不可以重复。
-- HashSet 实现子类:
-- TreeSet 实现子类:
2. Map 接口:键值对存数据。
-- HashMap :
3. Collections 注意: 加了s的就是工具类。
1.2.3 Collection 接口中方法介绍
- 学习共性方法,
java.util.collection
包下,了解之后在学习子类中特有的方法。
方法 | 主要作用 |
---|---|
boolean add(E e) | 添加元素 |
boolean addAll(Collection c) | 将指定集合中的所有元素添加到另一个集合。 |
boolean contains(Object o) | 如果此 collection 包含指定的元素,则返回 true。 |
boolean isEmpty() | 如果此 collection 没有元素,就是看集合是否为空,为空则返回 true。 |
int size() | 返回此 collection 中的元素数 |
Objec[] toArray() | 返回对象数组 |
boolean remove(Object o) | 从此 collection 中移除指定元素的单个实例 |
boolean removeAll(C c) | 集合之间的操作,将包含集合中的元素从集合中去除 |
void clear() | 从此集合中删除所有元素(可选操作)。 此方法返回后,集合将为空。 |
Iterator< E > iterator() | 返回在此 collection 的元素上进行迭代的迭代器。 |
1.3 练习: Collection 中常用方法的使用
java.util.conllection;
包下,注意别导错包,以及Collection
中,常用方法的使用。Iterator< E > iterator();
迭代器 的使用。
public class Test_Collection {
public static void main(String[] args) {
/* 怎么测试呢!?
1.创建对象
2.测试方法
3.查看结果
*/
Collection c = new ArrayList(); // 接口不能new 只能用实现类 ArrayList();
//1向集合中添加元素。add(E e)这个E是泛型的意思。可以固定类型。
c.add("老天师");
c.add("武当王也");
c.add(18);
c.add('男');
System.out.println(c); //直接可以查看结果。
//1.1 clear() :清除集合里的所有元素。
/*
c.clear();
System.out.println(c); //返回空了。
*/
//1.2 contains() :指的是该集合是否包含指定元数,返回类型 boolean类型。
System.out.println(c.contains("武当王也")); //true
//1.3 hashcode(): 返回集合在内存中的哈希码值
System.out.println(c.hashCode());
//1.4 isEmpty():判断集合是否为空,为空则true
System.out.println(c.isEmpty());
//1.5 remove() : 移除集合中指定元数。
System.out.println(c.remove("武当王也"));
System.out.println(c);
//1.6 size() : 返回集合中元数个数。
System.out.println(c.size());
//1.7 toArray() :集合返回数组对象返回的是object类型!
Object[] objects = c.toArray();
System.out.println(Arrays.toString(objects));
System.out.println("--------------------华丽分割线--------------------");
//2 <E>指的是固定类型。 一种约束。而且集合中只能添加引用类型。
Collection<Integer> c2 = new ArrayList();
//2.1 c2.add("王也"); //报错。 只能添加int类型的包装类Interger引用类型。
c2.add(1949);
c2.add(10);
c2.add(1);
/*测试集合与集合之间的操作*/
//2.1 addAll(C c) :将后面集合添加到前面集合中。
System.out.println(c.addAll(c2));
System.out.println(c);
System.out.println(c2);
//2.1.1集合是需要指定泛型的, 如果不指定类型,则可以添加如果指定则不可以。如String 和 Interger不可以相互添加。
Collection<String> c3 = new ArrayList();
c3.add("老天师");
// System.out.println(c2.addAll(c3)); //报错
//2.2 containsAll(); 前面的集合包含后面集合 包含则返回true;
System.out.println("containsAll: "+c.containsAll(c3));
//2.3 removeAll(); 将集合后面的所包含的元数移除。
// System.out.println("removeAll:"+c.removeAll(c3));
// System.out.println(c);// [老天师] 已经被移除。
//2.4 retainAll()只保留包含集合的相同的元素。
System.out.println(c.retainAll(c3));
System.out.println(c);
/*
3.重点: 迭代器 iterator(); 要想操作集合中的元素,就需要遍历\循环\迭代。
拿c2 集合做实验. 注意以下两个方法:
1.首先要获取结合的迭代器。
2.hasNext(); 如果集合有元素就返回 true;
3.next(); 返回迭代的元素。
*/
//3.1 获取集合的迭代器
Iterator<Integer> iterator = c2.iterator();
//3.2 循环判断是否有下一个元素,通常使用while循环
while (iterator.hasNext()){
//3.3 如果有元素,就获取当前迭代的元素。
Integer next = iterator.next();
System.out.println(next);//输出
}
}
}
输出结果:
[老天师, 武当王也, 18, 男]
true
986602119
false
true
[老天师, 18, 男]
3
[老天师, 18, 男]
--------------------华丽分割线--------------------
true
[老天师, 18, 男, 1949, 10, 1]
[1949, 10, 1]
containsAll: true
true
[老天师]
1949
10
1
- 为了方便理解,引入一个知识点: 泛型,通常和集合一起使用。
2 泛型
2.1 什么是泛型 ?
- 什么是泛型!? 其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。 在程序 ‘编译期’ 间就会约束类型,相当于在编译器提供了一个安全检查机制。
- 泛型生活中的例子
<T>
。如:班主任:她在门口看着每一个进班级的学生,<我们班学生>
是这个班就让进(相当于提前指定了类型), 不是就不让进。 - 要吃饭自然就少不了碗,但是没有规定碗只能盛饭,除了盛饭它还能干其他的,编写代码人员不关心碗盛什么,具体要盛什么由使用者来决定,。
- 泛型生活中的例子
- 泛型(Generics)通常跟集合一起使用。
- 集合特点:就是可以接受任意类型的元素,但是在实际使用过程中会经常遇到类型转换问题,这样就会存在隐患。 所以java提供了泛型 解决了安全问题。
- 如:
ClassCastException
类型转换不兼容异常。
- 如:
2.1.1 泛型的安全问题
-
这个时候用的
<T>
也是用的多态的思想,因为我们不知道子类会传什么对象过来,同时这样写也增加了子类的扩展性。 -
子类就可以像下面这样根据自己的需要传入对象:
- 如果没有泛型约束,就会经常发生类型强制转 换问题。
public static void main(String[] args) { /*1.没有泛型约束 相当于存入的是Object类型 如果使用就需要强制转换为Integer */ Collection c = new ArrayList(); c.add(18); c.add("20");//添加 String类型的 20 Iterator iterator = c.iterator(); while(iterator.hasNext()){ Object next = iterator.next();//如果使用需要转换类型 //需要使用时 Integer i = (Integer)next; System.out.println(i);//发生转换错误 ClassCastException } /*2.有泛型约束,取出结果不需要强制转换, 根据约束存储类型一致。 */ Collection<Integer> c2 = new ArrayList(); //c2.add("20"); //只能添加 integer类型 c2.add(18); Iterator<Integer> iterator2 = c2.iterator(); while(iterator2.hasNext()){ Integer next = iterator2.next();//直接就是Integer类型 System.out.println(next); } }
2.1.2 作用
- 通过泛型的语法定义,约束集合元素的类型,进行安全检查,把错误显示在编译期,减少在运行期间出错。
- 泛型可以提升程序代码的可读性,代码的通用性。
2.2 泛型格式
- Eclipse 是自动开启的, 但是IDEA 泛型设置默认不检查,需要自己开启。
- 第一个检查勾选的话是右边不检查约束。 在JDK 1.8之后第二个尖括号可以不写。
- 第一个检查勾选的话是右边不检查约束。 在JDK 1.8之后第二个尖括号可以不写。
public class Test_Generics {
public static void main(String[] args) {
/*
1.当我们创建一个集合时,就会有这样的警告,
// ArrayList arrayList = new ArrayList();
原因是:泛型警告,查看底层发现有泛型约束。这个<E>
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
*/
ArrayList<String> arrayList = new ArrayList();//在jdk1.8以后就不需要右边加约束了。也可以自动推断类型。
//2.约束的作用在于什么地方呢!?声明时需要指定传入参数类型,编译器在编译阶段会自动校验。
//arrayList.add(1949);//在编译期间就会提示错误。添加一个Interger类型或报错!?因为<String>约束里是String类型的。
arrayList.add("1949-10-1");//添加String类型就不会报错。
}
}
2.2.1 泛型声明位置
- 声明时,需要指定传入参数类型,编译器在编译阶段会自动校验。
名称 | 作用 |
---|---|
E | Element 在集合中使用,因为集合中存放的是元素 |
T | Type(表示Java 类,包括基本的类和我们自定义的类) |
K | Key(表示键,比如Map中的key) |
V | Value(表示值) |
N | Number(表示数值类型) |
? | (表示不确定的java类型) |
- 以使用在 接口,类,方法,上使用。
- 提示:参考Collection 集合源码。
- 泛型在类上, 泛型在方法上区别。
public class Test<E> { //泛型定义在类上,在整个类都有效 public void add(E e){ // 跟类的保持一致。 System.out.println(e); } //泛型在方法上,跟方法保持一致,不受类的影响。 public static <T> void show(T t){ System.out.println(t); } public static void main(String[] args) { Test<Integer> s = new Test(); s.add(12); //添加的就是Integer类型 Test<String> i = new Test(); i.add("12");//添加的就是 String类型 Test t = new Test(); //无约束 t.add(1.1);//没有约束 ,添加的就是Objetc类型 show("我叫张三"); show(12); } }
2.3. 课堂练习
2.3.1 练习:forech 使用
foreach
增强型的使用,与for()
循环比较。- 遍历数组,和 遍历集合。
public class Test2_Generics {
public static void main(String[] args) {
/*1. foreach 遍历效率高,缺点无法控制打印步长。
不需要控制变量长度时,选择foreach,效率高。
语法格式: for(数据类型 遍历名称:遍历对象){}
*/
//1.泛型和数组很像。
String[] arr = new String[5];
arr[0] ="1";
arr[3] ="10";
//arr[4] =20; //这样是不允许的。
//1.1 数组遍历方式
for (int i = 0; i <arr.length ; i++) {
System.out.println(arr[i]);//能控制打印哪个元数
}
//1.2 foreach 遍历
for (String s: arr) {
if (s!=null){//控制不打印无效元数
System.out.println(s); //直接全部打印
}
}
/*2. 集合遍历
*/
Collection c = new ArrayList();//没有泛型约束
c.add(1);
c.add("你好");
//2.1 遍历,因为没有约束 添加的就是Object类型
for (Object o : c) {
System.out.println(o);
}
}
}
2.3.2 练习:打印方法–数组
- 将泛型定义在方法上。
- 泛型增强的通用性,
foreach(E[ ] e)
- 泛型增强的通用性,
public static void main(String[] args) {
/*1.打印数组
*/
String[] s_name ={"张三","李四","王五"};
println_String(s_name);
//1.1 打印double int 方法
Double[] d_salary = {18.0,28.0,38.0,48.0};
Integer[] i_age = {18,28,38,48};
// println_String(i_age); //为什么不行?怎么办,,这样又写很多个方法
/*2.泛型的使用
*/
println_E(d_salary);
println_E(i_age);
}
/**
* 泛型定义在方法上
* @param e
*/
private static <E>void println_E(E[] e) {
//泛型通配符使用
for (E res:e) {
System.out.println(res);
}
}
/**
* 打印数组的方法
* @param s_name
*/
private static void println_String(String[] s_name) {
for (String s:s_name) {
System.out.println(s);
}
}
2.4 知识扩展: 泛型的上下界通配符
2.4.1 无界限
- 通配符有很多 T 就可以了 为什么还有 ?;
<T>
代表的声明的意思,<?>
具体是怎么使用。- 主要是为了 可读性。
public static void main(String[] args) {
//1.无界限通配符,打印集合
Collection<Integer> c = new ArrayList();
c.add(18);
c.add(18);
Collection<String> c2 = new ArrayList();
c2.add("你好");
c2.add("中国");
//2. 可以任意打印
print(c);
print(c2);
}
/**
* 无界限通配符<?>
* 打印集合
* @param c
*/
private static void print(Collection<?> c) {
Iterator<?> iterator = c.iterator();
while(iterator.hasNext()){
Object next = iterator.next(); //默认就是Object
System.out.println(next);
}
}
2.4.2 上界通配符
- 类型名称
<? extends 类 >
对象名称- 只能接受本类 或其子类。
public class Test {
public static void main(String[] args) {
Collection<Girl> c = new ArrayList();
Collection<Boy> c2 = new ArrayList();
Collection<Person> c3 = new ArrayList();
Collection<Object> c4 = new ArrayList();
//print(c4); //Object 不可以
}
//1.上界通配符,即上限
public static void print(Collection<? extends Person> c){
System.out.println(c);
}
}
class Person{}
class Girl extends Person{} //继承父类
class Boy extends Person{} // 继承父类
2.4.3 下界通配符
- 类型名称
<? super 类 >
对象名称- 只能接收本类型 及其父类。
public class Test {
public static void main(String[] args) {
Collection<Girl> c = new ArrayList();
Collection<Boy> c2 = new ArrayList();
Collection<Person> c3 = new ArrayList();
Collection<Object> c4 = new ArrayList();
// print(c); // 子类不可以
// print(c2); // 子类不可以
print(c4); // 父类可以
}
//1.下界通配符,即下限。
public static void print(Collection<? super Person> c){
System.out.println(c);
}
}
class Person{}
class Girl extends Person{} //继承父类
class Boy extends Person{} // 继承父类