全站资源开放下载,感谢广大网友的支持
链接失效请移步职涯宝平台的学习路线|资源下载分类
支持用户留言评论_客服实时在线_问题解决更快

第十八天 集合-泛型&list接口&set接口【悟空教程】

71
发表时间:2018-09-06 15:13

第十八天 集合-泛型&list接口&set接口【悟空教程】

第18天 集合

第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;

}

@Override

public String toString() {

return "Student [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 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 ArrayListcontains方法判断元素是否重复原理

ArrayList的contains方法会使用调用方法时,传入的元素的equals方法依次与集合中的旧元素所比较,从而根据返回的布尔值判断是否有重复元素。此时,当ArrayList存放自定义类型时,由于自定义类型在未重写equals方法前,判断是否重复的依据是地址值,所以如果想根据内容判断是否为重复元素,需要重写元素的equals方法。


3.5.2 HashSetadd/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;

}

@Override

public String toString() {

return "Student [name=" + name + ", age=" + age + "]";

}

// 想要保证 对象的唯一性, 必须重写 equals hashCode();


// (老王, 60 )  , (金莲 ,28 )

// hashCode 而是通过 属性的值计算出来的hashCode

@Override

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保存自定义类型的元素,必须通过重写hashCodeequals方法来保证对象的唯一性.

* 记住: 重写就好使,不重写就挂了. 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 编写一个程序,获取10120的随机数,要求随机数不能重复

import java.util.HashSet;

import java.util.Iterator;

import java.util.Random;

/*

* 需求:编写一个程序,获取10120的随机数,要求随机数不能重复。

* 并把最终的随机数输出到控制台。

*/

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<? extendsE> c)  创建集合,添加元素,迭代集合。

6.1.10 简述ArrayListcontains方法判断自定类型对象是否相同时的判断过程


6.1.11 简述HashSetadd方法判断自定类型对象是否相同时的判断过程


6.1.12 阅读Eclipse自动生成的hashCode方法与equals方法。


6.1.13 创建一个LinkedList集合,里面存储的数据类型为Integer类型,将1,2,3,4,55个数依次使用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  顶配的类.