前面的学习,咱们知道数据多了,使用数组存放。并且数组中存放的都是基本类型的数据,而且数组是定长的。当在程序中建立的对象比较多的时候,须要对这些对象进行统一的管理和操做,那么首先咱们就须要把这些对象存储起来。使用数组是能够存放对象的,咱们能够定义对象数组来存放,可是数组这个容器存放对象,要对其中的对象进行更复杂操做时,数据就显的很麻烦。那怎么办呢?java
Java中给咱们提供了另一类容器,专门用来存放对象,这个容器就是咱们要学习的集合。数组
集合和数组既然都是容器,它们有啥区别呢?数据结构
数组的长度是固定的。集合的长度是可变的。并发
数组中存储的是同一类型的元素,能够存储基本数据类型值。集合存储的都是对象。并且对象的类型能够不一致。框架
集合貌似看起来比较强大,它啥时用呢?工具
当对象多的时候,先进行存储。学习
集合自己是一个工具,它存放在java.util包中。测试
JDK最先的1.0版本中。提供的集合容器不多。升级到1.2版,为了更多的需求,出现了集合框架。有了更多的容器。能够完成不一样的需求。this
这些容器怎么区分?区分的方式:每个容器的数据结构(数据存储到的一种方式)不同。编码
不一样的容器进行不断的向上抽取,最后造成了一个集合框架,这个框架就是Collection接口。在Collection接口定义着集合框架中最最共性的内容。
在学习时:咱们须要看最顶层怎么用, 建立底层对象便可。由于底层继承了父类中的全部功能。
既然Collection接口是集合中的顶层接口,那么它中定义的全部功能子类均可以使用。查阅API中描述的Collection接口。Collection 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 容许有重复的元素,而另外一些则不容许。一些 collection 是有序的,而另外一些则是无序的。
其实咱们在使用ArrayList类时,该类已经把全部抽象方法进行了重写。那么,实现Collection接口的全部子类都会进行方法重写。
继续查阅API,发现Collection接口中不少集合的操做方法,那么这些方法都具体能作什么呢?
这里咱们不关心具体建立的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]
java中提供了不少个集合,它们在存储元素时,采用的存储方式不一样。咱们要取出这些集合中的元素,可经过一种通用的获取方式来完成。
Collection集合元素的通用获取方式:在取元素以前先要判断集合中有没有元素,若是有,就把这个元素取出来,继续在判断,若是还有就再取出出来。一直把集合中的全部元素所有取出。这种取出方式专业术语称为迭代。
集合中把这种取元素的方式描述在Iterator接口中。Iterator接口的经常使用方法以下:
迭代集合元素图解:
在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没有集合元素的错误。
下边分别介绍以上内容:
方法声明为:
Iterator<集合中数据类型>iterator()
用来返回专属于该集合对象的迭代器对象(Iterator的子类对象)。
该接口规定了迭代集合所须要的方法
Iterator规定了两个方法,集合对象产生的迭代器对象正是经过这两个方法帮助集合进行迭代工做的。
调用迭代器的hasNext方法判断是否有下一个元素
调用迭代器的next获取下一个元素
迭代的常规用法中咱们要尽可能避免在迭代过程当中为集合添加/删除数据。不然会报错,缘由是Java抛出了并发修改异常。
迭代过程当中并发修改异常的缘由为迭代器中”记忆”的集合长度与集合中实际长度不一样,而致使出现索引与实际元素不符甚至无限循环的状况发生。
因此在使用Iterator时,避免相似操做,for循环底层为迭代器实现,因此也须要避免相似操做。
有些迭代器避免了这样的问题,如ListIterator,但该类并不通用也不经常使用,实际开发中不多使用,只须要简单了解。
java中提供了不少个集合,它们在存储元素时,采用的存储方式不一样。咱们要取出这些集合中的元素,可经过一种通用的获取方式来完成。
加强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循环能够经过角标操做。
泛型用来灵活地将数据类型应用到不一样的类、方法、接口当中。将数据类型做为参数传递。
泛型是数据类型的一部分,咱们将类名与泛型合并一块儿看作数据类型。
泛型的定义:定义泛型能够在类中预支地使用未知的类型。
泛型的使用:通常在建立对象时,将未知的类型肯定具体的类型。当没有指定泛型时,默认类型为Object类型。
演示下列代码:
publicclass GenericDemo {
publicstaticvoid 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());
}
}
}
咱们在集合中会大量使用到泛型,这里来完整地学习泛型知识。
泛型,用来灵活地将数据类型应用到不一样的类、方法、接口当中。将数据类型做为参数进行传递。
定义格式:修饰符 class 类名<表明泛型的变量> { }
class ArrayList<E>{
public boolean add(E e){ }
public Eget(int index){ }
}
使用格式:建立对象时,肯定泛型的类型
此时,变量E的值就是String类型
class ArrayList<String>{
public boolean add(String e){ }
public Stringget(int index){ }
}
此时,变量E的值就是Integer类型
class ArrayList<Integer>{
public boolean add(Integer e){ }
public Integerget(int index){ }
}
举例自定义泛型类
publicclass GenericClass<E>{//自定义的类中,能够写<>泛型
//E 表示未知的数据类型 调用者建立对象的时候,才能明确数据类型
private E e;
publicvoid setE(E e){
this.e = e;
}
public E getE(){
returne;
}
}
使用:
publicclass GenericClassTest {
publicstaticvoid main(String[] args) {
//对自定义的泛型类,进行测试
GenericClass<Integer> g = new GenericClass<Integer>();
//E传递什么类型就是什么类型
g.setE(100);
Integer i = g.getE();
System.out.println(i);
}
}
定义格式:修饰符 <表明泛型的变量> 返回值类型 方法名(参数){ }
例如,
publicclass GenericMethod <E>{
publicvoid show(E e){
System.out.println(e);
}
public<T>void function(T t){//自定义泛型的方法
//本身写一个方法,方法中的数据类型,采用<>泛型
//若是方法中的泛型,和类上的泛型不一样
// 在方法返回值前加入<>
System.out.println(t);
}
}
使用格式:调用方法时,肯定泛型的类型
publicclass GenericMethodTest {
publicstaticvoid main(String[] args) {
GenericMethod<Double> g = new GenericMethod<Double>();
g.show(1.1);
g.function(1.2F);//传递什么类型就是什么类型
}
}
定义格式:修饰符 interface接口名<表明泛型的变量> { }
publicinterface Inter <E>{
publicabstractvoid show(E e);
}
使用格式:
1、定义类时肯定泛型的类型
publicclass InterImpl implements Inter<Integer>{
publicvoid show(Integer i){
System.out.println(i);
}
}
此时,变量E的值就是Integer类型。
2、始终不肯定泛型的类型,直到建立对象时,肯定泛型的类型
InterImpl<String> imp= new InterImpl<String>();
此时,变量E的值就是String类型。
publicclass InterImpl<E>implements Inter<E>{
publicvoid show(E e){
System.out.println(e);
}
当使用泛型类或者接口时,传递的数据中,泛型类型不肯定,能够经过通配符<?>表示。可是一旦使用泛型的通配符后,只能使用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 {
publicstaticvoid 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);
}
按照斗地主的规则,完成洗牌发牌的动做。
具体规则:
使用54张牌打乱顺序
三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留做底牌。
牌能够设计为一个ArrayList<String>,每一个字符串为一张牌。
每张牌由花色数字两部分组成,咱们可使用花色集合与数字集合嵌套迭代完成每张牌的组装。
牌由Collections类的shuffle方法进行随机排序。
将每一个人以及底牌设计为ArrayList<String>,将最后3张牌直接存放于底牌,剩余牌经过对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);
}
}