学习集合的目标 |
---|
1. 会使用集合存储数据 |
2. 会遍历集合,把数据取出来 |
3.掌握每种集合的特性 |
一、集合常用方法
boolean add(E e); 向集合中添加元素
boolean remove(E e); 删除集合中的某个元素
void clear(); 清除集合所有的元素
boolean contains(E e); 判断集合中是否包含某个元素
boolean isEmpty(); 判读那集合是否为空
int size(); 获取集合的长度
Object[] toArray(); 将集合转换成一个数组
package com.itheima.demo06;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
public class Test01 {
public static void main(String[] args) {
//创建集合对象
Collection<String> coll = new ArrayList<>();
// boolean add(E e); 向集合中添加元素
coll.add("hello");
coll.add("world");
coll.add("heima");
coll.add("java");
System.out.println(coll); //[hello,world,heima,java]
//boolean remove(E e); 删除集合中的某个元素
boolean result1 = coll.remove("hello");
System.out.println(result1); // true
System.out.println(coll); //[world,heima,java]
//void clear(); 清除集合所有的元素
coll.clear();
System.out.println(coll); //true/false
//boolean contains(E e); 判断集合中是否包含某个元素
boolean result2 = coll.contains("java");
System.out.println("是否包含此元素?" + result2); //true/false
//boolean isEmpty(); 判断那个集合是否为空
boolean result3 = coll.isEmpty();
System.out.println("是否为空?"+result3); //true/false
//int size(); 获取集合的长度
System.out.println("集合长度:"+coll.size());//集合长度:数字
//Object[] toArray(); 将集合转换成一个数组
Object[] arr = coll.toArray();
//有了这个数组之后就可以进行遍历数组
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]); //打印格式如:hello<br>world<br>heima<br>java<br>
}
}
}
二、Iterator迭代器
Iterator接口
* java.util.Iterator接口:迭代器(对集合进行遍历)
* 有两个常用的方法:
boolean hasNext();
如果仍有元素可以迭代,则返回 true。
判断集合中还有没有下一个元素,有就返回true,没有就返回false
E next();
返回迭代的下一元素
取出集合中的下一元素
* Iterator迭代器,是一个接口,我们无法直接使用,需要使用Iterator接口的实现类对象,获取实现类的方式比较特殊。
* Collection接口中有一个方法,叫iterator(),这个方法返回的就是迭代器的实现类对象。
Iterator<E> iterator() 返回在此 collection 的元素上进行迭代的迭代器。
* 迭代器的使用步骤(重点):
1. 使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
2. 使用Iterator接口中的方法nasNext判断还有没有下一元素
3. 使用Iterator接口中的方法next取出集合中的下一元素
package com.itheima.demo07;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo01Iterator {
public static void main(String[] args) {
//创建一个集合
Collection<String> coll = new ArrayList<>();
//往集合中添加数组
coll.add("姚明");
coll.add("科比");
coll.add("麦迪");
coll.add("詹姆斯");
coll.add("艾弗森");
/*
1. 使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
注意:
Iterator<E> 接口也是有泛型的,迭代器的泛型跟着集合走,集合是什么泛型,迭代器就是什么泛型。
*/
//多态: 接口 = 实现类对象
Iterator<String> it = coll.iterator();
/*
发现使用迭代器取出集合中元素的代码,是一个重复的过程
所以我们可以使用循环优化
不知道集合中有多少元素,使用while循环
循环结束的条件,hasNext方法返回false
*/
while (it.hasNext()) { //while循环实现迭代器
String e = it.next();
System.out.println(e); //[集合中的数据]
}
System.out.println("====================");
for (Iterator<String> it2 = coll.iterator(); it2.hasNext(); ) { //for循环实现迭代器
String e = it2.next();
System.out.println(e);
}
/*
// 2. 使用Iterator接口中的方法hasNext判断还有没有下一元素
boolean b = it.hasNext();
System.out.println(b); //true/false
// 3. 使用Iterator接口中的方法next取出集合中的下一元素
String s = it.next();
System.out.println(s); //姚明
b = it.hasNext();
System.out.println(b); //true
s = it.next();
System.out.println(s); //科比
b = it.hasNext();
System.out.println(b); //true
s = it.next();
System.out.println(s); //麦迪
b = it.hasNext();
System.out.println(b); //true
s = it.next();
System.out.println(s); //詹姆斯
b = it.hasNext();
System.out.println(b); //true
s = it.next();
System.out.println(s); //艾弗森
b = it.hasNext();
System.out.println(b); //没有元素,返回false
s = it.next();
System.out.println(s); //没有元素,再取出元素,会抛出异常: NoSuchElementException
*/
}
}
三、增强for循环
* 增强for循环:底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写
是JDK1.5之后出现的新特性
Collection<E>extends Iterable<E>: 所有的单列集合都可以使用增强for
public interface Iterable<T> 实现这个接口允许对象成为 “foreach” 语句的目标。
* 增强for循环:用来遍历集合和数组
格式:
for(集合/数组的数据类型 变量名:集合名/数组名){
sort(变量名);
}
package com.itheima.demo07;
import java.util.ArrayList;
import java.util.Arrays;
public class Demo02Foreach {
public static void main(String[] args){
demo01();
}
//使用增强for循环遍历集合
private static void demo02(){
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
for(String s : list){
System.out.println(s);
}
}
//使用增强for循环遍历数组
private static void demo01() {
int[] arr = {1,2,3,4,5};
for (int i:arr) {
System.out.println(i);
}
}
}
四、泛型
为什么要使用泛型
把运行时异常提前到编译时异常
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?
顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),
然后在使用/调用时传入具体的类型(类型实参)。
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,
操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);
for(int i = 0; i< arrayList.size();i++){
String item = (String)arrayList.get(i);
Log.d("泛型测试","item = " + item);
}
毫无疑问,上述程序的运行结果将会以崩溃的方式结束:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。
我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。
List<String> arrayList = new ArrayList<String>();
...
//arrayList.add(100); 在编译阶段,编译器就会报错
* 泛型的特性
泛型只在编译阶段有效。看下面的代码:
List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();
Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();
if(classStringArrayList.equals(classIntegerArrayList)){
Log.d("泛型测试","类型相同");
}
输出结果:D/泛型测试: 类型相同。
通过上面的例子可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。
对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型
使用泛型的好处与弊端
* 创建集合对象,使用泛型:
好处:
1. 避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
2. 把运行期异常(代码运行之后会抛出的异常),提升到了编译期(写代码的时候会报错)
弊端:
泛型是什么类型,只能存储什么类型的数据
* 创建集合对象,不使用泛型:
好处:
集合不使用泛型,默认的类型就是Object类型,可以存储任意类型的数据
弊端:
不安全,会引发异常
package com.itheima.demo07;
import java.util.ArrayList;
import java.util.Iterator;
public class demo03Generic {
public static void main(String[] args) {
show01();
}
// 创建集合对象,使用泛型
private static void show02() {
ArrayList<String> list = new ArrayList<>();
list.add("abc");
// list.add(1);//编译期报错,add(java.lang.String)in ArrayList cannot be applied to (int)
//使用迭代器遍历List集合
Iterator<String> it = list.iterator();
while (it.hasNext()) ;
String s = it.next();
System.out.println(s + "->"+s.length());
}
/*
创建集合对象,不使用泛型
*/
private static void show01() {
ArrayList list = new ArrayList();
list.add("abc");
list.add(1);
//使用迭代器遍历List集合
//获取迭代器
Iterator it = list.iterator();
//使用迭代器中的方法 hasNext和 next遍历集合
while (it.hasNext()) {
//取出元素也是Object类型
Object obj = it.next();
System.out.println(obj);
//想要使用String类特有的方法,length获取字符串的长度
//需要向下转型
//会抛出异常:ClassCastException: java.lang.Integer cannot be cast to java.lang.String
String s = (String) obj;
System.out.println(s.length());
}
}
}
含有泛型的类
* 当把类实例化为对象,即new 一个对象的时候,泛型就有了具体的类型
* 定义一个含有泛型的类,模拟ArrayList集合
泛型是一个位置的数据类型,当我们不确定使用什么数据类型的时候,可以使用泛型
泛型可以接收任意的数据类型,可以使用Integer,String,Student...
创建对象的时候可以确定泛型的数据类型
package com.itheima.demo07;
public class GenericClass<E> { //参数类型由 String改为 E
private E name;
public E getName() {
return name;
}
public void setName(E name) {
this.name = name;
}
}
package com.itheima.demo07;
import java.util.Objects;
public class Demo02GenericClass {
public static void main(String[] args){
//不写泛型默认为Object类型
GenericClass gc = new GenericClass(); //gc为Object类型-GenericClass类型
gc.setName("只能是字符串");
Object obj = gc.getName();
System.out.println(name); //只能是字符串
//创建GenericClass对象,泛型使用Integer类型,(创建对象的时候,确定泛型类型)
GenericClass<Integer> gc2 = new GenericClass<>();
gc2.setName(1);
Integer name = gc2.getName();
System.out.println(name);
//创建GenericClass对象,泛型使用String类型
GenericClass<String> gc3 = new GenericClass<>();
gc3.setName("小明");
String name1 = gc3.getName();
System.out.println(name1);
}
}
含有泛型的方法
* 定义含有泛型的方法:泛型定义在方法的修饰符和返回值类型之间
* 格式:
修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)){
方法体;
}
含有泛型的方法,在调用方法的时候确定泛型的数据类型
传递什么类型的参数,泛型就是什么类型
package com.itheima.demo07;
public class GenericMethod {
//定义一个含有泛型的方法
public <M> void method01(M m){
System.out.println(m);
}
//定义一个含有泛型的静态方法
public static <S> void method02(S s){
System.out.println(s);
}
}
package com.itheima.demo07;
/*
测试含有泛型的方法
*/
public class Demo03GenericMethod {
public static void main(String[] args) {
//首先创建爱你GenericMethod对象
GenericMethod gm = new GenericMethod();
/*
调用含有泛型的方法method
传递什么类型,泛型就是什么类型
*/
gm.method01(10);
gm.method01("abc");
gm.method01(8.8);
gm.method01(true);
gm.method02("静态方法,不建议创建对象使用");
//静态方法,通过类名,方法名(参数)可以直接使用
GenericMethod.method02("静态方法");
GenericMethod.method02(1);
}
}
含有泛型的接口
接口使用什么泛型,实现类就使用什么泛型,类跟着接口走。
package com.itheima.demo07;
/*
定义含有泛型的接口
*/
public interface GenericInterface<I> {
public abstract void method(I i);
}
package com.itheima.demo07;
/*
含有泛型的接口第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走
就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型
public interface list<E>{
boolean add(E e);
E get(int index);
}
public class ArrayList<E> implement List<E>{
public boolean add(E e) {}
public E get(int index) {}
}
*/
public class GenericInterfaceImpl2<I> implements GenericInterface<I>{
@Override
public void method(I i) {
System.out.println(i);
}
}
package com.itheima.demo07;
/*
测试含有泛型的接口
*/
public class Demo04GenericInterface {
public static void main(String[] args){
//创建GenericInterfaceImpl对象
GenericInterfaceImpl1 gi1 = new GenericInterfaceImpl1();
gi1.method("字符串"); //Object类型
//创建GenericInterfaceImpl2对象
GenericInterfaceImpl2<Integer> gi2 = new GenericInterfaceImpl2<>(); //Integer类型
gi2.method(10);
GenericInterfaceImpl2<Double> gi3 = new GenericInterfaceImpl2<>();
gi3.method(8.8);
}
}
泛型的通配符
* 泛型的通配符:
?:代表任意的数据类型
* 使用方式:
不能创建对象使用
只能作为方法的参数使用
package com.itheima.demo07;
import java.util.ArrayList;
import java.util.Iterator;
public class Demo05Generic {
public static void main(String[] args) {
ArrayList<Integer> list01 = new ArrayList<>(); //数组集合对象
list01.add(1);
list01.add(2);
ArrayList<String> list02 = new ArrayList<>();
list02.add("a");
list02.add("b");
printArray(list01); //1 2
printArray(list02); //a b
// ArrayList<?> list03 = new ArrayList<?>(); //定义的时候不能用泛型的通配符
}
/*
定义一个方法,能遍历所有类型的ArrayList集合
这时候我们不知道ArrayList集合使用什么数据类型,可以使用泛型的通配符?来接收数据类型
注意:
泛型没有继承的概念
*/
public static void printArray(ArrayList<?> list){
//使用迭代器遍历集合
Iterator<?> it = list.iterator();
while (it.hasNext()){
//it.next()方法,取出的元素是Object,可以接收任意的数据类型
Object o = it.next();
System.out.println(o);
}
}
}
斗地主综合案例
package com.itheima.demo08;
/*
斗地主综合案例:
1. 准备牌
2. 洗牌
3. 发牌
4. 看牌
*/
import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;
public class DouDiZhu {
public static void main(String[] args) {
//1. 准备牌
// 定义一个存储54张牌的ArrayList集合,泛型使用String
ArrayList<String> poker = new ArrayList<>();
//定义两个数组,一个数组存储牌的花色,一个数组存储牌的序号
String[] colors = {"♠", "♥", "♣", "♦"};
String[] numbers = {"2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3"};
//先把大王和小王存储到poker集合中
poker.add("大王");
poker.add("小王");
//循环嵌套遍历两个数组,组装52张牌
for (String number : numbers) {
for (String color : colors) {
//System.out.println(color+number);
//把组装好的牌存储到poker集合中
poker.add(color+number);
}
}
System.out.println(poker);
/*
2. 洗牌
使用集合的工具类Collections中的方法
static void shuffle(List<?> list) 使用默认随机源对指定列表进行置换
*/
Collections.shuffle(poker);
//System.out.println(poker); //每次输出结果都不一样,可用此方法进行洗牌
/*
3. 发牌
*/
//定义4个集合,存储玩家的牌和底牌
ArrayList<String> player01 = new ArrayList<>();
ArrayList<String> player02 = new ArrayList<>();
ArrayList<String> player03 = new ArrayList<>();
ArrayList<String> diPai = new ArrayList<>();
/*遍历poker集合,获取每一个集合
使用poker集合的索引%3 给3个玩家轮流发牌
剩余3张牌给底牌
注意:
先判断底牌(i >= 51),否则牌就发没了
*/
for(int i = 0; i < poker.size(); i++){
//获取每一张牌
String p = poker.get(i);
//轮流发牌
if(i >= 51){
//改底牌发牌
diPai.add(p);
}else if(i % 3 == 0){
//给玩家1发牌
player01.add(p);
}else if(i % 3 == 1){
//给玩家2发牌
player02.add(p);
}else if(i % 3 == 2){
//给玩家3发牌
player03.add(p);
}
}
//看牌
System.out.println("刘德华:" + player01);
System.out.println("周润发:" + player02);
System.out.println("周星驰:" + player03);
System.out.println("底牌:" + diPai);
}
}