day13
1. 集合的概述
- 集合是什么? 为什么要使用集合?
a : 集合是什么 : 集合就是一个容器, 可以装载很多的数据, 所有的集合中存储的数据全部都是引用数据类型, 基本数据类型通过自动装箱也可以直接存储在集合中
b : 为什么要使用集合 : 数组容器是定长容器, 集合是可变长度的容器, 集合有初始容量大小, 但是当向集合中添加数据超出容器之后, 集合底层自动进行扩容
- 集合体系结构:
2. Collection集合
2.1 Collection介绍和常用方法
1. Collection 接口 : 单列集合顶层父接口, 因此接口中包含了List和Set集合中所有共有方法功能. 来自于java.uitl包
2. Collection是接口, 不能new对象, 于是需要一个实现类ArrayList
接口的多态性 : Collection coll = new ArrayList();
- Collection中的常用方法功能:
1) add(Object obj) : 表示向集合中添加元素obj, 末尾添加
2) remove(Object obj) : 表示删除集合中的obj元素, 删除成功返回true. 删除失败返回false
3) clear() : 删除集合中的所有元素, 但是集合本身仍然存在, 没有返回值类型
4) isEmpty() : 集合不包含元素,则返回 true,否则返回false
5) contains(Object obj) : 验证集合中是否包含参数obj, 包含返回true, 不包含返回false
6) size() : 表示获取到集合中的元素个数, 返回int结果
代码
package com.ujiuye.collection; import java.util.ArrayList; import java.util.Collection; public class Demo01_Collection中的常用方法 { public static void main(String[] args) { // 1. 定义出一个集合容器 Collection coll = new ArrayList(); // 2. add(Object obj) : 表示向集合中添加元素obj, 末尾添加 coll.add("a"); coll.add("b"); coll.add("12"); coll.add("hello");
boolean boo2 = coll.contains("a"); System.out.println(boo2);// true
System.out.println(coll);// [a, b, 12, hello]
// 2.remove(Object obj) : 表示删除集合中的obj元素, 删除成功返回true. 删除失败返回false coll.remove("12"); System.out.println(coll);// [a, b, hello] System.out.println(coll.size());// 3
// 3. clear() : 删除集合中的所有元素, 但是集合本身仍然存在, 没有返回值类型 coll.clear(); System.out.println(coll);// []
// 4. isEmpty() : collection 不包含元素,则返回 true,否则返回false boolean boo = coll.isEmpty(); System.out.println(boo);// true
// 5.contains(Object obj) : 验证集合中是否包含参数boj, 包含返回true, 不包含返回false boolean boo1 = coll.contains("a"); System.out.println(boo1);// false
// 6.size() : 表示获取到集合中的元素个数, 返回int结果 System.out.println(coll.size());// 0 } } |
2.2 Collection集合第一种遍历
集合转数组 : 通过遍历数组相当于遍历集合中的元素内容
- toArray() : 将集合中的所有元素复制到一个Object[] 类型数组中, 返回值类型Object[]
代码
package com.ujiuye.collection; import java.util.ArrayList; import java.util.Collection; public class Demo02_Collection第一种遍历 { public static void main(String[] args) { // 1. 定义出一个集合容器 Collection coll = new ArrayList(); coll.add(12); coll.add(6); coll.add(-7); coll.add(0);
// 2. 将coll集合转换成Object[]数组 Object[] objArr = coll.toArray();
// 3. 遍历数组相当于遍历集合中的元素 for(int index = 0; index < objArr.length ; index++) { // 4. 做多态的向下转型, 将Object类型数据恢复成Integer类型 Integer i = (Integer)objArr[index]; System.out.println(i); } } } |
2.3 Collection中带All的方法
1. addAll(Collection c) : 将参数集合c中的所有元素添加到方法调用集合中
2. removeAll(Collection c) : 将参数集合c中的所有元素,从方法调用集合中进行删除
3. containsAll(Collection c) : 验证方法调用集合中的元素,是否完全包含参数c集合中的所有元素,完全包含返回true
4. retainAll(Collection c) : 将方法调用集合和参数集合c中交集(相同元素), 保留在方法调用集合中
代码
package com.ujiuye.collection; import java.util.ArrayList; import java.util.Collection; public class Demo03_Collection中带All方法 { public static void main(String[] args) { Collection c = new ArrayList(); c.add("a"); c.add("b");
Collection c1 = new ArrayList(); c1.add("b"); c1.add("hello");
// 1. addAll(Collection c) : 将参数集合c中的所有元素添加到方法调用集合中 c.addAll(c1); System.out.println(c);// [a, b, b, hello] // 2. removeAll(Collection c) : 将参数集合c中的所有元素,从方法调用集合中进行删除 Collection c2 = new ArrayList(); c2.add("b"); c2.add("15"); c.removeAll(c2); System.out.println(c);// [a, hello]
// 3.containsAll(Collection c) : 验证方法调用集合中的元素,是否完全包含参数c集合中的所以元素.,完全包含返回true Collection c3 = new ArrayList(); c3.add("a"); boolean boo = c.containsAll(c3); System.out.println(boo);// true
// c---> [a, hello] c1--->[b, hello] System.out.println(c.containsAll(c1));// false
// 4.retainAll(Collection c) : 将方法调用集合和参数集合c中交集(相同元素), 保留在方法调用集合中 Collection c4 = new ArrayList(); c4.add("hello"); c4.add("world"); Collection c5 = new ArrayList(); c5.add("hello"); c5.add("my"); Collection c6 = new ArrayList(); c6.add("me"); c6.add("my");
/*c4.retainAll(c5); System.out.println(c4);//[hello] */ c4.retainAll(c6); System.out.println(c4);//[] } } |
2.4 Collection第二种遍历方式迭代器
1. 迭代 : 表示从一个到下一个的过程, 理解为遍历
2. 迭代器 : 专门使用于将集合中的元素, 一个一个获取到的对象
- 获取到集合的迭代器对象:
Collection中有一个方法功能
iterator() : 获取到当前集合的迭代器对象, 返回值类型Iterator 迭代器接口, 接口作为方法的返回值类型, 实际上方法返回的是接口的一个实现类对象
- 迭代器中的方法遍历集合元素:
1) hasNext() : 判断当前集合中是否还具有下一个元素, 如果有下一个元素,返回true, 否则返回false
2) next() : 获取到集合中的下一个元素
3) remove() : 删除当前正在迭代的集合元素
- 迭代器的使用原理和注意事项:
注意事项: 当集合中所有元素已经遍历完毕,还使用next继续获取集合下一个元素
报出异常 : NoSuchElementException 没有这个元素异常
代码
package com.ujiuye.collection; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class Demo04_Collection迭代器遍历 { public static void main(String[] args) { // 1. 创建出一个集合 Collection c = new ArrayList(); c.add("hello"); c.add("h"); c.add("llo"); c.add("world");
// 2. 获取到c集合的迭代器对象 Iterator it = c.iterator(); // 3. hasNext: 判断当前集合中是否还具有下一个元素, 如果有下一个元素,返回true, 否则返回false while(it.hasNext()) { // 4. next() : 获取到集合中的下一个元素 String s = (String)it.next(); System.out.println(s); }
// 4. 当集合中所有元素已经遍历完毕,还使用next继续获取集合下一个元素 // 报出异常 : NoSuchElementException 没有这个元素异常 String s1 = (String)it.next(); System.out.println(s1);
} } |
3.List集合
3.1 List集合的介绍和特有方法
1. List : Collection集合子接口, 来自于java.util包
特有:
1) 元素存取有序
2) 每一个元素有索引
3) 可存储重复元素
2.List接口,需要实现类,ArrayList
3. List集合中的特有方法:
1) add(int index, Object obj) : 将obj数据添加到集合的指定索引index位置处,没有返回值类型
2) remove(int index) : 表示将集合中index索引位置的元素删除,返回值类型为Object, 返回就是当前被删除的元素本身
3) set(int index , Object ele): 将集合index索引位置上的元素修改成ele , 返回是的被替换掉元素本身
4) get(int index) : 将集合中index索引位置的元素获取到
代码
package com.ujiuye.list; import java.util.ArrayList; import java.util.List; public class Demo01_List特有方法 { public static void main(String[] args) { List list = new ArrayList(); list.add("a"); list.add("b"); // 1. add(int index, Object obj) : 将obj数据添加到集合的指定索引index位置处,没有返回值类型 list.add(0,"first");
System.out.println(list);//[first, a, b]
// 2.remove(int index) : 表示将集合中index索引位置的元素删除,返回值类型为Object, 返回就是当前被删除的元素本身 String s = (String)list.remove(1); System.out.println(s);// a System.out.println(list);//[first, b]
// 3. set(int index , Object ele): 将集合index索引位置上的元素修改成ele , 返回是的呗替换掉元素本身 String s1 = (String)list.set(1, "second"); System.out.println(s1);// b System.out.println(list);// [first, second]
// 4. get(int index) : 将集合中index索引位置的元素获取到 String s2 = (String)list.get(0); System.out.println(s2);// first String s3 = (String)list.get(1); System.out.println(s3);// first } } |
3.2 List集合第三种遍历
List集合可以通过索引进行遍历:
1) List集合索引范围 : 0---size()-1
2) 可以获取到集合中每一个索引, 结合List特有方法get(int index), 可以进行List集合的遍历
代码
package com.ujiuye.list; import java.util.ArrayList; import java.util.List; public class Demo02_List第三种遍历 { public static void main(String[] args) { List list = new ArrayList();
list.add(3.14);// Double list.add(12.0); list.add(9.9);
// 1. 设计出一个循环, 获取到集合list中的每一个索引 for(int index = 0; index < list.size(); index++) { // 2. 使用get(index) 获取到集合中的每一个元素 Double d = (Double)list.get(index); System.out.println(d); } } } |
3.3 并发修改异常
- ConcurrentModificationException
并发 修改 异常
- 并发修改异常的发生原因:
a : 使用迭代器进行集合遍历同时, 一边遍历, 一边修改集合中的元素, 迭代器, 有可能无法保证集合的迭代顺序,这是一种风险, 于是抛出并发修改异常
使用迭代器遍历同时向集合中添加元素或者删除元素都有可能会发生并发修改异常
- 如何避免并发修改异常:
a : 不要使用迭代器进行遍历, 使用List集合特有的索引遍历方式,可以避免并发修改异常
b : List接口中有特有迭代器, ListIterator,在原有 Iterator 迭代器接口提供的正常操作基础上,该迭代器还允许向集合中新增元素,替换元素等操作
List中特有方法 :
listIterator(); 获取到特殊的迭代器对象, 返回值类型ListIterator
ListIterator接口中包含方法 : 可以对于当前正在遍历的集合进行修改, 可以向集合中添加元素, 可以删除元素, 可以修改元素
案例 : 定义出一个集合,集合中存储字符串, 使用迭代器遍历集合, 遍历时, 如果有字符串的内容为Hello, 那么在集合中添加字符串 “World”
代码
package com.ujiuye.list; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ListIterator; public class Demo03_并发修改异常 { public static void main(String[] args) { List list = new ArrayList(); list.add("a"); list.add("Hello"); list.add("b");
// addEleToList(list); // addEleToListFor(list); addEleToListIterator(list); } /* 案例 : 定义出一个集合,集合中存储字符串, 使用迭代器遍历集合, 遍历时, 如果有字符串的内容为"Hello", 那么在集合中添加字符串 "World"*/ // 1. 发生并发修改异常 public static void addEleToList(List list) { // 1. 获取到list集合迭代器对象 Iterator it = list.iterator(); // 2. 遍历集合中元素 while(it.hasNext()) { String s = (String)it.next(); // 3. 判断获取的字符串是否匹配Hello if("Hello".equals(s)) { // 4. 在集合中添加world list.add("World"); } } System.out.println(list); } // 2. List集合特有方法解决问题 public static void addEleToListFor(List list) { for(int index = 0; index < list.size(); index ++) { String s = (String)list.get(index); if("Hello".equals(s)) { list.add("World"); } } System.out.println(list); } // 3. List集合特有迭代器解决问题 public static void addEleToListIterator(List list) { ListIterator it = list.listIterator(); while(it.hasNext()) { String s = (String)it.next(); if("Hello".equals(s)) { // 使用迭代器向集合中添加元素 it.add("World"); } } System.out.println(list); } } |
3.4 List的实现类
List接口根据实现类底层数据结构不同, 行为有不同实现
- ArrayList : 底层数组结构, 查询快, 增删相对慢
- LinkedList : 底层链表结构, 查询慢, 增删相对快
3.4.1 ArrayList实现类
ArrayList : 底层数组结构, 查询快, 增删相对慢
3.4.2 LinkedList实现类
LinkedList : 底层链表结构, 查询慢, 增删相对快
LinkedList 节点实现, 链式存储, 节点的实现方式 : 每一个节点有两块区域, 第一块区域用于记录元素值, 第二块区域记录下一个元素的地址, 节点存储的元素其地址不一定连续, 节点可以利用内存中的零散区域
LinkedList集合记录首尾两个元素地址:
因此LinkedList中有特有方法功能, 直接对于首尾元素进行操作, 效率很高
代码
package com.ujiuye.list; import java.util.LinkedList; public class Demo04_LinkedList { public static void main(String[] args) { LinkedList list = new LinkedList(); list.add("a");
list.addFirst("fir"); System.out.println(list);// [fir, a]
list.addLast("end"); System.out.println(list);// [fir, a, end]
System.out.println(list.getFirst());// fir System.out.println(list.getLast());// end
list.removeFirst(); System.out.println(list);// [a, end]
list.removeLast(); System.out.println(list);// [a] } } |
4. 泛型
4.1 泛型的概述和使用
- 泛型 : 用于表示广泛的类型, 如果定义一个类时, 类中的某些方法功能参数列表或者返回值类型无法确定, 可以使用泛型来表示无法确定的数据类型
举例 : ArrayList--->集合--->可以存储引用数据类型--->但是不能确定存储的引用类型到底 是什么具体类型, 可能 String, Integer,Double , Person...
因此用于向集合中添加数据的add方法功能, 不知道参数具体类型, 使用泛型
- 泛型的使用场景:
实际开发中, 泛型通常使用在集合中比较多, 迭代器也有泛型
泛型类的定义 : 在类型后面<大写字母>, 这个大写字母就用于表示广泛的类型
Collection<E>
List<E>
ArrayList<E>
Iterator<E>
- 泛型类型的确定:
需要在合适情况下确定出泛型的具体类型, 于是创建一个类型的对象时, 类型上的泛型需要给出具体的类型
类上定义的泛型,在整个类中当做一个已知类型使用,可以使用在类的方法功能中
- 泛型好处:
1) 提高代码的安全性, 将运行时期才会发生的问题提前到代码编译时期
2) 简化代码, 省掉转型麻烦
- 泛型使用注意事项:
1) 一致性: 创建出带有泛型类型对象时, 需要泛型前后保持一致
ArrayList<String> list1 = new ArrayList<String>();
2) 泛型推断, 从JDK1.7版本开始, 后面的泛型可以不写, 只写<>, 很像菱形, 称为菱形泛型, 后面的泛型不写,默认与前面泛型保持一致
代码
package com.ujiuye.list; import java.util.ArrayList; import java.util.Iterator; public class Demo01_泛型 { public static void main(String[] args) { /*ArrayList list = new ArrayList(); list.add("abc");// String list.add(12);// Integer list.add(3.14);// Double
for(int index = 0; index < list.size(); index++) { Object obj = list.get(index); // 类型转换成报错 String s = (String)obj; System.out.println(s); }*/
// 使用泛型创建出一个集合 // 当创建ArrayList集合时, 集合的泛型确定成了String类型 // list集合中只能存储String类型数据 ArrayList<String> list1 = new ArrayList<String>(); list1.add("a"); list1.add("b"); list1.add("c"); list1.add("d");
for(int index = 0; index < list1.size(); index++) { String s = list1.get(index); }
// 2)从JDK1.7版本开始, 后面的泛型可以不写, 只写<>, // 很像菱形, 称为菱形泛型, 泛型推断, 后面的泛型不写,默认与前面泛型保持一致 ArrayList<String> list2 = new ArrayList<>();
Iterator<String> it = list2.iterator(); while(it.hasNext()) { String s = it.next(); } } } |
4.2 泛型类
- 定义类时,类上带有泛型, 称为泛型类
修饰符 class 类名<泛型类型1, 泛型类型2...>{}
举例 : public class ArrayList<E>{}
注意 : 泛型类型在定义时, 可以使用任意一个大写字母表示, 比较常见E--->Element
K---Key V---Value T---Type
- 泛型类型的确定:
创建一个带有泛型类对象时, 泛型确定出具体的类型
举例 : ArrayList<String> list = new ArrayList<>();
- 类上定义的泛型, 在类中,可以做为一个已知类型使用
举例 : ArrayList中的add方法
public boolean add(E e){}
代码
package com.ujiuye.fanxing; // 定义泛型类 public class Homework4<T> { // 类上的泛型T在整个类中,可以违一个已知数据类型使用 // 参数列表为泛型类型的数组, 功能为该数组中的0索引和1索引元素进行替换 public void changeEle(T[] arr, int index, int index1) { // 1. 将index索引原值保留下来 T temp = arr[index]; // 2. 将index1索引的值赋值给index索引位元素 arr[index] = arr[index1]; // 3. 将temp值赋值给index1索引位置元素 arr[index1] = temp; } } |
package com.ujiuye.fanxing; import java.util.Arrays; public class TestFanxing { public static void main(String[] args) { // 1. Homework4类型的对象h中的所有的泛型T,目前已经被确定为String类型 Homework4<String> h = new Homework4<>(); String[] arr = {"1","2","3"}; h.changeEle(arr, 0, 1); System.out.println(Arrays.toString(arr)); } } |
4.3 泛型方法
- 方法上带有泛型使用,这个方法称为泛型方法
- 方法根据是否是静态对于泛型的使用不同
1) 泛型方法的定义:
修饰符<泛型类型1, 泛型类型2...> 返回值类型 方法名(参数列表){
方法体;
}
2) 非静态方法 : 可以在方法上自己定义泛型, 也可以使用类上声明的泛型
3) 静态方法 : 因为静态属于类, 不属于任何人像,并且优先于所有对象进入到内存中; 因此静态方法不能使用类上的泛型, 如果需要使用,只能在静态方法上自己定义泛型使用
4) 方法上定义的泛型只能在当前方法中使用
代码
package com.ujiuye.fanxing; import java.util.ArrayList; // 定义泛型类 public class Homework4<T> { // 类上的泛型T在整个类中,可以违一个已知数据类型使用 // 参数列表为泛型类型的数组, 功能为该数组中的0索引和1索引元素进行替换 public void changeEle(T[] arr, int index, int index1) { // 1. 将index索引原值保留下来 T temp = arr[index]; // 2. 将index1索引的值赋值给index索引位元素 arr[index] = arr[index1]; // 3. 将temp值赋值给index1索引位置元素 arr[index1] = temp; } public static<W> void changeList(ArrayList<W> list, W w) { //Cannot make a static reference to the non-static type T list.add(w); System.out.println(list); } } |
package com.ujiuye.fanxing; import java.util.ArrayList; import java.util.Arrays; public class TestFanxing { public static void main(String[] args) { // 1. Homework4类型的对象h中的所有的泛型T,目前已经被确定为String类型 Homework4<String> h = new Homework4<>(); String[] arr = {"1","2","3"}; h.changeEle(arr, 0, 1); System.out.println(Arrays.toString(arr)); ArrayList<Integer> list = new ArrayList<>(); // public static<W> void changeList(ArrayList<W> list, W w) { // W泛型在方法调用时,确定具体类型, 目前W泛型为 Integer类型 Homework4.changeList(list, 12); } } |
4.4 泛型通配符(了解)
- ? : 表示匹配任意泛型类型
举例 : removeAll(Collection<?> c) : 表示参数列表c给出的集合带有的泛型可以是任意类型, 可以是任意类型数据的集合
- ? extends E : 表示需要提供泛型类型是 ? , ? 需要是E类型本身类型或者是E类型的子类泛型
举例 : addAll(Collection<? extends E> c)
- ? super T : 表示需要提供泛型类型是?, ? ? 需要是T类型本身类型或者T类型的父类类型
代码
package com.ujiuye.fanxing; import java.util.ArrayList; public class Demo01_泛型通配符 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("a");
ArrayList<Integer> list1 = new ArrayList<>(); list1.add(12); // 1) list集合在删除list1中的交集元素, 但是list1集合中存储的数据类型不做要求 list.removeAll(list1);
// 2) addAll(Collection<? extends E> c) : 参数集合c中的数据类型, 需要是方法调用集合泛型E相同的类型 // ,或者是E类型的子类 // list.add(list1);
ArrayList<Object> list2 = new ArrayList<>(); list2.addAll(list); } } |