第1章 泛型
1.1 泛型概述
泛型用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数传递。
泛型是数据类型的一部分,我们将类名与泛型合并一起看做数据类型。
泛型的定义:定义泛型可以在类中预支地使用未知的类型。
泛型的使用:一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。
1.2 泛型的定义与使用
我们在集合中会大量使用到泛型,这里来完整地学习泛型知识。
泛型,用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。
1.3 泛型代码实现
1.3.1 含有泛型的类
定义格式:修饰符 class 类名<代表泛型的变量> { }
-
例如,API中的ArrayList集合:
class ArrayList<E>{
public boolean add(E e){ }
public E get(int index){ }
}
使用格式:创建对象时,确定泛型的类型
-
例如,ArrayList<String> list = new ArrayList<String>();
此时,变量E的值就是String类型
class ArrayList<String>{
public boolean add(String e){ }
public String get(int index){ }
}
-
例如,ArrayList<Integer> list = new ArrayList<Integer>();
此时,变量E的值就是Integer类型
class ArrayList<Integer>{
public boolean add(Integer e){ }
public Integer get(int index){ }
}
1.3.2 含有泛型的方法
定义格式:修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
-
例如,API中的ArrayList集合中的方法:
public <T> T[] toArray(T[] a){ }
//该方法,用来把集合元素存储到指定数据类型的数组中,返回已存储集合元素的数组
使用格式:调用方法时,确定泛型的类型
-
例如
ArrayList<String> list = new ArrayList<String>();
String[] arr = new String[100];
String[] result = list.toArray(arr);
此时,变量T的值就是String类型。变量T,可以与定义集合的泛型不同
public <String> String[] toArray(String[] a){ }
-
例如
ArrayList<String> list = new ArrayList<String>();
Integer[] arr = new Integer[100];
Integer [] result = list.toArray(arr);
此时,变量T的值就是Integer类型。变量T,可以与定义集合的泛型不同
public <Integer> Integer[] toArray(Integer[] a){ }
1.3.3 含有泛型的接口
定义格式:修饰符 interface接口名<代表泛型的变量> { }
-
例如,API中的Iterator迭代器接口
public interface Iterator<E> {
public abstract E next();
}
使用格式:
1、定义类时确定泛型的类型
-
例如
public final class Scanner implements Iterator<String> {
public String next(){ }
}
此时,变量E的值就是String类型。
2、始终不确定泛型的类型,直到创建对象时,确定泛型的类型
-
例如
Collection<String> list = new ArrayList<String>();
Iterator<String> it = list.iterator();
此时,变量E的值就是String类型。
public interface Iterator<String> {
public abstract String next();
}
}
1.4 泛型通配符
在JDK1.5出现前,使用Object代表任意类型,但在使用时,涉及到了强转的麻烦。泛型替代了Object来代表任意类型。
泛型在编译时会擦除:泛型仅用来在编译期限制、方便程序员的操作,实际上真正编译后的.class中是没有泛型的,其中仍然使用的为Obejct类,通过类似多态的方式完成任意某个类型的指定。
当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用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类型的父类即可
*/
public class 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);
}
1.5 泛型优点与常见应用
泛型的好处:
1.提高了程序的安全性
2.将运行期遇到的问题转移到了编译期
3.省去了类型强转的麻烦
泛型的常见应用:
1.泛型类
2.泛型方法
3.泛型接口
第2章 List接口
2.1 接口特点及主要子类
-
单列集合
-
可存放重复元素
-
元素有序
-
主要子类
ArrayList:底层数据结构是数组结构。线程不安全的。所以ArrayList的出现替代了Vector。增删慢,查找快。
LinkedList:底层是链表数据结构。线程不安全的,同时对元素的增删快,查找慢。
Vector:底层数据结构是数组结构。jdk1.0版本。线程安全的。无论增删还是查询都非常慢,已被ArrayList替代。
2.2 List集合存储数据的结构
List接口下有很多个集合,它们存储元素所采用的结构方式是不同的,这样就导致了这些集合有它们各自的特点,供给我们在不同的环境下进行使用。数据存储的常用结构有:堆栈、队列、数组、链表。我们分别来了解一下:
-
堆栈,采用该结构的集合,对元素的存取有如下的特点:
-
先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。
-
栈的入口、出口的都是栈的顶端位置
-
压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
-
弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。
-
-
队列,采用该结构的集合,对元素的存取有如下的特点:
-
先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,安检。排成一列,每个人依次检查,只有前面的人全部检查完毕后,才能排到当前的人进行检查。
-
队列的入口、出口各占一侧。例如,下图中的左侧为入口,右侧为出口。
-
-
数组,采用该结构的集合,对元素的存取有如下的特点:
-
查找元素快:通过索引,可以快速访问指定位置的元素
-
增删元素慢:
-
指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置。如下图
-
指定索引位置删除元素:需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置,原数组中指定索引位置元素不复制到新数组中。如下图
-
-
-
链表,采用该结构的集合,对元素的存取有如下的特点:
-
多个节点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下个人的左手,依次类推,这样多个人就连在一起了。
-
查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素
-
增删元素快:
-
增加元素:操作如左图,只需要修改连接下个元素的地址即可。
-
删除元素:操作如右图,只需要修改连接下个元素的地址即可。
-
-
2.3 List接口常用方法
void add(int index, E element) //指定索引添加元素
E remove(int index) //移除指定索引处元素
E get(int index) //获取指定索引元素
E set(int index, E element) //修改指定索引元素
List<E> subList(int fromIndex, int toIndex) //截取指定索引子集
int indexOf(Object o) //返回指定元素索引位置
import java.util.ArrayList;
import java.util.List;
/*
* List 方法
*
* void add(int index, E element) //指定索引添加元素
*/
public class Demo {
public static void main(String[] args) {
//fun3();
//fun2();
//fun();
}
private static void fun3() {
List <String > al = new ArrayList<>();
al.add("111");// 添加 Collection中 继承来的
al.add("222");
al.add("333");
al.add("444");
System.out.println(al);
al.set(2, "6666");
System.out.println(al);
List<String> subList = al.subList(1, 4);
System.out.println(subList);
int indexOf = subList.indexOf("444");
System.out.println(indexOf);
}
private static void fun2() {
List <Integer > al = new ArrayList<>();
al.add(1);
al.add(2);
al.add(3);
al.add(4);
al.remove(2);
System.out.println(al);
}
private static void fun() {
List <String > al = new ArrayList<>();
al.add("111");// 添加 Collection中 继承来的
al.add("222");
al.add("333");
al.add("444");
al.add(2, "666");
al.add(5, "999");
al.add(6, "000999");
System.out.println(al);
al.remove(6);
System.out.println(al);
}
}
ListIterator<E> listIterator() 注意:用于应对并发修改异常的返回迭代器方法与迭代器
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/*
* ListIterator : Iterator 子接口.
*
* Collection 是得不到ListIterator , 因为 List 专属的.
*
* add添加方法 比较常用.
*/
public class Demo2 {
public static void main(String[] args) {
//fun();
List c = new ArrayList<>();
c.add("孙悟空");
c.add("白骨精");
c.add("唐三藏");
c.add("八戒");
ListIterator li = c.listIterator();
while (li.hasNext()) {
Object object = (Object) li.next();
if (object.equals("白骨精")) {
li.add("白龙马");
}
}
System.out.println(c);
ListIterator listIterator = c.listIterator();
listIterator.next();
System.out.println(listIterator.hasPrevious());
}
private static void fun() {
Collection c = new ArrayList<>();
c.add("孙悟空");
c.add("白骨精");
c.add("唐三藏");
c.add("八戒");
//遍历 集合, 如果 包含 白骨精 ,你就添加一个 白龙马.
Iterator iterator = c.iterator();
while (iterator.hasNext()) {
Object object = (Object) iterator.next();
if (object.equals("白骨精")) {
c.add("白龙马"); //ConcurrentModificationException
}
}
System.out.println(c);
}
}
2.4 具体子类介绍
2.4.1 ArrayList
ArrayList底层数据结构是数组结构。线程不安全的,所以运行速度快,ArrayList的出现替代了Vector。增删慢,查找快,由于日常开发中使用最多的功能为查询数据,遍历数据,所以ArrayList是最常用的集合。目前市面上许多程序员开发时并不严谨,非常随意地使用ArrayList完成任何需求,这种用法是不提倡的。
2.4.2 LinkedList
LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。如下图
LinkedList是List的子类,List中的方法LinkedList都是可以使用,这里就不做详细介绍,我们只需要了解LinkedList的特有方法即可。在开发时,LinkedList集合也可以作为堆栈,队列的结构使用。
方法演示:
LinkedList<String> link = new LinkedList<String>();
//添加元素
link.addFirst("abc1");
link.addFirst("abc2");
link.addFirst("abc3");
//获取元素
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()); //弹出集合中的栈顶元素
}
2.4.3 Vector
Vector:我们可以将其理解为版本旧的、安全的、效率低的ArrayList,Vector中提供了一个独特的取出方式,就是枚举Enumeration。此接口Enumeration的功能与 Iterator 接口的功能是类似的。
有兴趣的同学可以自己了解:
public E elementAt(int index) / get(index )
public E firstElement()
public E lastElement()
public void setElementAt(E obj, int index) set(index,obj)
public void removeElementAt(int index)及其他删除 remove(index)
public Enumeration<E> elements()
import java.util.Enumeration;
import java.util.Vector;
/*
* Vector : 数组结构,单线程的 ArrayList.
*/
public class Demo2 {
public static void main(String[] args) {
Vector<String > vector = new Vector<>();
vector.addElement("111");
vector.addElement("222");
vector.addElement("444");
vector.addElement("333333");
// 获取 枚举.
Enumeration<String> elements = vector.elements();
while (elements.hasMoreElements()) {
String nextElement = elements.nextElement();
System.out.println(nextElement);
}
}
}
第3章 Set接口
查阅Set集合的API介绍,通过元素的equals方法,来判断是否为重复元素,它是个不包含重复元素的集合。Set集合取出元素的方式可以采用:迭代器、增强for。
Set集合有多个子类,这里我们介绍其中的HashSet、LinkedHashSet这两个集合。
3.1 HashSet集合介绍
查阅HashSet集合的API介绍:此类实现Set接口,由哈希表支持(实际上是一个 HashMap集合)。HashSet集合不能保证的迭代顺序与元素存储顺序相同。
HashSet集合,采用哈希表结构存储数据,保证元素唯一性的方式依赖于:hashCode()与equals()方法。
3.2 HashSet集合存储数据的结构(哈希表)
什么是哈希表呢?
哈希表底层使用的也是数组机制,数组中也存放对象,而这些对象往数组中存放时的位置比较特殊,当需要把这些对象给数组中存放时,那么会根据这些对象的特有数据结合相应的算法,计算出这个对象在数组中的位置,然后把这个对象存放在数组中。而这样的数组就称为哈希数组,即就是哈希表。
当向哈希表中存放元素时,需要根据元素的特有数据结合相应的算法,这个算法其实就是Object类中的hashCode方法。由于任何对象都是Object类的子类,所以任何对象有拥有这个方法。即就是在给哈希表中存放对象时,会调用对象的hashCode方法,算出对象在表中的存放位置,这里需要注意,如果两个对象hashCode方法算出结果一样,这样现象称为哈希冲突,这时会调用对象的equals方法,比较这两个对象是不是同一个对象,如果equals方法返回的是true,那么就不会把第二个对象存放在哈希表中,如果返回的是false,就会把这个值存放在哈希表中。
总结:保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。
3.3 HashSet存储JavaAPI中的类型元素
给HashSet中存储JavaAPI中提供的类型元素时,不需要重写元素的hashCode和equals方法,因为这两个方法,在JavaAPI的每个类中已经重写完毕,如String类、Integer类等。
-
创建HashSet集合,存储String对象。
public class HashSetDemo {
public static void main(String[] args) {
//创建HashSet对象
HashSet<String> hs = new HashSet<String>();
//给集合中添加自定义对象
hs.add("zhangsan");
hs.add("lisi");
hs.add("wangwu");
hs.add("zhangsan");
//取出集合中的每个元素
Iterator<String> it = hs.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
}
}
输出结果如下,说明集合中不能存储重复元素:
wangwu
lisi
zhangsan
3.4 HashSet存储自定义类型元素
给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一
-
创建自定义对象Student
public class Student {
private String name;
private int age;
public Student(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;
}
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if(!(obj instanceof Student)){
System.out.println("类型错误");
return false;
}
Student other = (Student) obj;
return this.age == other.age && this.name.equals(other.name);
}
}
-
创建HashSet集合,存储Student对象。
public class HashSetDemo {
public static void main(String[] args) {
//创建HashSet对象
HashSet hs = new HashSet();
//给集合中添加自定义对象
hs.add(new Student("zhangsan",21));
hs.add(new Student("lisi",22));
hs.add(new Student("wangwu",23));
hs.add(new Student("zhangsan",21));
//取出集合中的每个元素
Iterator it = hs.iterator();
while(it.hasNext()){
Student s = (Student)it.next();
System.out.println(s);
}
}
}
输出结果如下,说明集合中不能存储重复元素:
Student [name=lisi, age=22]
Student [name=zhangsan, age=21]
Student [name=wangwu, age=23]
3.5 判断元素唯一原理
3.5.1 ArrayList的contains方法判断元素是否重复原理
ArrayList的contains方法会使用调用方法时,传入的元素的equals方法依次与集合中的旧元素所比较,从而根据返回的布尔值判断是否有重复元素。此时,当ArrayList存放自定义类型时,由于自定义类型在未重写equals方法前,判断是否重复的依据是地址值,所以如果想根据内容判断是否为重复元素,需要重写元素的equals方法。
3.5.2 HashSet的add/contains等方法判断元素是否重复原理
Set集合不能存放重复元素,其添加方法在添加时会判断是否有重复元素,有重复不添加,没重复则添加。
HashSet集合由于是无序的,其判断唯一的依据是元素类型的hashCode与equals方法的返回结果。规则如下:
先判断新元素与集合内已经有的旧元素的HashCode值
-
如果不同,说明是不同元素,添加到集合。
-
如果相同,再判断equals比较结果。返回true则相同元素;返回false则不同元素,添加到集合。
所以,使用HashSet存储自定义类型,如果没有重写该类的hashCode与equals方法,则判断重复时,使用的是地址值,如果想通过内容比较元素是否相同,需要重写该元素类的hashcode与equals方法。
hashCode方法重写规则:将该对象的各个属性值hashCode相加即是整个对象的HashCode值。如果是基本类型,类似int,则直接返回int值就是该属性的hash值,如果是引用类型,类似String,就调用该成员变量的hashCode方法返回该成员变量hash值。这样可以根据对象的内容返回hashCode值,从而可以根据hashCode判断元素是否唯一。
但是由于在一些”碰巧的”情况下,可能出现内容不同但hashCode相同的情况,为了避免这些情况,我们加入一些干扰系数。
可是加入干扰系数后,仍会出现一些”碰巧”的情况,所以我们还要进行equals的二次判断。
public class Student {
String name ;
int age;
public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
// 想要保证 对象的唯一性, 必须重写 equals 和 hashCode();
// (老王, 60 ) , (金莲 ,28 )
// hashCode 而是通过 属性的值计算出来的hashCode
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age; // 91 59
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) // 当前对象与 传参对象 地址 相同 .返回true .
return true;
if (obj == null) //传参对象 为null, 返回 false .
return false;
//当前对象与 传参对象 不一样, 返回false .
if (this.getClass() != obj.getClass()) // getClass() 获取类型
return false;
// 强制转换 .
Student other = (Student) obj;
if (age != other.age) //比较年龄
return false;
if (name == null) { // 比较名字, 我没名,你有名, 返回false
if (other.name != null)
return false;
} else if (!name.equals(other.name)) // 姓名不一样,返回false .
return false;
return true;
}
}
import java.util.HashSet;
/*
* Set : 不包含重复元素的集合.无序 常用 其子类 HashSet .
*
* 你想要使用HashSet保存自定义类型的元素,必须通过重写hashCode和equals方法来保证对象的唯一性.
* 记住: 重写就好使,不重写就挂了. alt + shift + s --> h
*/
public class Demo {
public static void main(String[] args) {
// fun();
HashSet<Student> hashSet = new HashSet<>();
Student student = new Student("金莲", 28);
Student student2 = new Student("小明", 15);
Student student3 = new Student("韩梅梅", 16);
Student student4 = new Student("李雷", 18);
Student student5 = new Student("李雷", 18);
Student student6 = new Student("李雷", 18);
Student student7 = new Student("李雷", 18); // 属性值 全一样, 说明 是同一个对象.
hashSet.add(student);
hashSet.add(student2);
hashSet.add(student3);
hashSet.add(student4);
hashSet.add(student5);
hashSet.add(student6);
hashSet.add(student7);
// 打印 出几个同学?
System.out.println(hashSet);
}
private static void fun() {
HashSet<String> hashSet = new HashSet<>();
// 添加元素
hashSet.add("武大郎");
hashSet.add("武大郎");
hashSet.add("武大郎");
hashSet.add("奥巴马");
hashSet.add("金三胖");
System.out.println(hashSet);
}
}
3.6 Hashset练习
3.6.1 编写一个程序,获取10个1至20的随机数,要求随机数不能重复
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
/*
* 需求:编写一个程序,获取10个1至20的随机数,要求随机数不能重复。
* 并把最终的随机数输出到控制台。
*/
public class Test {
public static void main(String[] args) {
// 创建Random 对象
Random random = new Random();
//创建集合
HashSet<Integer> hashSet = new HashSet<>();
int count = 0;
// 循环获取,循环添加 , 集合的个数做为条件.
while (hashSet.size() < 10) {
count ++; // 计数
// 调用Random 获取随机数
int i = random.nextInt(20) + 1;
// 添加到集合 , HashSet , 无序,唯一的.
hashSet.add(i);
}
System.out.println(count);
System.out.println("添加完毕,遍历集合");
// 遍历 hashSet
Iterator<Integer> iterator = hashSet.iterator();
while (iterator.hasNext()) {
Integer integer = (Integer) iterator.next();
System.out.print(integer +" ");
}
}
}
3.6.2 将集合中的重复元素去掉(自定义类 )
public class Student {
String name;
int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return " Student 姓名=" + name + ", 年龄=" + 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;
Student other = (Student) 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;
}
// 重写hashCode 和equals
}
import java.util.ArrayList;
import java.util.HashSet;
/*
* 将集合中的重复元素去掉(自定义类 )
ArrayList<Student> list = new ArrayList<>();
Student student = new Student("金莲", 28);
Student student2 = new Student("小明", 15);
Student student3 = new Student("韩梅梅", 16);
Student student4 = new Student("李雷", 18);
Student student5 = new Student("李雷", 18);
Student student6 = new Student("李雷", 18);
list.add(student);
list.add(student2);
list.add(student3);
list.add(student4);
list.add(student5);
list.add(student6);
*/
public class Test2 {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
Student student = new Student("金莲", 28);
Student student2 = new Student("小明", 15);
Student student3 = new Student("韩梅梅", 16);
Student student4 = new Student("李雷", 18);
Student student5 = new Student("李雷", 18);
Student student6 = new Student("李雷", 18);
list.add(student);
list.add(student2);
list.add(student3);
list.add(student4);
list.add(student5);
list.add(student6);
System.out.println(list);
//创建hashSet
HashSet<Student> hashSet = new HashSet<>();
for (Student s : list) {
hashSet.add(s);
}
System.out.println(hashSet);
}
}
第4章 集合综合案例
4.1 案例介绍
按照斗地主的规则,完成洗牌发牌的动作。
具体规则:
使用54张牌打乱顺序
三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
4.2 案例需求分析
-
准备牌:
牌可以设计为一个ArrayList<String>,每个字符串为一张牌。
每张牌由花色数字两部分组成,我们可以使用花色集合与数字集合嵌套迭代完成每张牌的组装。
牌由Collections类的shuffle方法进行随机排序。
-
发牌:
将每个人以及底牌设计为ArrayList<String>,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。
-
看牌:
直接打印每个集合。
4.3 实现代码步骤
修改文件编码由GBK修改为UTF-8,因为GBK没有我们要的梅花、方片、黑桃、红桃(♠♥♦♣)等字符。
public class Poker {
public static void 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));
}else if(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集合后,我们提供一个排序的解决方案。
第5章 Collection知识点总结
-
Collection:
|- List 可以存储重复元素,有序的(元素存取顺序)
|- ArrayList
|- LinkedList
|- Set 不能存储重复元素,无序的(元素存取顺序)
|- HashSet
|- LinkedHashSet
-
Collection方法:
-
boolean add(Object e) 把给定的对象添加到当前集合中
-
void clear() 清空集合中所有的元素
-
boolean remove(Object o) 把给定的对象在当前集合中删除
-
boolean contains(Object o) 判断当前集合中是否包含给定的对象
-
boolean isEmpty() 判断当前集合是否为空
-
Iterator iterator() 迭代器,用来遍历集合中的元素的
-
int size() 返回集合中元素的个数
-
Object[] toArray() 把集合中的元素,存储到数组中
-
-
Iterator : 迭代器
-
Object next()返回迭代的下一个元素
-
boolean hasNext()如果仍有元素可以迭代,则返回 true。
-
-
List与Set集合的区别?
List:
它是一个有序的集合(元素存与取的顺序相同)
它可以存储重复的元素
Set:
它是一个无序的集合(元素存与取的顺序可能不同)
它不能存储重复的元素
-
List集合中的特有方法
-
void add(int index, Object element) 将指定的元素,添加到该集合中的指定位置上
-
Object get(int index)返回集合中指定位置的元素。
-
Object remove(int index) 移除列表中指定位置的元素, 返回的是被移除的元素
-
Object set(int index, Object element)用指定元素替换集合中指定位置的元素,返回值的更新前的元素
-
-
ArrayList:
底层数据结构是数组,查询快,增删慢
线程不安全,效率高
-
LinkedList:
底层数据结构是链表,查询慢,增删快
线程不安全,效率高
-
泛型: 用来约束数据的数据类型
-
泛型的格式:
-
<数据类型>
泛型可以使用在 类,接口,方法,变量上
-
泛型的好处
A:提高了程序的安全性
B:将运行期遇到的问题转移到了编译期
C:省去了类型强转的麻烦
-
增强for
简化数组和Collection集合的遍历
格式:
for(元素数据类型 变量 : 数组或者Collection集合) {
使用变量即可,该变量就是元素
}
好处:简化遍历
-
HashSet:
元素唯一不能重复
底层结构是 哈希表结构
元素的存与取的顺序不能保证一致
如何保证元素的唯一的?
重写hashCode() 与 equals()方法
-
LinkedHashSet:
元素唯一不能重复
底层结构是 哈希表结构 + 链表结构
元素的存与取的顺序一致
第6章 本日自习作业:
6.1 知识点相关题
1. 知识点:自定义类 迭代器 泛型 增强for
要求:会使用迭代器
1) 按以下步骤编写代码:
a) 定义类:Cat,包含以下成员:
成员属性(私有):
名称:
颜色:
年龄:
构造方法:
无参
全参
成员方法:
1).get/set方法;
2).重写toString()方法;内部打印所有属性的值;
b) 在main()方法中,按以下步骤编写代码:
-
向集合中添加以下cat对象:
“波斯猫”,“白色”,2
“折耳猫”,“灰色”,1
“加菲猫”,“红色”,3
“机器猫”,“蓝色”,5
遍历集合
1) 使用普通for循环
2) 迭代器(需要指定泛型)
3) 增强for三种方式
6.1.1 泛型的好处是什么?泛型用于什么上?
泛型的好处:
1.提高了程序的安全性
2.将运行期遇到的问题转移到了编译期
3.省去了类型强转的麻烦
泛型的常见应用:
1.泛型类
2.泛型方法
3.泛型接口
6.1.2 请简述List<? extends T>和List<? super T>之间有什么区别?
答案:
List<? extends T> :向下限制
List<? super T> :向上限制
? extends T : 代表接收的泛型类型为T类型或T子类类型
? super T :代表接收的泛型类型为T类型或T父类类型
6.1.3 请编写程序,将3个学生的信息存储到数组中,并遍历数组,获取得到每一个学生信息
6.1.4 请编写程序,存储3个手机对象到ArrayList集合中
a) 使用迭代器进行遍历,要有泛型
b) 打印出三个手机对象的信息,比如颜色,品牌。
6.1.5 描述Collection Frameword的体系,可以简易画图
6.1.6 描述每种接口或类的存储特点
6.1.7 熟练使用迭代器与foreach循环完成Collection/List/Set任意集合迭代
6.1.8 试完成以下需求,使用集合嵌套完成
某企业集合:包含各分校元素
每个分校均有班级元素
班级为一个类,类中包括:
所有同学属性
班级班号属性
班级所在楼层属性
同学为一个类,类中包括:
姓名
学号
年龄
6.1.9 熟练泛型的使用方法,完成简单泛型类定义,并使用。
说出 ? extends E的含义。使用ArrayList的构造方法:public ArrayList(Collection<? extends E> c) 创建集合,添加元素,迭代集合。
6.1.10 简述ArrayList的contains方法判断自定类型对象是否相同时的判断过程
6.1.11 简述HashSet的add方法判断自定类型对象是否相同时的判断过程
6.1.12 阅读Eclipse自动生成的hashCode方法与equals方法。
6.1.13 创建一个LinkedList集合,里面存储的数据类型为Integer类型,将1,2,3,4,5这5个数依次使用push方法,添加到集合中,使得打印集合时显示的效果是[5, 4, 3, 2, 1]
package day08_Test基础练习题; import java.util.LinkedList; /* * 考察LinkedList的push方法 */ public class Test001 { public static void main(String[] args) { //创建一个LinkedList集合 LinkedList<Integer> stack = new LinkedList<Integer>(); //使用push方法 stack.push(1); stack.push(2); stack.push(3); stack.push(4); stack.push(5); //打印此时的集合 System.out.println(stack); } } |
6.1.14 创建LinkedList集合,里面存储的数据类型是String类型,分别将字符串"我","爱","Java","但是","我","更","爱","LOL"添加到集合中,使得打印这个集合最终显示为:[我, 爱, Java, 但是, 我, 更, 爱, LOL]
package day08_Test基础练习题; import java.util.LinkedList; /* * 考察LinkedList的add方法 */ public class Test002 { public static void main(String[] args) { //创建LinkedList集合,里面存储的数据类型是String类型,分别将字符串"我","爱","Java","但是","我","更","爱","LOL"添加到集合中, //使得打印这个集合最终显示为:[我, 爱, Java, 但是, 我, 更, 爱, LOL] LinkedList<String> list = new LinkedList<String>(); list.add("我"); list.add("爱"); list.add("Java"); list.add("但是"); list.add("我"); list.add("更"); list.add("爱"); list.add("LOL"); //打印这个集合 System.out.println(list); } } |
6.1.15 创建一个LinkedList集合,里面存储的数据类型是String类型;创建一个String类型的数组,里面的元素为{"我","爱","LOL","但是","我","更","爱","MONEY"};将String类型的数组里面的元素依次添加到创建的LinkedList集合中,最后打印这个LinkedList集合,显示为:[我, 爱, LOL, 但是, 我, 更, 爱, MONEY]
package day08_Test基础练习题; import java.util.LinkedList; /* * 考察LinkedList和数组之间的灵活运用 */ public class Test003 { /*创建一个LinkedList集合,里面存储的数据类型是String类型; 创建一个String类型的数组,里面的元素为{"我","爱","LOL","但是","我","更","爱","MONEY"}; 将String类型的数组里面的元素依次添加到创建的LinkedList集合中 最后打印这个LinkedList集合,显示为:[我, 爱, LOL, 但是, 我, 更, 爱, MONEY]*/ public static void main(String[] args) { //创建一个LinkedList集合 LinkedList<String> list = new LinkedList<String>(); //创建一个字符串数组 String[] str = {"我","爱","LOL","但是","我","更","爱","MONEY"}; //方法一:使用普通的方式 /*for (int i = 0; i < str.length; i++) { list.add(str[i]); } System.out.println(list);*/ System.out.println("---------------------------------------"); //方法二:使用增强for循环的方式 for (String string : str) { list.add(string); } System.out.println(list); } }
|
6.1.16 在括号中选择对应结构的解释,什么是数据结构(),什么是数组结构(),什么是链表结构(),什么是队列结构(),什么是栈结构()
A:数据存储和组织方式
B:容器先进后出规则
C:容器先进先出的规则
D:每个元素指向下一个元素
E:一块连续的存储区域
答案说明: 数组结构:一块连续的存储区域,查询速度快,添加速度慢 链表结构:每个元素指向下一个元素,添加快,查询慢 队列结构:容器先进先出的规则 栈结构:容器先进后出规则 |
6.1.17 给定一个字符串数组,数组内容为:{"我","爱","编程","但是","我","更","爱","IPHONE"}创建一个集合,这个集合存储上面字符串数组里面的元素,并且不存储重复的,此时的集合打印效果为:[更, 爱, IPHONE, 我, 编程, 但是],遍历集合,将集合中元素是IPHONE的元素删除,过后添加一个新的元素,元素为MJ,最终的集合的打印效果为:[更, 爱, 我, 编程, MJ, 但是]
package day08_Test基础练习题; import java.util.HashSet; public class Test005 { public static void main(String[] args) { HashSet<String> list = new HashSet<>(); String[] str = {"我","爱","编程","但是","我","更","爱","IPHONE"}; for (int i = 0; i < str.length; i++) { list.add(str[i]); } System.out.println(list); for (String string : str) { if(string.equals("IPHONE")){ list.remove(string); //添加元素 list.add("MJ"); } } System.out.println("操作过后的元素为:"); System.out.println(list); } } |
6.1.18 创建Person类并测试
1.编写Person类,有age属性(数据类型是int)和name属性(数据类型是String),且都被private修饰,提供get/set方法,不重写equals方法和hashCode方法
2.编写Test类,在Test类的main方法中定义一个ArrayList集合,集合内存放的元素的数据类型是Person类
-
分别创建三个对象,对象p1的年龄为10,名字为李四;对象p2的年龄为20,名字为王五;对象p3的年龄为30,名字为小强
-
将三个对象依次添加到定义的集合中
-
创建一个对象p4,此对象的年龄为30,名字为小强
-
调用集合的contains方法,将p4作为参数传递进去,查看打印的是true还是false
-
如果打印为false,那么在Person类中重写hashCode和equals方法
-
查看打印的结果是否为true
思考:是否还能够将重复的对象添加成功?
Person类: package day08_Test基础练习题; public class Person { private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Person(int age, String name) { super(); this.age = age; this.name = name; } public Person() { super(); // TODO Auto-generated constructor stub } @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; Person other = (Person) 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; } @Override public String toString() { return "Person [age=" + age + ", name=" + name + "]"; } } |
测试类: package day08_Test基础练习题; import java.util.ArrayList; public class Test006 { public static void main(String[] args) { //定义一个ArrayList集合,集合中存储的数据类型是一个类,类的名字叫Person ArrayList<Person> list = new ArrayList<>(); //使用匿名对象的方式分别将三个Person对象添加到集合中 list.add(new Person(10,"李四")); list.add(new Person(20,"王五")); list.add(new Person(30,"小强")); //创建一个新的对象 Person p4 = new Person(30, "小强"); //打印结果,在Person类中有equals方法和hashCode方法时为true,反之没有时为false System.out.println(list.contains(p4)); System.out.println(list); System.out.println("------------------是否还能够将重复的对象添加成功?-------------------"); list.add(new Person(30, "小强")); System.out.println(list); } } |
6.2 代码题
6.2.1 点菜系统
/*
* 菜品类. 名字 ,价格, id. corsh 顶配的类.
*/
public class Cai {
private String name; // 菜名
private double price ;//价格
private String id ; // 菜id . 001
public Cai() {
super();
}
public Cai(String name, double price, String id) {
super();
this.name = name;
this.price = price;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return id + "--[" + name +":"+ price+"]" ;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
long temp;
temp = Double.doubleToLongBits(price);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Cai other = (Cai) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (Double.doubleToLongBits(price) != Double.doubleToLongBits(other.price))
return false;
return true;
}
}
/*
* 点菜程序.
* 1. 创建一个集合,保存所有的菜品.
* 2. 录入菜品的编号, 遍历集合,选择菜品 ,保存到新的集合.
* 3. exit . 不点了, 遍历 新的集合,即是你点过菜.
*
* =====================
* 根据价格去选择!
* 16块钱.
* 18块钱.
*
* ========================
*
* 带肉的 . 简单实现 . 菜名有肉就行. 菇
* 复杂实现 . 根据 菜的原料 ,去判断.
*
* 实现:
*
* 1. 定义类 : 菜品类. 名字 ,价格, id.
* 2. 创建菜品对象,保存到集合.
* a. 通过 id ,点菜 .
* b. 价格 选菜 .
* c. 菜名字 ,选菜.
*
* 3.选择的菜品 ,添加到点菜的集合中.
* 4. 遍历集合.
*/
public class Test {
public static void main(String[] args) {
Cai cai = new Cai("锅包肉", 28.8, "001");
System.out.println(cai);
}
}
6.2.2 LinkedList集合题
步骤一:创建LinkedList集合,里面存储的元素的数据类型是Integer
步骤二:将1,2,3,4,5依次添加到集合中
步骤三:调用集合的push方法将6,7,8,9,10依次添加到集合中
步骤四:最后打印集合的效果为:[10, 9, 8, 7, 6, 1, 2, 3, 4, 5]
package day08_Test拓展三道编程题; import java.util.LinkedList; /* * LinkedList的弹栈和压站 * push * add */ public class Test001 { public static void main(String[] args) { //创建LinkedList集合,里面存储的元素的数据类型是Integer LinkedList<Integer> stack = new LinkedList<Integer>(); //将1,2,3,4,5添加到集合中 stack.add(1); stack.add(2); stack.add(3); stack.add(4); stack.add(5); System.out.println(stack); System.out.println("------------------------------"); //调用集合的push方法将6,7,8,9,10添加到集合中 stack.push(6); stack.push(7); stack.push(8); stack.push(9); stack.push(10); //最后打印集合的效果为:[10, 9, 8, 7, 6, 1, 2, 3, 4, 5] System.out.println(stack); } } |
6.2.3 set集合题
1.定义一个员工类(Employee),要求具有以下属性:
a)empName(姓名)
b)sex(性别)
c)age(年龄)
2.定义一个List集合,存储以下”Employee”信息:
a)孙红雷 男 20
b)葛优 男 30
c)黄渤 男 25
d)孙俪 女 18
3.要求:
a)在”葛优”前面添加一个学员:王骏迪 女 17
b)定义一个Set集合存储Employee信息,要求不能存储相同的:姓名,性别,年龄的员工信息.并测试;
Employee类: package day08_Test拓展三道编程题; public class Employee { private String name; private char sex; private int age; public Employee(String name, char sex, int age) { super(); this.name = name; this.sex = sex; this.age = age; } public Employee() { super(); // TODO Auto-generated constructor stub } public String getName() { return name; } public void setName(String name) { this.name = name; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Employee [name=" + name + ", sex=" + sex + ", 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()); result = prime * result + sex; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Employee other = (Employee) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (sex != other.sex) return false; return true; } }
|
测试类: package day08_Test拓展三道编程题; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class Test002 { public static void main(String[] args) { List<Employee> empList = new ArrayList<>(); empList.add(new Employee("孙红雷",'男',20)); empList.add(new Employee("葛优",'男',30)); empList.add(new Employee("孙俪",'女',18)); //在”葛优”前面添加一个学员:王骏迪 女 17 empList.add(1,new Employee("王骏迪",'女',17)); //使用Set存储 Set<Employee> empSet = new HashSet<>(); empSet.add(new Employee("孙红雷",'男',20)); empSet.add(new Employee("葛优",'男',30)); empSet.add(new Employee("葛优",'男',30));//存储相同元素--失败 System.out.println(empSet); } } |
6.2.4 定义集合并去除重复元素
1.定义一个存储数字的集合,并随意存储一些数据(要求这些数据中包含一些重复数字);
2.使用一种很方便的方式将这个集合中重复的数字去掉.
package day08_Test拓展三道编程题; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class Test003 { /* * 1.定义一个存储数字的集合,并随意存储一些数据(要求这些数据中包含一些重复数字); 2.使用一种很方便的方式将这个集合中重复的数字去掉. */ public static void main(String[] args) { List<Integer> intList = new ArrayList<>(); intList.add(10); intList.add(20); intList.add(20); intList.add(30); intList.add(30); Set<Integer> intSet = new HashSet<>(); intSet.addAll(intList); System.out.println(intSet); } } |
参考链接:https://mp.weixin.qq.com/s/8PDpnjkPtcT6-tzUNjaq8Q