第1章集合
1.1集合
1.1.1集合介绍
前面的学习,我们知道数据多了,使用数组存放。而且数组中存放的都是基本类型的数据,并且数组是定长的。当在程序中创建的对象比较多的时候,需要对这些对象进行统一的管理和操作,那么首先我们就需要把这些对象存储起来。使用数组是可以存放对象的,我们可以定义对象数组来存放,但是数组这个容器存放对象,要对其中的对象进行更复杂操作时,数组就显的很麻烦。那怎么办呢?
Java中给我们提供了另外一类容器,专门用来存放对象,这个容器就是我们要学习的集合。
集合和数组既然都是容器,它们有啥区别呢?
数组的长度是固定的。集合的长度是可变的。
数组中存储的是同一类型的元素,可以存储基本数据类型值。
集合存储的都是对象。而且对象的类型可以不一致。
集合貌似看起来比较强大,它啥时用呢?
当对象多的时候,先进行存储。
1.1.2集合框架的由来
集合本身是一个工具,它存放在java.util包中。
JDK最早的1.0版本中。提供的集合容器很少。升级到1.2版,为了更多的需求,出现了集合框架。有了更多的容器。可以完成不同的需求。
这些容器怎么区分,区分的方式?每一个容器的数据结构(数据存储的一种方式)不一样。
不同的容器进行不断的向上抽取,最后形成了一个集合框架,这个框架就是Collection接口。在Collection接口定义着集合框架中最共性的内容。
在学习时:我们需要看最顶层怎么用, 创建底层对象即可。因为底层继承了父类中的所有功能。
1.1.3Collection接口的描述
既然Collection接口是集合中的顶层接口,那么它中定义的所有功能子类都可以使用。查阅API中描述的Collection接口。Collection 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。
其实我们在使用ArrayList类时,该类已经把所有抽象方法进行了重写。那么,实现Collection接口的所有子类都会进行方法重写。
Collecton接口常用的子接口有:List接口、Set接口
List接口常用的子类有:ArrayList类、LinkedList类
Set接口常用的子类有:HashSet类、LinkedHashSet类
继续查阅API,发现Collection接口中很多集合的操作方法,那么这些方法都具体能做什么呢?
1.1.4Collection基本方法了解
1.2Collection接口的基本方法
这里我们不关心具体创建的Collection中的那个子类对象,这里重点演示的是Collection接口中的方法
Collection<String> coll = new ArrayList<String>();
//1,往集合中添加对象元素。add(Object);
coll.add("itcast1");
coll.add("itcast2");
coll.add("itcast3");
//2,删除。
coll.remove("itcast2");
//3,判断是否包含。
System.out.println(coll.contains("itcast11"));
//4,清除。
coll.clear();
//把集合打印一下。
System.out.println(coll);//[itcast1, itcast2, itcast3]
第2章Iterator迭代器
2.1Iterator迭代器概述
java中提供了很多个集合,它们在存储元素时,采用的存储方式不同。我们要取出这些集合中的元素,可通过一种通用的获取方式来完成。
Collection集合元素的通用获取方式:在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出来。一直把集合中的所有元素全部取出,这种取出方式专业术语称为迭代。
集合中把这种取元素的方式描述在Iterator接口中。
Iterator接口的常用方法如下:
**hasNext()方法:**用来判断集合中是否有下一个元素可以迭代。如果返回true,说明可以迭代。
**next()方法:**用来返回迭代的下一个元素,并把指针向后移动一位。
迭代集合元素图解:
2.2Iterator迭代方式的代码体现
在Collection接口描述了一个抽象方法iterator方法,所有Collection子类都实现了这个方法,并且有自己的迭代形式。
进行代码演示:
//1,创建集合对象。
Collection<String> coll = new ArrayList<String>();
coll.add("abc1");
coll.add("abc2");
coll.add("abc3");
coll.add("abc4");
//2.获取容器的迭代器对象。通过iterator方法。
Iterator it = coll.iterator();
//3,使用具体的迭代器对象获取集合中的元素。参阅迭代器的方法
while(it.hasNext()){
System.out.println(it.next());
}
//迭代器for循环的形式的使用
for (Iterator it = coll.iterator(); it.hasNext(); ) {
System.out.println(it.next());
}
注意:在进行集合元素取出时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会发生java.util.NoSuchElementException异常错误,指集合中没有元素的异常。
下边分别介绍以上内容:
a)Collection接口的iterator
方法声明为:
Iterator<集合中数据类型> iterator()
用来返回专属于该集合对象的迭代器对象(Iterator的子类对象)。
b)Iterator接口
该接口规定了迭代集合所需要的方法
c)Iterator接口的两个方法:hasNext与next方法
Iterator规定了两个方法,集合对象产生的迭代器对象正是通过这两个方法帮助集合进行迭代工作的。
调用迭代器的hasNext()方法判断是否有下一个元素
调用迭代器的next()方法获取下一个元素
2.3并发修改异常
迭代的常规用法中我们要尽量避免在迭代过程中为集合添加/删除数据。否则会报错,原因是Java抛出了并发修改异常。
迭代过程中抛出并发修改异常的原因为迭代器中**”记忆”的集合长度与集合中实际长度不同**,而导致出现索引与实际元素不符甚至无限循环的情况发生。
所以在使用Iterator时,避免类似操作,for循环底层为迭代器实现,所以也需要避免类似操作。
有些迭代器避免了这样的问题,如ListIterator,但该类并不通用,不常用,实际开发中很少使用,只需要简单了解。
java中提供了很多个集合,它们在存储元素时,采用的存储方式不同。我们要取出这些集合中的元素,可通过一种通用的获取方式来完成。
2.4增强for循环
增强for循环是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。
格式:
for(元素的数据类型 变量 : Collection集合or数组){
}
它用于遍历Collection和数组。通常只进行遍历元素,不要在遍历的过程中对集合元素进行增删操作。
练习一:遍历数组
int[] arr = new int[]{11,22,33};
for (int n : arr) {//变量n代表被遍历到的数组元素
System.out.println(n);
}
练习二:遍历集合
Collection<String> coll = new ArrayList<String>();
coll.add("itcast1");
coll.add("itcast2");
coll.add("itcast3");
coll.add("itcast4");
for(String str : coll){//变量Str代表被遍历到的集合元素
System.out.println(str);
}
增强for循环和老式的for循环有什么区别?
注意:新for循环必须有被遍历的目标。目标只能是Collection集合或[]数组。
建议:遍历数组时,如果仅为遍历,可以使用增强for。如果要对数组的元素进行 操作,使用老式for循环可以通过角标操作。
第3章泛型element
3.1泛型概述
泛型用来灵活地将数据类型应用到不同的类、方法、接口当中,将数据类型作为参数传递。
泛型是数据类型的一部分,我们将类名与泛型合并一起看做数据类型。
泛型的定义:定义泛型可以在类中预支地使用未知的类型。
泛型的使用:一般在创建对象时,将未知的类型确定为具体的类型。当没有指定泛型时,默认类型为Object类型。
3.2使用泛型的好处
将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
避免了类型强转的麻烦。
演示下列代码:
public class GenericDemo {
public static void main(String[] args) {
Collection<String> list = new ArrayList<String>();
list.add("abc");
list.add("itcast");
//list.add(5);//当集合明确类型后,存放类型不一致就会编译报错
//集合已经明确具体存放的元素类型,那么在使用迭代器的时候,迭代器也同样会知道具体遍历元素类型
Iterator<String> it = list.iterator();
while(it.hasNext()){
String str = it.next();
//当使用Iterator<String>控制元素类型后,就不需要强转了。获取到的元素直接就是String类型
System.out.println(str.length());
}
}
}
3.3泛型的定义与使用
我们在集合中会大量使用到泛型,这里来完整地学习泛型知识。
泛型,用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。
3.3.1含有泛型的类
定义格式:修饰符 class 类名<代表泛型的变量> { }
例如,API中的ArrayList集合:
class ArrayList<E>{
public boolean add(E e){ }
public Eget(int index){ }
}
使用格式:创建对象时,确定泛型的类型例如1,ArrayList list = new ArrayList();
此时,变量E*的值就是String类型
class ArrayList<String>{
public boolean add(String e){ }
public Stringget(int index){ }
}
例如2,ArrayList list = new ArrayList();
此时,变量E的值就是Integer类型
class ArrayList<Integer>{
public boolean add(Integer e){ }
public Integerget(int index){ }
}
例如3:自定义泛型类
public class GenericClass<E>{//自定义的类中,可以写<>泛型
//E 表示未知的数据类型 调用者创建对象的时候,才能明确数据类型
private E e;
public void setE(E e){
this.e = e;
}
public E getE(){
returne;
}
}
//使用:
public class GenericClassTest {
public static void main(String[] args) {
//对自定义的泛型类,进行测试
GenericClass<Integer> g = new GenericClass<Integer>();
//E传递什么类型就是什么类型
g.setE(100);
Integer i = g.getE();
System.out.println(i);
}
}
3.3.2含有泛型的方法
定义格式:修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
例如:
public class GenericMethod <E>{
public void show(E e){
System.out.println(e);
}
//自定义泛型的方法
public<T>void function(T t){
//自己写一个方法,方法中的数据类型,采用<>泛型
//如果方法中的泛型,和类上的泛型不同
// 在方法返回值前加入<>
System.out.println(t);
}
}
使用格式:调用方法时,确定泛型的类型
public class GenericMethodTest {
public static void main(String[] args) {
GenericMethod<Double> g = new GenericMethod<Double>();
g.show(1.1);
//传递什么类型就是什么类型
g.function(1.2F);
}
}
3.3.3含有泛型的接口
定义格式:修饰符 interface接口名<代表泛型的变量> { }
例如,
public interface Inter <E>{
public abstract void show(E e);
}
使用格式:
1、定义类时确定泛型的类型
例如
public class InterImpl implements Inter<Integer>{
publicvoid show(Integer i){
System.out.println(i);
}
}
此时,变量E的值就是Integer类型。
2、始终不确定泛型的类型,直到创建对象时,确定泛型的类型
例如
public class InterImpl<E>implements Inter<E>{
publicvoid show(E e){
System.out.println(e);
}
}
InterImpl<String> imp= new InterImpl<String>();
此时,变量E的值就是String类型。
3.4泛型通配符
当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
定义:(查看ArrayList的构造方法)无法在类中使用
使用:调用方法时可以给予任意类型。参照Arraylist的构造方法
? extends E代表只要是E类型的子类即可
? super E代表只要是E类型的父类即可
/*
* 泛型通配符?,代表任意的数据类型
* 定义:(查看ArrayList的构造方法)无法在类中使用
* 使用:调用方法时可以给予任意类型。参照Arraylist的构造方法
* public ArrayList(Collection<? extends E> c)
* 为了便于?的理解,我们将以上方法重写为public ArrayList(ArrayList<? extends E> c)
* 该方法的意思:创建集合对象A时,给于另外一个集合对象B作为参数,则创建好的集合A中包含了集合B中的元素
*
* ? extends E代表只要是E类型的子类即可
* ? super E代表只要是E类型的父类即可
*/
publicclass Demo01 {
public static void main(String[] args) {
//定义集合b,包含3个元素
ArrayList<String> listB = new ArrayList<String>();
listB.add("Jack");
listB.add("Rose");
listB.add("Trump");
//使用集合b创建集合a
ArrayList<Object> listA = new ArrayList<Object>(listB);
listA.add("Obama");
//观察集合A
System.out.println(listA);
}
}
第4章集合综合案例
4.1案例介绍
按照斗地主的规则,完成洗牌发牌的动作。
具体规则:
使用54张牌打乱顺序
三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
4.2案例需求分析
准备牌:
牌可以设计为一个ArrayList,每个字符串为一张牌。
每张牌由花色数字两部分组成,我们可以使用花色集合与数字集合嵌套迭代完成每张牌的组装。
牌由Collections类的shuffle方法进行随机排序。
发牌:
将每个人以及底牌设计为ArrayList,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。
看牌:
直接打印每个集合。
4.3实现代码步骤
修改文件编码由GBK修改为UTF-8,因为GBK没有我们要的梅花、方片、黑桃、红桃(♠♥♦♣)等字符。
publicclass Poker {
publicstaticvoid main(String[] args) {
//♠♥♦♣
//准备牌
ArrayList<String> poker = new ArrayList<String>();
//花色
ArrayList<String> color = new ArrayList<String>();
color.add("♠");
color.add("♥");
color.add("♦");
color.add("♣");
//数字
ArrayList<String> number = new ArrayList<String>();
for (int i = 2; i <= 10; i++) {
number.add(i+"");
}
number.add("J");
number.add("Q");
number.add("K");
number.add("A");
//完成新牌
for (String thisColor : color) {
for (String thisNumber : number) {
String thisCard = thisColor + thisNumber;
poker.add(thisCard);
}
}
poker.add("小☺");
poker.add("大☻");
//洗牌
Collections.shuffle(poker);
//发牌
//玩家1
ArrayList<String> player1 = new ArrayList<String>();
//玩家2
ArrayList<String> player2 = new ArrayList<String>();
//玩家3
ArrayList<String> player3 = new ArrayList<String>();
//底牌
ArrayList<String> secretCards = new ArrayList<String>();
for (int i = 0; i < poker.size(); i++) {
if(i>=51) {
//最后三张发给底牌
secretCards.add(poker.get(i));
}else {
//剩余牌通过对3取模依次摸牌
if(i%3==0) {
player1.add(poker.get(i));
}elseif(i%3==1) {
player2.add(poker.get(i));
}else {
player3.add(poker.get(i));
}
}
}
//看牌
System.out.println(player1);
System.out.println(player2);
System.out.println(player3);
System.out.println(secretCards);
}
}
最后发到三个人手中的牌是无序的,在明天学习完Map集合后,我们提供一个排序的解决方案。