摘要
学习一下Collection单列集合,顺便了解一下泛型
正文
第一章 Collection集合 1.1 集合概述 已经学习过并使用过集合ArrayList ,那么集合到底是什么呢?
集合 :集合是java中提供的一种容器,可以用来存储多个数据。
集合和数组既然都是容器,它们有啥区别呢?
数组的长度是固定的。集合的长度是可变的。 数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象。而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储。
1.2 集合框架 JAVASE提供了满足各种需求的API,在使用这些API前,先了解其继承与接口操作架构,才能了解何时采用哪个类,以及类之间如何彼此合作,从而达到灵活应用。
集合按照其存储结构可以分为两大类,分别是单列集合java.util.Collection和双列集合java.util.Map,今天主要学习Collection集合,在day04时讲解Map集合。
Collection :单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是java.util.List和java.util.Set。其中,List的特点是元素有序、元素可重复。Set的特点是元素无序,而且不可重复。List接口的主要实现类有java.util.ArrayList和java.util.LinkedList,Set接口的主要实现类有java.util.HashSet和java.util.TreeSet。从上面的描述可以看出JDK中提供了丰富的集合类库,为了便于初学者进行系统地学习,接下来通过一张图来描述整个集合类的继承体系。
其中,橙色框里填写的都是接口类型,而蓝色框里填写的都是具体的实现类。这几天将针对图中所列举的集合类进行逐一地讲解。
集合本身是一个工具,它存放在java.util包中。在Collection接口定义着单列集合框架中最最共性的内容。
1.3 Collection 常用功能 Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。方法如下:
public boolean add(E e): 把给定的对象添加到当前集合中 。public void clear() :清空集合中所有的元素。public boolean remove(E e): 把给定的对象在当前集合中删除。public boolean contains(E e): 判断当前集合中是否包含给定的对象。public boolean isEmpty(): 判断当前集合是否为空。public int size(): 返回集合中元素的个数。public Object[] toArray(): 把集合中的元素,存储到数组中。方法演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.util.ArrayList ;
import java.util.Collection ;
public class Demo1Collection {
public static void main ( String [] args ) {
// 创建集合对象
// 使用多态形式
Collection < String > coll = new ArrayList < String > ();
// 使用方法
// 添加功能 boolean add(String s)
coll . add ( "小李广" );
coll . add ( "扫地僧" );
coll . add ( "石破天" );
System . out . println ( coll );
// boolean contains(E e) 判断o是否在集合中存在
System . out . println ( "判断 扫地僧 是否在集合中" + coll . contains ( "扫地僧" ));
//boolean remove(E e) 删除在集合中的o元素
System . out . println ( "删除石破天:" + coll . remove ( "石破天" ));
System . out . println ( "操作之后集合中元素:" + coll );
// size() 集合中有几个元素
System . out . println ( "集合中有" + coll . size () + "个元素" );
// Object[] toArray()转换成一个Object数组
Object [] objects = coll . toArray ();
// 遍历数组
for ( int i = 0 ; i < objects . length ; i ++ ) {
System . out . println ( objects [ i ] );
}
// void clear() 清空集合
coll . clear ();
System . out . println ( "集合中内容为:" + coll );
// boolean isEmpty() 判断是否为空
System . out . println ( coll . isEmpty ());
}
}
tips: 有关Collection中的方法可不止上面这些,其他方法可以自行查看API学习。
第二章 Iterator迭代器 2.1 Iterator接口 在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.Iterator。Iterator接口也是Java集合中的一员,但它与Collection、Map接口有所不同,Collection接口与Map接口主要用于存储元素,而Iterator主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象也被称为迭代器。
想要遍历Collection集合,那么就要获取该集合迭代器完成迭代操作,下面介绍一下获取迭代器的方法:
public Iterator iterator(): 获取集合对应的迭代器,用来遍历集合中的元素的。下面介绍一下迭代的概念:
迭代 :即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。Iterator接口的常用方法如下:
public E next():返回迭代的下一个元素。public boolean hasNext():如果仍有元素可以迭代,则返回 true。接下来通过案例学习如何使用Iterator迭代集合中元素:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class IteratorDemo {
public static void main ( String [] args ) {
// 使用多态方式 创建对象
Collection < String > coll = new ArrayList < String > ();
// 添加元素到集合
coll . add ( "串串星人" );
coll . add ( "吐槽星人" );
coll . add ( "汪星人" );
//遍历
//使用迭代器 遍历 每个集合对象都有自己的迭代器
Iterator < String > it = coll . iterator ();
// 泛型指的是 迭代出 元素的数据类型
while ( it . hasNext ()){ //判断是否有迭代元素
String s = it . next (); //获取迭代出的元素
System . out . println ( s );
}
}
}
tips::在进行集合元素取出时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会发生java.util.NoSuchElementException没有集合元素的错误。
2.2 迭代器的实现原理 在之前案例已经完成了Iterator遍历集合的整个过程。当遍历集合时,首先通过调用t集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。
Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素,为了让初学者能更好地理解迭代器的工作原理,接下来通过一个图例来演示Iterator对象迭代元素的过程:
在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。
2.3 增强for 增强for循环(也称for each循环)是JDK1.5 以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。
格式:
1
2
3
for ( 元素的数据类型 变量 : Collection集合or数组 ){
//写操作代码
}
它用于遍历Collection和数组。通常只进行遍历元素,不要在遍历的过程中对集合元素进行增删操作。
练习1:遍历数组 1
2
3
4
5
6
7
8
9
public class NBForDemo1 {
public static void main ( String [] args ) {
int [] arr = { 3 , 5 , 6 , 87 };
//使用增强for遍历数组
for ( int a : arr ){ //a代表数组中的每个元素
System . out . println ( a );
}
}
}
练习2:遍历集合 1
2
3
4
5
6
7
8
9
10
11
12
public class NBFor {
public static void main ( String [] args ) {
Collection < String > coll = new ArrayList < String > ();
coll . add ( "小河神" );
coll . add ( "老河神" );
coll . add ( "神婆" );
//使用增强for遍历
for ( String s : coll ){ //接收变量s代表 代表被遍历到的集合元素
System . out . println ( s );
}
}
}
tips: 新for循环必须有被遍历的目标。目标只能是Collection或者是数组。新式for仅仅作为遍历操作出现。
第三章 List接口 学习Collection中的常用几个子类(java.util.List集合、java.util.Set集合)。
3.1 List接口介绍 java.util.List接口继承自Collection接口,是单列集合的一个重要分支,习惯性地会将实现了List接口的对象称为List集合。在List集合中允许出现重复的元素,所有的元素是以一种线性方式进行存储的,在程序中可以通过索引来访问集合中的指定元素。另外,List集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致。
看完API,总结一下:
List接口特点:
它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。 tips: List接口的子类java.util.ArrayList类,该类中的方法都是来自List中定义。
3.2 List接口中常用方法 List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法,如下:
public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。public E get(int index):返回集合中指定位置的元素。public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。List集合特有的方法都是跟索引相关.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class ListDemo {
public static void main ( String [] args ) {
// 创建List集合对象
List < String > list = new ArrayList < String > ();
// 往 尾部添加 指定元素
list . add ( "图图" );
list . add ( "小美" );
list . add ( "不高兴" );
System . out . println ( list );
// add(int index,String s) 往指定位置添加
list . add ( 1 , "没头脑" );
System . out . println ( list );
// String remove(int index) 删除指定位置元素 返回被删除元素
// 删除索引位置为2的元素
System . out . println ( "删除索引位置为2的元素" );
System . out . println ( list . remove ( 2 ));
System . out . println ( list );
// String set(int index,String s)
// 在指定位置 进行 元素替代(改)
// 修改指定位置元素
list . set ( 0 , "三毛" );
System . out . println ( list );
// String get(int index) 获取指定位置元素
// 跟size() 方法一起用 来 遍历的
for ( int i = 0 ; i < list . size (); i ++ ){
System . out . println ( list . get ( i ));
}
//还可以使用增强for
for ( String string : list ) {
System . out . println ( string );
}
}
}
第四章 List的子类 4.1 ArrayList集合 java.util.ArrayList集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合。
许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。
4.2 LinkedList集合 java.util.LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。
LinkedList是一个双向链表,那么双向链表是什么样子的呢,
看图这个解下et_img 双向链表.png 双向链表 %}
实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。这些方法作为了解即可:
public void addFirst(E e):将指定元素插入此列表的开头。public void addLast(E e):将指定元素添加到此列表的结尾。public E getFirst():返回此列表的第一个元素。public E getLast():返回此列表的最后一个元素。public E removeFirst():移除并返回此列表的第一个元素。public E removeLast():移除并返回此列表的最后一个元素。public E pop():从此列表所表示的堆栈处弹出一个元素。public void push(E e):将元素推入此列表所表示的堆栈。public boolean isEmpty():如果列表不包含元素,则返回true。LinkedList是List的子类,List中的方法LinkedList都是可以使用,这里就不做详细介绍,只需要了解LinkedList的特有方法即可。在开发时,LinkedList集合也可以作为堆栈,队列的结构使用。(了解即可)
方法演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class LinkedListDemo {
public static void main ( String [] args ) {
LinkedList < String > link = new LinkedList < String > ();
//添加元素
link . addFirst ( "abc1" );
link . addFirst ( "abc2" );
link . addFirst ( "abc3" );
System . out . println ( link );
// 获取元素
System . out . println ( link . getFirst ());
System . out . println ( link . getLast ());
// 删除元素
System . out . println ( link . removeFirst ());
System . out . println ( link . removeLast ());
while ( ! link . isEmpty ()) { //判断集合是否为空
System . out . println ( link . pop ()); //弹出集合中的栈顶元素
}
System . out . println ( link );
}
}
第五章 Set接口 java.util.Set接口和java.util.List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格了。与List接口不同的是,Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
Set集合有多个子类,这里介绍其中的java.util.HashSet、java.util.LinkedHashSet这两个集合。
tips:Set集合取出元素的方式可以采用:迭代器、增强for。
5.1 HashSet集合介绍 java.util.HashSet是Set接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。java.util.HashSet底层的实现其实是一个java.util.HashMap支持,由于暂时还未学习,先做了解。
HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCode与equals方法。
先来使用一下Set集合存储,看下现象,再进行原理的讲解:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HashSetDemo {
public static void main ( String [] args ) {
//创建 Set集合
HashSet < String > set = new HashSet < String > ();
//添加元素
set . add ( new String ( "cba" ));
set . add ( "abc" );
set . add ( "bac" );
set . add ( "cba" );
//遍历
for ( String name : set ) {
System . out . println ( name );
}
}
}
输出结果如下,说明集合中不能存储重复元素:
tips:根据结果发现字符串"cba"只存储了一个,也就是说重复的元素set集合不存储。
5.2 哈希值 哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来的地址,不是数据实际存储的物理地址)
在Object类有方法hashCode(),可以获取对象的哈希值
源码:
1
2
public native int hashCode ();
// native 代表该方法调用的是本地操作系统的方法
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package demo23 ;
/*
* 哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来的地址,不是数据实际存储的物理地址)
* 在Object类有方法hashCode(),可以获取对象的哈希值
* 源码:
* public native int hashCode();
* native代表该方法调用的是本地操作系统的方法
*/
class Person /* extends Object */ {
//重写hashCode方法
@Override
public int hashCode () {
return 1 ;
}
}
public class Demo04HashCode {
public static void main ( String [] args ) {
Person p = new Person ();
System . out . println ( p );
System . out . println ( p . hashCode ()); //664740647
Person p2 = new Person ();
System . out . println ( p2 . hashCode ()); //804564176
System . out . println ( Integer . toHexString ( p2 . hashCode ())); //2ff4acd0
System . out . println ( p2 ); //demo23.Person@2ff4acd0
System . out . println ( p == p2 ); //false
//上面的是未重写方法之前
System . out . println ( "==========================================" );
//此处已经将hashCode()更改为返回值为1,但是p==p2还是false
System . out . println ( p == p2 );
System . out . println ( p . hashCode () == p2 . hashCode ());
/*
* String类的哈希值
* String类也重写了Object类的hashCode方法
*/
System . out . println ( "123" . hashCode ()); //48690
System . out . println ( "重地" . hashCode ()); //1179395
System . out . println ( "通话" . hashCode ()); //1179395
System . out . println ( "334" . hashCode ()); //50644
}
}
像重地和通话这样的,哈希值是一样的,叫做哈希冲突
哈希冲突:两个元素不同,但是哈希值相同。
5.3 HashSet集合存储数据的结构(哈希表) 什么是哈希表呢?
在JDK1.8 之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。
JDK1.8 (包括jdk1.8之后)中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。
看到这张图就有人要问了,这个是怎么存储的呢?
为了方便大家的理解结合一个存储流程图来说明一下:
总而言之,JDK1.8 引入红黑树大程度优化了HashMap的性能,那么对于来讲保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。
5.4 Set集合存储元素不重复的原理 1
2
3
4
5
6
7
8
9
10
public static void main ( String [] args ) {
HashSet < String > set = new HashSet ();
String s1 = new String ( "abc" );
String s2 = new String ( "abc" );
set . add ( s1 );
set . add ( s2 );
set . add ( "重地" );
set . add ( "通话" );
System . out . println ( set ); //重地,通话,abc
}
存储过程:
在add方法中,会调用s1的hashCode方法,计算字符串"abc"的哈希值,哈希值是96354,在集合中找,有没有96354这个哈希值的元素,发现没有,就会把s1存储到集合中
在add方法中,会调用s2的hashCode方法,计算字符串"abc"的哈希值,哈希值是96354,在集合中找,有没有96354这个哈希值的元素,发现有,发生哈希冲突,s2调用equals方法和哈希值相同的元素进行比较,s2.equals(s1),返回true;
两个元素的哈希值相同,equals方法返回true,认定两个元素相同。就不会把s2存储到集合中。
在add方法中,会调用"重地"的hashCode方法,计算字符串"重地"的哈希值,哈希值是1139395,在集合中找,有没有1139395这个哈希值的元素,发现没有,就会把"重地"存储到集合中
在add方法中,会调用"通话"的hashCode方法,计算字符串"通话"的哈希值,哈希值是1139395,在集合中找,有没有1139395这个哈希值的元素,发现有,发生哈希冲突,"通话"调用equals方法和哈希值相同的元素进行比较,s2.equals("重地"),返回false;
两个元素的哈希值相同,equals方法返回false,认定两个元素不同。就会把"通话"存储到集合中。
5.5 HashSet存储自定义类型元素 给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package demo23 ;
import java.util.HashSet ;
/*
* HashSet存储自定义元素
* set集合报错元素唯一:
* 存储的元素(String,Integer,...Student,Person),必须重写hashCode方法和equals方法
* 要求:
* 同名和同年龄的人,视为同一个人,只能存储一次
*/
class People {
private String name ;
private int age ;
public People () {
}
public People ( String name , int age ) {
super ();
this . name = name ;
this . age = age ;
}
public String getName () {
return name ;
}
public void setName ( String name ) {
this . name = name ;
}
public int getAge () {
return age ;
}
public void setAge ( int age ) {
this . age = age ;
}
// 下面这三个重写方法,都是eclipse自动生成的
@Override
public String toString () {
return "People [name=" + name + ", age=" + age + "]" ;
}
@Override
public int hashCode () {
final int prime = 31 ;
int result = 1 ;
result = prime * result + age ;
result = prime * result + (( name == null ) ? 0 : name . hashCode ());
return result ;
}
@Override
public boolean equals ( Object obj ) {
if ( this == obj )
return true ;
if ( obj == null )
return false ;
if ( getClass () != obj . getClass ())
return false ;
People other = ( People ) obj ;
if ( age != other . age )
return false ;
if ( name == null ) {
if ( other . name != null )
return false ;
} else if ( ! name . equals ( other . name ))
return false ;
return true ;
}
}
public class Demo06HashSetSavePerson {
public static void main ( String [] args ) {
HashSet < People > set = new HashSet <> ();
People p1 = new People ( "水冰儿" , 18 );
People p2 = new People ( "水冰儿" , 19 );
People p3 = new People ( "水冰儿" , 18 );
System . out . println ( p1 . hashCode ());
System . out . println ( p2 . hashCode ());
System . out . println ( p3 . hashCode ());
System . out . println ( p1 == p3 ); // false
System . out . println ( p1 . equals ( p3 )); // false
set . add ( p1 );
set . add ( p2 );
set . add ( p3 );
System . out . println ( set );
}
}
输出结果
1
2
3
4
5
6
27290290
27290321
27290290
false
true
[People [name=水冰儿, age=19], People [name=水冰儿, age=18]]
5.6 LinkedHashSet java.util.LinkedHashSet 继承自于 HashSet集合
特点:
底层是一个哈希表(数组+链表+红黑树)+链表——多了一条链表,用来记录元素的存储顺序,保证元素有序。
演示代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package demo23 ;
import java.util.HashSet ;
import java.util.LinkedHashSet ;
/*
* java.util.linkedHashSet extends HashSet集合
* 特点:
* 底层是一个哈希表(数组+链表+红黑树)+链表:多了一条链表,用来记录元素的存储顺序,保证元素有序
*/
public class Demo07LinkedHashSet {
public static void main ( String [] args ) {
HashSet < String > set = new HashSet <> ();
set . add ( "焰灵姬" );
set . add ( "妙妙" );
set . add ( "水冰儿" );
set . add ( "水冰儿" );
System . out . println ( set ); // 无序,并且不允许重复
//运行结果:[焰灵姬, 水冰儿, 妙妙]
LinkedHashSet < String > linkedset = new LinkedHashSet <> ();
linkedset . add ( "焰灵姬" );
linkedset . add ( "妙妙" );
linkedset . add ( "水冰儿" );
linkedset . add ( "水冰儿" );
System . out . println ( linkedset ); // 按照添加进去的顺序。有序的,并且不允许重复
//运行结果:[焰灵姬, 妙妙, 水冰儿]
}
}
5.7 可变参数 可变参数: jdk1.5之后出现的新特性。
使用前提:
当方法的参数列表的数据类型已经确定,参数的个数不确定,就可以使用可变参数。
使用格式:
1
2
定义方法时使用
修饰符 返回值类型 方法名 ( 数据类型 ... 变量名 ){}
其实这个书写完全等价于
1
修饰符 返回值类型 方法名 ( 数据类型 [] 变量名 ){}
可变参数原理:
可变参数底层就是一个数组,根据传递的参数个数不同,会创建不同长度的数组,来存储这些参数。
传递的参数个数可以是0个或者多个。
代码演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Demo {
public static int add ( int ... arr ){
//定义一个初始化的变量,记录累加和
int sum = 0 ;
//遍历数组,获取数组中的每个元素
for ( int i : arr ){
sum += i ;
}
return sum ;
}
public static void main ( String [] args ){
int i = add ( 10 , 11 );
System . out . println ( i );
}
}
可变参数的注意事项:
一个方法的参数列表,只能有一个可变参数 如果方法的参数有多个,那么可变参数必须写在参数列表的末尾 可变参数更高级的写法:
1
public static void methodFinal ( Object obj ){} // 可以接受任意类型的参数
第六章 Collections 6.1 常用功能 java.utils.Collections是集合工具类,用来对集合进行操作。部分方法如下:public static <T> boolean addAll(Collection<T> c, T... elements) :往集合中添加一些元素。public static void shuffle(List<?> list) 打乱顺序:打乱集合顺序。public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。代码演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class CollectionsDemo {
public static void main ( String [] args ) {
ArrayList < Integer > list = new ArrayList < Integer > ();
//原来写法
//list.add(12);
//list.add(14);
//list.add(15);
//list.add(1000);
//采用工具类 完成 往集合中添加元素
Collections . addAll ( list , 5 , 222 , 1 , 2 );
System . out . println ( list );
//排序方法
Collections . sort ( list );
System . out . println ( list );
}
}
结果 :
[ 5 , 222 , 1 , 2 ]
[ 1 , 2 , 5 , 222 ]
代码演示之后 ,发现的集合按照顺序进行了排列,可是这样的顺序是采用默认的顺序,如果想要指定顺序那该怎么办呢?
发现还有个方法没有讲,public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。接下来讲解一下指定规则的排列。
6.2 Comparable自然排序 sort(List list)的使用前提:
被排序的集合里面存储的元素,必须实现Comparable,重写接口中的方法CompareTo的定义排序的规则
Comparable接口的排序规则:
代码演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package demo24 ;
import java.util.ArrayList ;
import java.util.Collections ;
class Person implements Comparable < Person > {
private String name ;
private int age ;
public Person () {
}
public Person ( String name , int age ) {
this . name = name ;
this . age = age ;
}
public String getName () {
return name ;
}
public void setName ( String name ) {
this . name = name ;
}
public int getAge () {
return age ;
}
public void setAge ( int age ) {
this . age = age ;
}
@Override
public String toString () {
return "Person [name=" + name + ", age=" + age + "]" ;
}
//重写排序的规则
@Override
public int compareTo ( Person o ) {
// TODO Auto-generated method stub
// return 0;//默认返回0,意思是认为元素都是相同的。
//自定义规则,比较两个人的年龄——this指调用这个方法的
return o . getAge () - this . getAge (); //按照年龄降序排序
}
}
public class Demo02Sort {
public static void main ( String [] args ) {
ArrayList < String > list = new ArrayList <> ();
list . add ( "b" );
list . add ( "c" );
list . add ( "a" );
System . out . println ( list );
Collections . sort ( list );
System . out . println ( list );
ArrayList < Integer > list01 = new ArrayList <> ();
list01 . add ( 1 );
list01 . add ( 3 );
list01 . add ( 2 );
System . out . println ( list01 );
Collections . sort ( list01 );
System . out . println ( list01 );
/*
* 注意:
* sort(List<T> list)的使用前提——被排序的集合里面存储的元素,必须实现Comparable,重写接口中的方法CompareTo的定义
* 排序的规则
*
* Comparable接口的排序规则:
* 自己(this)-参数——升序
* 参数-this——降序
*/
ArrayList < Person > list02 = new ArrayList <> ();
list02 . add ( new Person ( "水冰儿" , 19 ));
list02 . add ( new Person ( "焰灵姬" , 28 ));
list02 . add ( new Person ( "紫女" , 30 ));
list02 . add ( new Person ( "水冰儿" , 20 ));
System . out . println ( list02 );
Collections . sort ( list02 );
System . out . println ( list02 );
}
}
6.3 Comparator比较器 还是先研究这个方法
public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。
不过这次存储的是字符串类型。
1
2
3
4
5
6
7
8
9
10
11
12
public class CollectionsDemo2 {
public static void main ( String [] args ) {
ArrayList < String > list = new ArrayList < String > ();
list . add ( "cba" );
list . add ( "aba" );
list . add ( "sba" );
list . add ( "nba" );
//排序方法
Collections . sort ( list );
System . out . println ( list );
}
}
结果:
使用的是默认的规则完成字符串的排序,那么默认规则是怎么定义出来的呢?
说到排序了,简单的说就是两个对象之间比较大小,那么在JAVA中提供了两种比较实现的方式,一种是比较死板 的采用java.lang.Comparable接口去实现,一种是灵活 的当我需要做排序的时候在去选择的java.util.Comparator接口完成。
那么采用的public static <T> void sort(List<T> list)这个方法完成的排序,实际上要求了被排序的类型需要实现Comparable接口完成比较的功能,在String类型上如下:
1
public final class String implements java . io . Serializable , Comparable < String > , CharSequence {
String类实现了这个接口,并完成了比较规则的定义,但是这样就把这种规则写死了,那比如我想要字符串按照第一个字符降序排列,那么这样就要修改String的源代码,这是不可能的了 ,那么这个时候可以使用
public static <T> void sort(List<T> list,Comparator<? super T> )方法灵活的完成,这个里面就涉及到了Comparator这个接口,位于位于java.util包下,排序是comparator能实现的功能之一,该接口代表一个比较器,比较器具有可比性!顾名思义就是做排序的,通俗地讲需要比较两个对象谁排在前谁排在后,那么比较的方法就是:
操作如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
class People {
private String name ;
private int age ;
public People () {
}
public People ( String name , int age ) {
this . name = name ;
this . age = age ;
}
public String getName () {
return name ;
}
public void setName ( String name ) {
this . name = name ;
}
public int getAge () {
return age ;
}
public void setAge ( int age ) {
this . age = age ;
}
@Override
public String toString () {
return "People [name=" + name + ", age=" + age + "]" ;
}
}
public class Demo03Sort {
public static void main ( String [] args ) {
ArrayList < Integer > list = new ArrayList <> ();
list . add ( 1 );
list . add ( 2 );
list . add ( 3 );
Collections . sort ( list , new Comparator < Integer > (){
//重写比较的规则
@Override
public int compare ( Integer o1 , Integer o2 ) {
return o2 - o1 ; //降序
// return o1-o2;//升序
}
});
System . out . println ( list );
ArrayList < People > list01 = new ArrayList <> ();
list01 . add ( new People ( "焰灵姬" , 28 ));
list01 . add ( new People ( "水冰儿" , 19 ));
list01 . add ( new People ( "b水冰儿" , 22 ));
list01 . add ( new People ( "a妙妙" , 22 ));
System . out . println ( list01 );
Collections . sort ( list01 , new Comparator < People > () {
@Override
public int compare ( People o1 , People o2 ) {
/*
* 按照年龄升序排序
* 如果两个人年龄相同,再使用姓名相同的第一个字比较
*/
int result = o1 . getAge () - o2 . getAge ();
if ( result == 0 ) {
result = o1 . getName (). charAt ( 0 ) - o2 . getName (). charAt ( 0 );
}
return result ;
}
});
System . out . println ( list01 );
}
}
结果如下:
1
2
3
[ 3 , 2 , 1 ]
[ People [ name = 焰灵姬 , age = 28 ] , People [ name = 水冰儿 , age = 19 ] , People [ name = b水冰儿 , age = 22 ] , People [ name = a妙妙 , age = 22 ]]
[ People [ name = 水冰儿 , age = 19 ] , People [ name = a妙妙 , age = 22 ] , People [ name = b水冰儿 , age = 22 ] , People [ name = 焰灵姬 , age = 28 ]]
6.4 简述Comparable和Comparator两个接口的区别。 Comparable :强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码实现自己想要的排序。实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
上面是官方文档的,我再总结一下,这个太死板了。
Comparator 强行对某个对象进行整体排序。可以将Comparator 传递给sort方法(如Collections.sort或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。
上面是官方文档的,我再总结一下,这个很灵活。
6.5 练习——Comparable 创建一个学生类,存储到ArrayList集合中完成指定排序操作。
Student 初始类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Student {
private String name ;
private int age ;
public Student () {
}
public Student ( String name , int age ) {
this . name = name ;
this . age = age ;
}
public String getName () {
return name ;
}
public void setName ( String name ) {
this . name = name ;
}
public int getAge () {
return age ;
}
public void setAge ( int age ) {
this . age = age ;
}
@Override
public String toString () {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}' ;
}
}
测试类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Demo {
public static void main ( String [] args ) {
// 创建四个学生对象 存储到集合中
ArrayList < Student > list = new ArrayList < Student > ();
list . add ( new Student ( "rose" , 18 ));
list . add ( new Student ( "jack" , 16 ));
list . add ( new Student ( "abc" , 16 ));
list . add ( new Student ( "ace" , 17 ));
list . add ( new Student ( "mark" , 16 ));
/*
让学生 按照年龄排序 升序
*/
// Collections.sort(list);//要求 该list中元素类型 必须实现比较器Comparable接口
for ( Student student : list ) {
System . out . println ( student );
}
}
}
发现,当调用Collections.sort()方法的时候 程序报错了。
原因:如果想要集合中的元素完成排序,那么必须要实现比较器Comparable接口。
于是就完成了Student类的一个实现,如下:
1
2
3
4
5
6
7
public class Student implements Comparable < Student > {
....
@Override
public int compareTo ( Student o ) {
return this . age - o . age ; //升序
}
}
再次测试,代码就OK 了效果如下:
1
2
3
4
5
Student { name = ' jack ' , age = 16 }
Student { name = ' abc ' , age = 16 }
Student { name = ' mark ' , age = 16 }
Student { name = ' ace ' , age = 17 }
Student { name = ' rose ' , age = 18 }
6.6 扩展练习——Comparator 如果在使用的时候,想要独立的定义规则去使用 可以采用Collections.sort(List list,Comparetor c)方式,自己定义规则:
1
2
3
4
5
6
Collections . sort ( list , new Comparator < Student > () {
@Override
public int compare ( Student o1 , Student o2 ) {
return o2 . getAge () - o1 . getAge (); //以学生的年龄降序
}
});
效果:
1
2
3
4
5
Student { name = ' rose ' , age = 18 }
Student { name = ' ace ' , age = 17 }
Student { name = ' jack ' , age = 16 }
Student { name = ' abc ' , age = 16 }
Student { name = ' mark ' , age = 16 }
如果想要规则更多一些,可以参考下面代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
Collections . sort ( list , new Comparator < Student > () {
@Override
public int compare ( Student o1 , Student o2 ) {
// 年龄降序
int result = o2 . getAge () - o1 . getAge (); //年龄降序
if ( result == 0 ){ //第一个规则判断完了 下一个规则 姓名的首字母 升序
result = o1 . getName (). charAt ( 0 ) - o2 . getName (). charAt ( 0 );
}
return result ;
}
});
效果如下:
1
2
3
4
5
Student { name = ' rose ' , age = 18 }
Student { name = ' ace ' , age = 17 }
Student { name = ' abc ' , age = 16 }
Student { name = ' jack ' , age = 16 }
Student { name = ' mark ' , age = 16 }
第七章 泛型 7.1 泛型概述 在前面学习集合时,都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。
大家观察下面代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class GenericDemo {
public static void main ( String [] args ) {
Collection coll = new ArrayList ();
coll . add ( "abc" );
coll . add ( "itcast" );
coll . add ( 5 ); //由于集合没有做任何限定,任何类型都可以给其中存放
Iterator it = coll . iterator ();
while ( it . hasNext ()){
//需要打印每个字符串的长度,就要把迭代出来的对象转成String类型
String str = ( String ) it . next ();
System . out . println ( str . length ());
}
}
}
程序在运行时发生了问题java.lang.ClassCastException 。 为什么会发生类型转换异常呢? 来分析下:由于集合中什么类型的元素都可以存储。导致取出时强转引发运行时 ClassCastException。 怎么来解决这个问题呢? Collection虽然可以存储各种对象,但实际上通常Collection只存储同一类型对象。例如都是存储字符串对象。因此在JDK5之后,新增了泛型 (Generic )语法,让你在设计API时可以指定类或方法支持泛型,这样使用API的时候也变得更为简洁,并得到了编译时期的语法检查。
tips:一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。
7.2 使用泛型的好处与弊端 上一节只是讲解了泛型的引入,那么泛型带来了哪些好处呢?
将运行时期的ClassCastException,转移到了编译时期变成了编译失败。 避免了类型强转的麻烦。 通过如下代码体验一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class GenericDemo2 {
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 ());
}
}
}
tips:泛型是数据类型的一部分,将类名与泛型合并一起看做数据类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class Demo04Generic {
public static void main ( String [] args ) {
// show01();
show02 ();
}
/*
* 创建集合对象,使用泛型
* 好处:
* 1.避免了类型转换的麻烦,存储的是什么类型,取出的是什么类型
* 2.把运行期异常(代码运行之后会抛出的异常),提升到了编译期(写代码的时候就会报错 )
* 弊端:
* 泛型是什么样的类型,就只能存储什么类型的数据
*/
public static void show02 () {
ArrayList < String > list = new ArrayList < String > ();
list . add ( "焰灵姬" );
list . add ( "潮女妖" );
list . add ( "江厌离" ); //存储别的类型,会直接报错
//使用迭代器遍历一个集合
Iterator < String > it = list . iterator ();
while ( it . hasNext ()) {
String s = it . next ();
System . out . println ( s + "-->" + s . length ());
}
}
/*
* 创建集合,不使用泛型
* 好处:
* 集合不使用泛型,默认的类型就是Object类型,可以存储任意类型的数据
*
* 弊端:
* 集合不安全,会引发异常
*/
public static void show01 () {
ArrayList list = new ArrayList (); //不使用泛型,此时默认为Object类型
list . add ( 1 );
list . add ( "焰灵姬" );
list . add ( "潮女妖" );
//增强for循环遍历
// for(Object o:list) {
// System.out.println(o);
// }
//迭代器遍历
Iterator it = list . iterator ();
while ( it . hasNext ()) {
Object obj = it . next ();
System . out . println ( obj );
//想要使用String类特有的方法,length获取字符串的长度;不能使用多态 Object obj="abc"
//需要向下转型
String s = ( String ) obj ;
System . out . println ( s . length ());
//此时会报错,因为有个Integer类型的
}
}
}
7.3 泛型的定义与使用 在集合中会大量使用到泛型,这里来完整地学习泛型知识。
泛型,用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。
定义和使用含有泛型的类 定义格式:
1
修饰符 class 类名<代表泛型的变量> { }
例如,API中的ArrayList集合:
1
2
3
4
5
6
class ArrayList < E > {
public boolean add ( E e ){ }
public E get ( int index ){ }
....
}
使用泛型: 即什么时候确定泛型。
在创建对象的时候确定泛型
例如,ArrayList<String> list = new ArrayList<String>();
此时,变量E的值就是String类型,那么的类型就可以理解为:
1
2
3
4
5
6
class ArrayList < String > {
public boolean add ( String e ){ }
public String get ( int index ){ }
...
}
再例如,ArrayList<Integer> list = new ArrayList<Integer>();
此时,变量E的值就是Integer类型,那么的类型就可以理解为:
1
2
3
4
5
6
class ArrayList < Integer > {
public boolean add ( Integer e ) { }
public Integer get ( int index ) { }
...
}
举例自定义泛型类
1
2
3
4
5
6
7
8
9
10
11
12
public class MyGenericClass < MVP > {
//没有MVP类型,在这里代表 未知的一种数据类型 未来传递什么就是什么类型
private MVP mvp ;
public void setMVP ( MVP mvp ) {
this . mvp = mvp ;
}
public MVP getMVP () {
return mvp ;
}
}
使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class GenericClassDemo {
public static void main ( String [] args ) {
// 创建一个泛型为String的类
MyGenericClass < String > my = new MyGenericClass < String > ();
// 调用setMVP
my . setMVP ( "大胡子登登" );
// 调用getMVP
String mvp = my . getMVP ();
System . out . println ( mvp );
//创建一个泛型为Integer的类
MyGenericClass < Integer > my2 = new MyGenericClass < Integer > ();
my2 . setMVP ( 123 );
Integer mvp2 = my2 . getMVP ();
}
}
含有泛型的方法 定义格式:
1
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
例如,
1
2
3
4
5
6
7
8
9
public class MyGenericMethod {
public < MVP > void show ( MVP mvp ) {
System . out . println ( mvp . getClass ());
}
public < MVP > MVP show2 ( MVP mvp ) {
return mvp ;
}
}
使用格式:调用方法时,确定泛型的类型
1
2
3
4
5
6
7
8
9
10
public class GenericMethodDemo {
public static void main ( String [] args ) {
// 创建对象
MyGenericMethod mm = new MyGenericMethod ();
// 演示看方法提示
mm . show ( "aaa" );
mm . show ( 123 );
mm . show ( 12 . 45 );
}
}
含有泛型的接口 定义格式:
1
修饰符 interface接口名<代表泛型的变量> { }
例如,
1
2
3
4
5
public interface MyGenericInterface < E > {
public abstract void add ( E e );
public abstract E getE ();
}
使用格式:
1、定义类时确定泛型的类型
例如
1
2
3
4
5
6
7
8
9
10
11
public class MyImp1 implements MyGenericInterface < String > {
@Override
public void add ( String e ) {
// 省略...
}
@Override
public String getE () {
return null ;
}
}
此时,泛型E的值就是String类型。
2、始终不确定泛型的类型,直到创建对象时,确定泛型的类型
例如
1
2
3
4
5
6
7
8
9
10
11
public class MyImp2 < E > implements MyGenericInterface < E > {
@Override
public void add ( E e ) {
// 省略...
}
@Override
public E getE () {
return null ;
}
}
确定泛型:
1
2
3
4
5
6
7
8
9
/*
* 使用
*/
public class GenericInterface {
public static void main ( String [] args ) {
MyImp2 < String > my = new MyImp2 < String > ();
my . add ( "aa" );
}
}
7.4 泛型通配符 当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
通配符基本使用 泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。
此时只能接受数据,不能往该集合中存储数据。
举个例子大家理解使用即可:
1
2
3
4
5
6
7
8
public static void main ( String [] args ) {
Collection < Intger > list1 = new ArrayList < Integer > ();
getElement ( list1 );
Collection < String > list2 = new ArrayList < String > ();
getElement ( list2 );
}
public static void getElement ( Collection <?> coll ){}
// ? 代表可以接收任意类型
tips:泛型不存在继承关系 Collection list = new ArrayList();这种是错误的。
通配符高级使用----受限泛型 之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限 和下限 。
泛型的上限 :
格式 : 类型名称 <? extends 类 > 对象名称意义 : 只能接收该类型及其子类泛型的下限 :
格式 : 类型名称 <? super 类 > 对象名称意义 : 只能接收该类型及其父类型比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Demo09Generic {
public static void main ( String [] args ) {
Collection < Integer > list1 = new ArrayList < Integer > ();
Collection < String > list2 = new ArrayList < String > ();
Collection < Number > list3 = new ArrayList < Number > ();
Collection < Object > list4 = new ArrayList < Object > ();
/*
* list1 Integer
* list2 String
* list3 Number
* list4 Object
*/
getElement1 ( list1 );
getElement1 ( list2 ); // 报错
getElement1 ( list3 );
getElement1 ( list4 ); // 报错
getElement2 ( list1 ); // 报错
getElement2 ( list2 ); // 报错
getElement2 ( list3 );
getElement2 ( list4 );
}
/*
* 类与类之间的继承关系
* Integer extends Number extends Object
* String extends Object
*/
// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement1 ( Collection <? extends Number > coll ) {
}
// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2 ( Collection <? super Number > coll ) {
}
}
第八章 集合综合案例 8.1 案例介绍 按照斗地主的规则,完成洗牌发牌的动作。
具体规则:
使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
8.2 案例分析
准备牌:
牌可以设计为一个ArrayList,每个字符串为一张牌。
每张牌由花色数字两部分组成,可以使用花色集合与数字集合嵌套迭代完成每张牌的组装。
牌由Collections类的shuffle方法进行随机排序。
发牌
将每个人以及底牌设计为ArrayList,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。
看牌
直接打印每个集合。
8.3 代码实现 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package demo20 ;
import java.util.ArrayList ;
import java.util.Collections ;
/*
* 斗地主:
* 1.准备牌
* 2.洗牌
* 3.发牌
* 4.看牌
*/
public class DouDiZhu {
public static void main ( String [] args ) {
/*
* 1.准备牌
*/
//定义一个存储54张牌的ArrayList集合,泛型使用字符串
ArrayList < String > poke = new ArrayList < String > ();
//定义两个数组,一个数组存储牌的花色,一个存储序号
String [] colors = {
"♥" , "♠" , "♣" , "♦"
};
String [] numbers = {
"2" , "A" , "K" , "Q" , "J" , "10" , "9" , "8" , "7" , "6" , "5" , "4" , "3"
};
//先把大王跟小王存储到集合中
poke . add ( "大王" );
poke . add ( "小王" );
//循环遍历两个数组,组装52张牌
for ( String number : numbers ) {
for ( String color : colors ) {
//把组装好的牌存储到poke牌当中
poke . add ( color + number );
}
}
// System.out.println(poke);
/*
* 2.洗牌
* 使用集合的工具类Collections中的方法
* static void shuffle(List<?> list) 使用默认随机源对指定列表进行置换
*/
Collections . shuffle ( poke );
// System.out.println(poke);
/*
* 3.发牌
* 定义四个集合,存储3个玩家和1底牌
*/
ArrayList < String > player01 = new ArrayList < String > ();
ArrayList < String > player02 = new ArrayList < String > ();
ArrayList < String > player03 = new ArrayList < String > ();
ArrayList < String > diPai = new ArrayList < String > ();
//遍历poke集合,获取每一张牌,使用poke的索引,%3给三个玩家轮流发牌
//剩余三张牌给底牌
//注意:
//先判断底牌(i)>=51,否则就发没了
for ( int i = 0 ; i < poke . size (); i ++ ) {
//获取每一张牌
String p = poke . 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 );
}
}
/*
* 4.看牌
*/
System . out . println ( "紫女:" + player01 );
System . out . println ( "焰灵姬:" + player02 );
System . out . println ( "潮女妖:" + player03 );
System . out . println ( "底牌:" + diPai );
}
}