集合

集合

1.Collection

1.1 集合概述

集合类的特点:提供一种存储空间可变的存储类型,存储的数据容量可以随时发生改变

1.2 体系结构

集合体系结构图

1.3 Collection概述和使用

Collection集合概述

  • 是单列集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
  • JDK不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现

创建Collection集合的对象

  • 多态的方式
  • 具体的实现类ArrayList
package com.advanced.collection;

import java.util.ArrayList;
import java.util.Collection;

/*
    创建Collection集合对象
        多态的方式
        ArrayList
 */
public class CollectionDemo01 {
    public static void main(String[] args) {
        //创建Collection集合的对象
        Collection<String> c1 = new ArrayList<String>();

        //添加元素:boolean add(E e)
        c1.add("hello");
        c1.add("world");
        c1.add("java");

        //输出集合对象
        System.out.println(c1);
    }
}

1.4 Collection常用方法

方法名说明
boolean add(E e)添加元素
boolean remove(Object o)从集合中移除指定元素
void clear()清空集合中元素
boolean contains(Object o)判断集合中是否存在指定元素
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中元素个数
package com.advanced.collection;

import java.util.ArrayList;
import java.util.Collection;

//Collection常用方法
public class CollectionDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        Collection<String> c1 = new ArrayList<String>();

        //boolean add(E e):添加元素
        System.out.println(c1.add("java"));//true,调用add方法永远返回true
        System.out.println(c1.add("hello"));//true
        System.out.println(c1.add("hello"));//true

        //输出集合对象
        System.out.println(c1);//[java, hello, hello]

        //boolean remove(Object o):从集合中移除指定元素
        System.out.println(c1.remove("hello"));//true
        System.out.println(c1.remove("javase"));//false
        System.out.println(c1);//[java, hello]

        //void clear():清空集合中元素
//        c1.clear();
        System.out.println(c1);//[]

        //boolean contains(Object o):判断集合中是否存在指定元素
        System.out.println(c1.contains("java"));//true
        System.out.println(c1.contains("javase"));//false

        //boolean isEmpty():判断集合是否为空
        System.out.println(c1.isEmpty());//false
//        c1.clear();
        System.out.println(c1.isEmpty());//true

        //int size():集合的长度,也就是集合中元素个数
        System.out.println(c1.size());//2
    }
}

1.5 Collection集合遍历

Iterator:迭代器,集合的专用遍历方式

  • Iterator iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
  • 迭代器是通过集合的iterator()方法得到的,所以我们说它是依赖于集合而存在的

Iterator中常用方法

  • E next():返回迭代中的下一个元素
  • boolean hasNext():如果迭代具有更多元素,则返回true
package com.advanced.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

//Iterator迭代器
public class IteratorDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        Collection<String> c1 = new ArrayList<String>();

        //添加元素
        c1.add("hello,world!");
        c1.add("java,javase,javaee");
        System.out.println(c1);//[hello,world!, java,javase,javaee]

        //Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
        Iterator<String> it = c1.iterator();

        //E next():返回迭代中的下一个元素
//        System.out.println(it.next());//hello,world!
//        System.out.println(it.next());//java,javase,javaee
//        System.out.println(it.next());//NoSuchElementException

        //boolean hasNext():如果迭代具有更多元素,则返回true
//        System.out.println(it.hasNext());//false
        while (it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
    }
}

1.6 案例

Collection集合存储学生对象并遍历

需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合

package com.advanced.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

//案例:Collection集合存储学生对象并遍历
public class CollectionDemo03 {
    public static void main(String[] args) {
        //创建集合对象
        Collection<Student> c = new ArrayList<Student>();

        //创建学生对象
        Student s1 = new Student("罗沙雕", 20);
        Student s2 = new Student("陈大锤", 25);
        Student s3 = new Student("苏斩妹", 28);

        //添加学生对象到集合
        c.add(s1);
        c.add(s2);
        c.add(s3);

        //迭代器遍历集合
        Iterator<Student> it = c.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
    }
}

//Student类
class Student{
    String name;
    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 +
                '}';
    }
}

2.List

2.1 List集合概述

List集合概述:

  • 有序集合(也称为序列),用户可以精确控制每个元素插入的位置,用户可以通过整数索引访问元素,并搜索列表中的元素
  • 与Set集合不同,列表通常允许重复的元素

List集合特点:

  • 有序:存储和取出的元素顺序一致
  • 可重复:存储的元素可以重复
package com.advanced.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

//List集合概述
public class ListDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        List<String> list = new ArrayList<String>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");
        list.add("hello");

        //输出集合元素
        System.out.println(list);//[hello, world, java, hello]

        //迭代器方式遍历集合
        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
    }
}

2.2 List集合特有方法

方法名说明
void add(int index,E element)在此集合中指定位置插入指定的元素
E remove(int index)删除指定索引处的元素,返回被删除的元素
E set(int index,E element)修改指定索引处的元素,返回被修改的元素
E get(int index)返回指定索引处的元素
package com.advanced.collection;

import java.util.ArrayList;
import java.util.List;

//List集合特有方法
public class ListDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        List<String> list = new ArrayList<String>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        //输出集合元素
        System.out.println(list);//[hello, world, java]
        System.out.println("========================");

        //void add(int index,E element):在此集合中指定位置插入指定的元素
//        list.add(4,"javase");//IndexOutOfBoundsException
        list.add(0,"javase");
        System.out.println(list);//[javase, hello, world, java]
        System.out.println("========================");

        //E remove(int index):删除指定索引处的元素,返回被删除的元素
        String s1 = list.remove(0);
        System.out.println(s1);//javese
        System.out.println(list);//[hello, world, java]
        System.out.println("========================");

        //E set(int index,E element):修改指定索引处的元素,返回被修改的元素
        String s2 = list.set(2, "javase");
        System.out.println(s2);//java
        System.out.println(list);//[hello, world, javase]
        System.out.println("========================");

        //E get(int index):返回指定索引处的元素
        String s3 = list.get(2);
        System.out.println(s3);//javase
        System.out.println("========================");

        //遍历集合
        System.out.println(list.get(0));//hello
        System.out.println(list.get(1));//world
        System.out.println(list.get(2));//javase
        System.out.println("========================");

        //用for循环改进遍历
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            System.out.println(s);
        }
    }
}

2.3 案例

List集合存储学生对象并遍历

需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合

package com.advanced.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

//案例:List集合存储学生对象并遍历
public class ListDemo03 {
    public static void main(String[] args) {
        //创建集合对象
        List<Student> list = new ArrayList<Student>();

        //创建学生对象
        Student s1 = new Student("罗沙雕", 20);
        Student s2 = new Student("陈大锤", 25);
        Student s3 = new Student("苏斩妹", 28);

        //添加学生对象到集合
        list.add(s1);
        list.add(s2);
        list.add(s3);

        //方式1:使用迭代器方式遍历
        Iterator<Student> it = list.iterator();
        while(it.hasNext()){
            Student student = it.next();
            System.out.println(student);
        }

        //方式2:使用for循环方式遍历
        for (int i = 0; i < list.size(); i++) {
            Student student = list.get(i);
            System.out.println(student);
        }
    }
}

2.4 并发修改异常

并发修改异常:ConcurrentModificationException

产生原因:迭代器遍历过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素中判断实际修改值与预期修改值不一致

解决方法:用for循环遍历,然后集合对象做相应的操作即可

package com.advanced.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

//ConcurrentModificationException:当不允许这样的修改时,可以通过检测到对象的并发修改方法来抛出此异常
public class ListDemo04 {
    public static void main(String[] args) {
        //创建List集合对象
        List<String> list = new ArrayList<String>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        //遍历:如果集合中有元素"world",则添加一个元素"javase"
/*
        Iterator<String> it = list.iterator();//多态方式创建迭代器对象
        while (it.hasNext()){
            String s = it.next();//ConcurrentModificationException:并发修改异常
            if (s.equals("world")){
                list.add("javase");
            }
        }
*/

        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);//get()方法未做实际修改值与预期修改值判断
            if (s.equals("world")){
                list.add("javase");
            }
        }
        System.out.println(list);
    }
}

并发修改异常源码分析

//源代码截取内容
public interface List<E> extends Collection<E> {
    boolean add(E e);
    E get(int index);
    Iterator<E> iterator();
}

public class ArrayList<E> extends AbstractList<E> implements List<E>{
    public Iterator<E> iterator() {
         return new Itr();
    }
        
    private class Itr implements Iterator<E> {            
        int expectedModCount = modCount;
        /*
            modCount:实际修改集合的次数
            expectedModCount:预期修改集合的次数
        */       
        public E next() {//需要判断比较实际修改值与预期修改值是否相等
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)//这里判断实际修改值与预期修改值导致抛出异常
                throw new ConcurrentModificationException();
        }
    }
    
    public boolean add(E e) {//add()方法添加元素后modCount++
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
    }
    
    public E get(int index) {//不需要判断比较实际修改值与预期修改值是否相等
        rangeCheck(index);   
        return elementData(index);
    }   
}

2.5 ListIterator

ListIterator:列表迭代器

  1. 通过List集合的ListIterator方法得到,所以说它是List集合特有的迭代器
  2. 允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中的迭代器的当前位置

ListIterator中的常用方法

  • E next():返回迭代中的下一个元素
  • boolean hasNext():如果迭代器中具有更多元素,则返回true
  • E previous():返回列表中的上一个元素
  • boolean hasPrevious():如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回true
  • void add(E e):在列表中插入指定的元素
package com.advanced.collection;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

//ListIterator列表迭代器
public class ListIteratorDemo01 {
    public static void main(String[] args) {
        //创建列表对象
        List<String> list = new ArrayList<String>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        //创建ListIterator迭代器对象
        ListIterator<String> st = list.listIterator();

        //E next():返回迭代中的下一个元素
        String s = st.next();
        String s2 = st.next();

        System.out.println(s);//hello

        //boolean hasNext():如果迭代器中具有更多元素,则返回true
        System.out.println(st.hasNext());//true

        //E previous():返回列表中的上一个元素
        String previous = st.previous();
        System.out.println(previous);//world

        //boolean hasPrevious():如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回true
        System.out.println(st.hasPrevious());//true

        System.out.println("============================");

        //1.next方法向后遍历
        while (st.hasNext()) {
            String s1 = st.next();
            System.out.println(s1);
        }

        System.out.println("============================");

        //2.previous方法向前遍历
        while (st.hasPrevious()) {
            String p1 = st.previous();
            System.out.println(p1);
        }

        System.out.println("============================");

        //void add(E e):在列表中插入指定的元素
        st.add("javase");
        while (st.hasPrevious()) {
            String previous1 = st.previous();
            System.out.println(previous1);
        }

        while(st.hasNext()){
            String s1 = st.next();
            if (s1.equals("world")){
                st.add("javaee");//使用列表迭代器add方法,不会报并发修改异常
            }
        }
    }
}

不会并发修改异常源码分析

//源码分析
public interface List<E> extends Collection<E> {
    boolean add(E e);
    ListIterator<E> listIterator();
}

public interface ListIterator<E> extends Iterator<E> {
    boolean hasNext();
    E next();
    void add(E e);
}

public class ArrayList<E> extends AbstractList<E> implements List<E>{
    public ListIterator<E> listIterator() {
        return new ListItr(0);
    }

    private class ListItr extends Itr implements ListIterator<E> {
        public void add(E e) {
            checkForComodification();
            try {
                int i = cursor;
                ArrayList.this.add(i, e);
                cursor = i + 1;
                lastRet = -1;
                expectedModCount = modCount; //列表迭代器add方法将实际修改值赋值给预期修改值
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }

    private class Itr implements Iterator<E> {
        int expectedModCount = modCount;//开始时,实际修改值与预期修改值一致
        /*
            modCount:实际修改集合的次数
            expectedModCount:预期修改集合的次数
        */
        public E next() {//需要判断比较实际修改值与预期修改值是否相等
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)//这里判断实际修改值与预期修改值导致抛出异常
                throw new ConcurrentModificationException();
        }
    }
}

modCount:实际修改集合的次数

expectedModCount:预期修改集合的次数

开始时,实际修改值与预期修改值一致,expectedModCount = modCount,List集合对象调用add()方法后,modCount++,此时modCount != expectedModCount,Iterator迭代器对象调用next()方法,抛出ConcurrentModificationException;而列表迭代器对象调用add()方法后,expectedModCount = modCount,因此再调用next()方法,实际修改值与预期修改值一致,不会报并发修改异常

2.6 增强for循环

增强for:简化数组和Collection集合的遍历

  • 实现Iterable接口的类允许其对象称为增强for语句的目标
  • 它是JDK5之后出现的,其内部原理是一个Iterator迭代器

增强for格式:

for(元素数据类型 变量名:数组或Collection集合){

​ //在此处使用变量即可,该变量就是元素

}

package com.advanced.collection;

import java.util.ArrayList;
import java.util.List;

//增强for
public class LsitForeachDemo {
    public static void main(String[] args) {
        //创建List集合对象
        List<String> list = new ArrayList<String>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        //增强for遍历列表元素
        for (String s:list) {
            System.out.println(s);
        }

        System.out.println("==================");

        //内部原理:一个Iterator迭代器
/*
        for (String s:list) {
            if (s.equals("world"))
                list.add("javase");//ConcurrentModificationException
        }
*/
    }
}

2.7 案例

List集合存储学生对象用三种方式遍历

需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合

package com.advanced.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

//案例:List集合存储学生对象用三种方式遍历
public class ListDemo05 {
    public static void main(String[] args) {
        //创建List列表对象
        List<Student> list = new ArrayList<Student>();

        //创建学生对象
        Student s1 = new Student("上清天尊", 20);
        Student s2 = new Student("太清天尊", 23);
        Student s3 = new Student("玉清天尊", 18);

        //添加学生对象到列表
        list.add(s1);
        list.add(s2);
        list.add(s3);

        //创建Iterator迭代器对象
        Iterator<Student> it = list.iterator();

        //创建ListIterator迭代器对象
        ListIterator<Student> lit = list.listIterator();

        //方式一:普通for方式遍历
        for (int i = 0; i < list.size(); i++) {
            Student s = list.get(i);
            System.out.println(s);
        }
        System.out.println("================================");

        //方式二:Iterator迭代器遍历
        while (it.hasNext()){
            Student s = it.next();
            System.out.println(s);
        }
        System.out.println("================================");

        //方式三:增强for遍历
        for (Student s:list) {
            System.out.println(s);
        }
    }
}

2.8 常见数据结构之栈

数据进入栈模型的过程称为:压/进栈

数据离开栈模型的过程称为:弹/出栈

栈是一种数据先进后出的模型

img

2.9 常见数据结构之队列

数据从后端进入队列模型的过程称为:入队列

数据从前端离开队列模型的过程称为:出队列

队列是一种数据先进先出的模型

img

2.10 常见数据结构之数组

查询数据通过索引定位,查询任意数据耗时相同,查询效率高

删除数据时,需要将原始数据删除,并将后面数据每个前移,删除效率低

添加数据时,需要将添加数据位置后数据后移,再添加元素,添加效率低

数组是一种查询快,增删慢的模型

2.11 常见数据结构之链表

链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

链表是一种查询慢,增删快的模型(对比数组)

img

2.12 List集合子类特点

List集合常用子类:ArrayList,LinkedList

  1. ArrayList:底层数据结构是数组,查询快,增删慢
  2. LinkedList:底层数据结构是链表,查询慢,增删快

练习:分别使用ArrayList和LinkedList存储字符串数据并遍历

  1. ArrayList存储字符串的三种遍历方式

    package com.advanced.collection;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    
    //练习:分别使用ArrayList和LinkedList存储字符串数据并遍历
    public class ListDemo06 {
        public static void main(String[] args) {
            //创建List列表对象
            ArrayList<String> list = new ArrayList<String>();
    
            //添加字符串元素
            list.add("hello");
            list.add("world");
            list.add("java");
    
            //迭代器方式遍历
            Iterator<String> it = list.iterator();
            while (it.hasNext()){
                String s = it.next();
                System.out.println(s);
            }
            System.out.println("==========");
    
            //普通for遍历
            for (int i = 0; i < list.size(); i++) {
                String s = list.get(i);
                System.out.println(s);
            }
            System.out.println("==========");
    
            //增强for遍历
            for(String s:list){
                System.out.println(s);
            }
        }
    }
    
  2. LinkedList存储字符串的三种遍历方式

    package com.advanced.collection;
    
    import java.util.Iterator;
    import java.util.LinkedList;
    
    //练习:分别使用ArrayList和LinkedList存储字符串数据并遍历
    public class ListDemo07 {
        public static void main(String[] args) {
            //创建List列表对象
            LinkedList<String> list = new LinkedList<String>();
    
            //添加字符串元素
            list.add("hello");
            list.add("world");
            list.add("java");
    
            //普通for遍历
            for (int i = 0; i < list.size(); i++) {
                String s = list.get(i);
                System.out.println(s);
            }
            System.out.println("==========");
    
            //增强for遍历
            for(String s : list){
                System.out.println(s);
            }
            System.out.println("==========");
    
            //迭代器方式遍历
            Iterator<String> iterator = list.iterator();
            while (iterator.hasNext()){
                String s = iterator.next();
                System.out.println(s);
            }
        }
    }
    

2.13 案例

ArrayList集合存储学生对象用三种方式遍历

需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合

package com.advanced.collection;

import java.util.ArrayList;
import java.util.Iterator;

//ArrayList集合存储学生对象用三种方式遍历
public class ListDemo08 {
    public static void main(String[] args) {
        //创建集合对象
        ArrayList<Student> list = new ArrayList<>();

        //创建学生对象
        Student s1 = new Student("Tom", 20);
        Student s2 = new Student("Sam", 25);
        Student s3 = new Student("Jaker", 23);

        //添加元素
        list.add(s1);
        list.add(s2);
        list.add(s3);

        //方式1:迭代器方式遍历
        Iterator<Student> it = list.iterator();
        while (it.hasNext()){
            Student s = it.next();
            System.out.println(s);
        }
        System.out.println("==================");

        //方式2:普通for遍历
        for (int i = 0; i < list.size(); i++) {
            Student s = list.get(i);
            System.out.println(s);
        }
        System.out.println("==================");

        //方式3:增强for遍历
        for(Student s:list){
            System.out.println(s);
        }
    }
}

LinkedList集合存储学生对象用三种方式遍历

具体实现代码与上面类似

2.14 LinkedList特有方法

方法名说明
public void addFirst(E e)在该列表开头插入指定元素
public void addLast(E e)将指定元素追加到此列表末尾
public E getFirst()返回此列表第一个元素
public E getLast()返回此列表最后一个元素
public E removeFirst()从此列表删除并返回第一个元素
public E removeLast()从此列表删除并返回最后一个元素
package com.advanced.collection;

import java.util.LinkedList;

//LinkedList特有方法
public class ListDemo09 {
    public static void main(String[] args) {
        //创建集合对象
        LinkedList<String> list = new LinkedList<>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        System.out.println(list);//[hello, world, java]
        System.out.println("=====================================");

        //public void addFirst(E e)在该列表开头插入指定元素
        list.addFirst("javase");
        System.out.println(list);//[javase, hello, world, java]
        System.out.println("=====================================");

        //public void addLast(E e)将指定元素追加到此列表末尾
        list.addLast("javaee");
        System.out.println(list);//[javase, hello, world, java, javaee]
        System.out.println("=====================================");

        //public E getFirst()返回此列表第一个元素
        String first = list.getFirst();
        System.out.println(first);//javase
        System.out.println("=====================================");

        //public E getLast()返回此列表最后一个元素
        String last = list.getLast();
        System.out.println(last);//javaee
        System.out.println("=====================================");

        //public E removeFirst()从此列表删除并返回第一个元素
        String removeFirst = list.removeFirst();
        System.out.println(removeFirst);//javase
        System.out.println(list);//[hello, world, java, javaee]
        System.out.println("=====================================");

        //public E removeLast()从此列表删除并返回最后一个元素
        String removeLast = list.removeLast();
        System.out.println(removeLast);//javaee
        System.out.println(list);//[hello, world, java]
    }
}

3.Set

3.1 Set集合概述

Set集合特点:

  1. 不包含重复元素的集合
  2. 没有带索引的方法,所以不能使用普通for循环遍历

Set集合练习:存储字符串并遍历

package com.advanced.collection;

import java.util.HashSet;
import java.util.Set;

//Set集合
//HashSet:对集合的迭代顺序不做任何保证
public class SetDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        Set<String> set = new HashSet<String>();

        //添加元素
        set.add("hello");
        set.add("world");
        set.add("java");

        //不包含重复元素
        set.add("world");

        //遍历元素
        for(String s:set){
            System.out.println(s);
        }
    }
}

3.2 哈希值

哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

Object类中有一个方法可以获取对象的哈希值

​ public int hashCode():返回对象的哈希值

哈希值特点

  1. 同一对象多次调用hashCode()方法返回的哈希值相同
  2. 默认情况下,不同对象调用hashCode()方法返回的哈希值不同。而重写hashCode()方法,可以实现不同对象的哈希值相同
package com.advanced.collection;

//哈希值
public class SetDemo02 {
    public static void main(String[] args) {
        //创建学生对象
        Student s1 = new Student("Tom", 20);
        Student s2 = new Student("Sam", 23);

        //同一对象多次调用hashCode()方法返回哈希值相同
        System.out.println(s1.hashCode());//460141958
        System.out.println(s1.hashCode());//460141958

        //默认情况下,不同对象的哈希值是不同的
        //通过方法重写,可以实现不同对象的哈希值相同
        System.out.println(s2.hashCode());//1163157884
        System.out.println("==============================");

        System.out.println("hello".hashCode());//99162322
        System.out.println("world".hashCode());//113318802
        System.out.println("java".hashCode());//3254818
        System.out.println("world".hashCode());//113318802
        System.out.println("==============================");

        System.out.println("北京".hashCode());//679541
        System.out.println("成都".hashCode());//815341
        System.out.println("重地".hashCode());//1179395
        System.out.println("通话".hashCode());//1179395
    }
}

3.3 HashSet集合概述

  1. HashSet集合特点:

    • 底层数据结构是哈希表
    • 对集合的迭代顺序不做任何保证,也就是说不保证存储和取出元素顺序一致
    • 没有带索引的方法,所以不能使用普通for循环遍历
    • 由于是Set集合,所以是不包含重复元素的集合
  2. HashSet集合练习:

    • 存储字符串并遍历
package com.advanced.collection;

import java.util.HashSet;

//HashSet集合:存储字符串并遍历
public class HashSetDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        HashSet<String> hs = new HashSet<String>();

        //添加元素
        hs.add("hello");
        hs.add("world");
        hs.add("java");

        //遍历元素
        for(String s: hs){
            System.out.println(s);
        }
    }
}

3.4 HashSet元素唯一性

HashSet集合存储元素:要保证元素的唯一性,需要重写hashCode()equals()方法

HashSet添加一个元素的过程:

1.调用对象的hashCode()方法获取对象哈希值

2.根据调用对象的哈希值计算对象的存储位置

3.判断该位置是否有元素存在

​ ①如果没有,则将元素存储到该位置;

​ ②如果有,遍历该位置的所有元素,和新存入的元素比较哈希值是否相同,

​ Ⅰ. 如果都不相同,则将元素存储到该位置

​ Ⅱ. 如果有相同的,调用equals()方法比较对象内容是否相同

​ i)如果不同,将元素存储到该位置

​ ii)如果相同,说明元素重复,不存储

源码分析

//HashSet集合保证元素唯一性源码分析

hs.add("world");

//================================================================

public class HashSet<E> extends AbstractSet<E> implements Set<E>{
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
}

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>{
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
    //hash值和元素的hashCode()方法有关
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        
        //如果哈希表未初始化,就对它初始化
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
            
        //根据对象的哈希值计算对象的存储位置,如果该位置没有元素,就存储该元素    
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            
            /*
                存入的元素和以前的元素比较哈希值
                    如果哈希值不同,会继续向下执行,把元素添加到集合
                    如果哈希值相同,则会调用对象的equals()方法比较
                        如果返回false,会继续向下执行,把元素添加到集合
                        如果返回true,说明元素重复,不存储
            */
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
        }
    }
}

3.5 常见数据结构之哈希表

哈希表:

  • JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
  • JDK8之后,在长度比较长的时候,底层实现了优化

img

3.6 案例

HashSet集合存储学生对象并遍历

需求:创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合

​ 需求:学生对象的成员变量值相同,我们就认为是同一个对象

注意:学生类需要重写hashCode()和equals(),自动生成即可

package com.advanced.collection;

import java.util.HashSet;

//HashSet集合存储学生对象并遍历
    //需求:创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合
        //需求:学生对象的成员变量值相同,我们就认为是同一个对象
public class HashSetDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        HashSet<Student> hs = new HashSet<Student>();

        //创建学生对象
        Student s1 = new Student("Tom", 5);
        Student s2 = new Student("Jerry", 3);
        Student s3 = new Student("Sam", 25);
        Student s4 = new Student("Tom", 5);

        //添加元素
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);

        //遍历元素
        for(Student s:hs){
            System.out.println(s);
        }
    }
}
/*
    输出结果:
        Student{name='Sam', age=25}
        Student{name='Jerry', age=3}
        Student{name='Tom', age=5}
        Student{name='Tom', age=5}
    重复元素依然输出,要使内容相同为同一个对象,需要在学生类重写hashCode()和equals()
 */

3.7 LinkedHashSet集合

LinkedHashSet集合特点

  1. 哈希表和链表实现的Set接口,具有可预测的迭代次数
  2. 由链表保证数据有序,也就是说元素存储和取出顺序是一致的
  3. 由哈希表保证元素唯一,也就是说元素没有重复的

LinkedHashSet集合练习

  • 存储字符串并遍历
package com.advanced.collection;

import java.util.LinkedHashSet;

//LinkedHashSet集合
public class LinkedHashSetDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        LinkedHashSet<String> lsh = new LinkedHashSet<>();

        //添加字符串元素
        lsh.add("hello");
        lsh.add("world");
        lsh.add("java");
        lsh.add("java");

        //遍历元素
        for(String s: lsh){
            System.out.println(s);
        }
    }
}
/*
   输出结果:
        hello
        world
        java
 */

3.8 TreeSet集合

TreeSet集合特点

  1. 元素有序,这里的顺序不是指元素存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
    • TreeSet():根据其元素的自然排序进行排序
    • TreeSet(Comparator comparator):根据指定的比较器进行排序
  2. 没有带索引的方法,所以不能使用普通for循环遍历
  3. 由于是Set集合,所以是不包含重复元素的集合

TreeSet集合练习

  • 存储整数并遍历
package com.advanced.collection;

import java.util.TreeSet;

//TreeSet集合练习:存储整数并遍历
public class TreeSetDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Integer> tr = new TreeSet<Integer>();

        //添加元素
        tr.add(2);
        tr.add(5);
        tr.add(4);
        tr.add(1);
        tr.add(3);

        //遍历元素
        for (Integer i : tr) {
            System.out.println(i);
        }
    }
}
/*
    1
    2
    3
    4
    5
 */

3.9 Comparable使用

存储学生对象并遍历,创建TreeSet集合使用无参构造方法>

要求:按照年龄从小到大排序,年龄相同则按姓名字母顺序排序

结论:

  1. 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
  2. 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
  3. 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
package com.advanced.collection;

import java.util.TreeSet;

//Comparable
public class TreeSetComparable01 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>();

        //创建学生对象
        Student s1 = new Student("zhangsan", 20);
        Student s2 = new Student("lisi", 25);
        Student s3 = new Student("wangwu", 23);
        Student s4 = new Student("zhaoliu", 25);
        Student s5 = new Student("zhaoliu", 25);

        //添加元素
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);

        //遍历元素
        for(Student s: ts){
            System.out.println(s);
        }
    }
}
class Student implements Comparable<Student> {
    //其余属性方法省略   
    @Override
    public int compareTo(Student s) {
        //return 0;//表示两个对象相同
        //return 1;//表示后一个大,按添加顺序排序
        //return -1;//表示前一个大,按添加顺序倒序排序
        //按年龄从小到大排序
        int num = this.age - s.age;
        //年龄相同,按照名字字母顺序排序
        int n = num == 0 ? this.name.compareTo(s.name) : num;

        return n;
    }
} 
/*
	Student{name='zhangsan', age=20}
	Student{name='wangwu', age=23}
	Student{name='lisi', age=25}
	Student{name='zhaoliu', age=25}
*/

3.10 Comparator比较器

存储学生对象并遍历,创建TreeSet集合使用带参构造方法>

要求:按照年龄从小到大排序,年龄相同则按姓名字母顺序排序

结论:

  1. 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
  2. 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
  3. 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
package com.advanced.collection;

import java.util.Comparator;
import java.util.TreeSet;

//Comparator比较器
public class TreeSetComparator01 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                // int num = this.age - s.age;
                //             s1        s2
                int num1 = s1.getAge() - s2.getAge();
                int num2 = num1==0?s1.getName().compareTo(s2.getName()):num1;
                return num2;
            }
        });

        //创建学生对象
        Student s1 = new Student("zhangsan", 20);
        Student s2 = new Student("lisi", 25);
        Student s3 = new Student("wangwu", 23);
        Student s4 = new Student("zhaoliu", 25);
        Student s5 = new Student("zhaoliu", 25);

        //添加元素
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);

        //遍历元素
        for(Student s: ts){
            System.out.println(s);
        }
    }
}

3.11 案例:成绩排序

需求:用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合

​ 需求:按照总分从高到低出现

package com.advanced.collection;

import java.util.Comparator;
import java.util.TreeSet;

//案例:成绩排序(Comparator比较器)
public class TreeSetDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int score1 = s1.getChineseScore() + s1.getMathScore();
                int score2 = s2.getChineseScore() + s2.getMathScore();
                //主要条件
                int num1 = score2 - score1;
                //次要条件
                int num2 = num1 == 0 ? s1.getChineseScore() - s2.getChineseScore() : num1;
                int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()) : num2;
                return num3;
            }
        });

        //创建学生对象
        Student s1 = new Student("张雅", 90, 86);
        Student s2 = new Student("姬玲珑", 98, 88);
        Student s3 = new Student("白灵", 89, 89);
        Student s4 = new Student("紫月", 91, 85);
        Student s5 = new Student("黎若雨", 85, 90);
        Student s6 = new Student("张雅", 90, 86);

        //添加元素
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍历元素
        for (Student s : ts) {
            int socre = s.getChineseScore() + s.getMathScore();
            System.out.println(s.getName() + ", 语文成绩:" + s.getChineseScore()
                    + ", 数学成绩:" + s.getMathScore() + ", 总分:" + socre);
        }
    }
}
/*
    姬玲珑, 语文成绩:98, 数学成绩:88, 总分:186
    白灵, 语文成绩:89, 数学成绩:89, 总分:178
    张雅, 语文成绩:90, 数学成绩:86, 总分:176
    紫月, 语文成绩:91, 数学成绩:85, 总分:176
    黎若雨, 语文成绩:85, 数学成绩:90, 总分:175
 */

3.12 案例:不重复的随机数

需求:编写一个程序,获取10个1-20之间的随机数,要求随机数不能重复,并在控制台输出

package com.advanced.collection;

import java.util.HashSet;
import java.util.Random;

//案例:不重复的随机数
//需求:编写一个程序,获取10个1-20之间的随机数,要求随机数不能重复,并在控制台输出
public class HashSetDemo03 {
    public static void main(String[] args) {
        //创建集合对象
        HashSet<Integer> hs = new HashSet<Integer>();

        //创建随机数对象
        Random random = new Random();

        //循环添加随机数元素
        while (hs.size()<10){
            int i = random.nextInt(20)+1;
            hs.add(i);
        }

        for(Integer i : hs){
            System.out.println(i);
        }
    }
}

同理,可以使用TreeSet集合。HashSet集合元素无序,TreeSet集合元素自然排序

4.泛型

4.1 泛型概述

泛型:是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法类型

它的本质是参数化类型,也就是说所操作的类型被定义为一个参数

一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参,那么参数化类型怎么理解呢?

顾名思义,就是将类型由原来具体的类型参数化,然后在使用/调用时传入具体的类型

这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法和泛型接口

泛型定义格式:

  • <类型>:指定一种类型的格式。这里的类型可以看成是形参
  • <类型1,类型2…>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
  • 将来具体调用时给定的类型可以看成实参,并且实参的类型只能是引用数据类型

泛型的好处:

  • 把运行时期的问题提前到编译期间
  • 避免了强制类型转换
package com.advanced.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

//泛型
public class CollectionDemo04 {
    public static void main(String[] args) {
        //创建集合对象
        //Collection c = new ArrayList();
        Collection<String> c = new ArrayList<String>();

        //添加对象
        c.add("hello");
        c.add("world");
        c.add("java");
        //c.add(20);//定义泛型后只能传递指定类型

        for (Object o:c){
            //String s = (String) o;//
            System.out.println(o);
        }
    }
}

4.2 泛型类

泛型类定义格式:

​ 格式:修饰符 class 类名<类型>{}

​ 范例:public class Generic< T>{}

​ 此处T可以随便写为任意标识。常见的如T、E、K、V等形式的参数常用于表示泛型

package com.advanced.collection;

import com.oop.demo05.Teacher;

//泛型类
public class GenericDemo02 {
    public static void main(String[] args) {
        //创建学生类对象
        Student s = new Student();
        s.setName("张伟");
        System.out.println(s.getName());//张伟
	    
        //创建教师类对象
        Teacher t = new Teacher();
        t.setAge(30);
        System.out.println(t.getAge());//30

        System.out.println("==========");
        
        //创建泛型类对象
        Generic<String> sg = new Generic<String>();
        sg.setT("张伟");
        System.out.println(sg.getT());//张伟

        Generic<Integer> ig = new Generic<>();
        ig.setT(30);
        System.out.println(ig.getT());//30

        Generic<Boolean> bg = new Generic<>();
        bg.setT(true);
        System.out.println(bg.getT());//true
    }
}

class Generic<T>{
    T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

4.3 泛型方法

泛型方法定义格式:

​ 格式:修饰符 <类型> 返回值类型 方法名(类型 变量名){}

package com.advanced.collection;

//泛型方法
public class GenericDemo03 {
    public static void main(String[] args) {
        //创建对象
        GenericDemo03 gd = new GenericDemo03();

        //调用泛型方法
        gd.show("hello");//hello
        gd.show(20);//20
        gd.show(true);//true
    }
    
    //泛型方法
    public <T> void show(T t){
        System.out.println(t);
    }
}

4.4 泛型接口

泛型接口定义格式:

​ 格式:修饰符 interface 接口名<类型>{}

package com.advanced.collection;

//泛型接口测试类
public class GenericDemo04 {
    public static void main(String[] args) {
        //创建对象
        Generic1<String>  g1= new GenericImpl<String>();
        g1.show("林青霞");

        Generic1<Integer>  g2= new GenericImpl<Integer>();
        g2.show(30);
    }
}

//泛型接口
interface Generic1<T>{
    public void show(T t);
}

//泛型接口实现类
class GenericImpl<T> implements Generic1<T>{
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

4.5 类型通配符

为了表示各种泛型List的父类,可以使用类型通配符

  1. 类型通配符:<?>
  2. List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型
  3. 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中

如果说我们不希望List<?>是任何泛型List的父类,只希望它代表某一类泛型List的父类,可以通过类型通配符的上限

  1. 类型通配符上限:<? extends 类型>
  2. List<? extends Number>:他表示的类型是Number或其子类型

除了可以指定类型通配符的上限,我们也可以指定类型通配符的下限

  1. 类型通配符下限:<? super 类型>
  2. List<? super Number>:它表示的类型是Number或其父类型
package com.advanced.collection;

import java.util.ArrayList;
import java.util.List;

//类型通配符
public class GenericDemo05 {
    public static void main(String[] args) {
        //List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型
        List<?> list1 = new ArrayList<Object>();
        List<?> list2 = new ArrayList<Number>();
        List<?> list3 = new ArrayList<Integer>();

        //List<? extends Number>:他表示的类型是Number或其子类型
//        List<? extends Number> list4 = new ArrayList<Object>();// Object -> Number -> Integer
        List<? extends Number> list5 = new ArrayList<Number>();
        List<? extends Number> list6 = new ArrayList<Integer>();

        //List<? super Number>:它表示的类型是Number或其父类型
        List<? super Number> list7 = new ArrayList<Object>();
        List<? super Number> list8 = new ArrayList<Number>();
//        List<? super Number> list9 = new ArrayList<Integer>();// Object -> Number -> Integer
    }
}

4.6 可变参数

可变参数又称为参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了

  1. 格式:修饰符 返回值类型 方法名(数据类型… 变量名){}
  2. 范例:public static int sum(int… a){}

可变参数注意事项:

  1. 这里的变量其实是一个数组
  2. 如果一个方法有多个变量,包含可变参数,可变参数要放在最后面
package com.advanced.collection;

//可变参数
public class ArgsDemo01 {
    public static void main(String[] args) {
        System.out.println(sum(10, 20));
        System.out.println(sum(10, 20, 30));

        System.out.println(sum(10, 20, 30, 40));//如果多个参数使用方法重载很麻烦,推荐使用可变参数
        System.out.println(sum(10, 20, 30, 40, 50));
        System.out.println(sum(10, 20, 30, 40, 50, 60));

    }
    
//    public static int sum(int b,int... a){return 0;}//可变参数放最后

    public static int sum(int...a){
//        System.out.println(a);//[I@1b6d3586 从输出结果可以看出a是一个数组
        int sum = 0;
        for (int i:a){
            sum += i;
        }
        return sum;
    }

    public static int sum(int a,int b){
        return a+b;
    }

    public static int sum(int a,int b,int c){
        return a+b+c;
    }
}

4.7 可变参数的使用

Arrays工具类中有一个静态方法::

​ public static List asList(T…a):返回由指定数组支持的固定大小的列表

​ 返回的集合不能做增删操作,可以做修改操作

List接口中有一个静态方法:

​ public static List of(E… elements):返回包含任意数量元素的不可变列表

​ 返回的集合不能做增删改操作

Set接口中有一个静态方法:

​ public static Set of(E… elements):返回一个包含任意数量元素的不可变集合

​ 在给元素的时候,不能给重复的元素

​ 返回的集合不能做增删操作,没有修改方法

package com.advanced.collection;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

//可变参数的使用
public class ArgsDemo02 {
    public static void main(String[] args) {

        //public static <T> List<T> asList(T...a):返回由指定数组支持的固定大小的列表
/*
        List<String> list1 = Arrays.asList("hello", "world", "java");
        System.out.println(list1);//[hello, world, java]
//        list1.add("javase");//UnsupportedOperationException
//        list1.remove("world");//UnsupportedOperationException
        list1.set(1,"javase");
        System.out.println(list1);//[hello, javase, java]
*/

        //public static <E> List<E> of(E... elements):返回包含任意数量元素的不可变列表
/*
//        List<String> list2 = List.of("hello","world","java");
//        list2.add("javase");//UnsupportedOperationException
//        list2.remove("world");//UnsupportedOperationException
//        list2.set(1,"javase");
*/

        //public static <E> Set<E> of(E... elements):返回一个包含任意数量元素的不可变集合
/*
//        Set<String> list3 = Set.of("hello","world","java");
//        Set<String> list3 = Set.of("hello","world","java","world");//IllegalArgumentException
//        list3.add("javase");//UnsupportedOperationException
//        list3.remove("world");//UnsupportedOperationException       
*/
    }
}

5.Map

5.1 Map集合概述

Map集合概述

interface Map<K,V> K:键的类型 V:值的类型

将键映射到值的类型;不能包含重复的键,每个键可以映射到最多一个值

举例:学生的学号和姓名 (学号是键,姓名是值)

​ 001 林青霞

​ 002 张曼玉

​ 003 王祖贤

创建Map集合对象

  1. 多态的方法
  2. 具体的实现类HashMap
package com.advanced.collection;

import java.util.HashMap;
import java.util.Map;

//Map集合
public class MapDemo01 {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<String ,String> map1 = new HashMap<String, String>();

        //添加元素
        map1.put("001","林青霞");
        map1.put("002","张曼玉");
        map1.put("003","王祖贤");

        //输出集合对象
        System.out.println(map1);//{001=林青霞, 002=张曼玉, 003=王祖贤}

        map1.put("003","柳岩");//该键存在元素,则会覆盖以前元素
        System.out.println(map1);//{001=林青霞, 002=张曼玉, 003=柳岩}
    }
}

5.2 Map集合基本功能

方法名说明
V put(K key,V value)添加元素
V remove(Object key)根据键删除键值对元素
void clear()删除所有键值对元素
boolean containsKey(Object key)判断集合是否包含指定的键
boolean containsValue(Object value)判断集合是否包含指定的值
boolean isEmpty()判断集合是否为空
int size()集合的长度,集合中键值对的个数
package com.advanced.collection;

import java.util.HashMap;
import java.util.Map;

//Map集合基本功能
public class MapDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        Map<String, String> hm = new HashMap<String, String>();

        //V put(K key,V value) 添加元素
        hm.put("001","林青霞");
        hm.put("002","张曼玉");

        //输出元素
        System.out.println(hm);//{001=林青霞, 002=张曼玉}

        //int size() 集合的长度,集合中键值对的个数
        int size = hm.size();
        System.out.println(size);//2

        //V remove(Object key) 根据键删除键值对元素
        hm.remove("001");
        System.out.println(hm);//{002=张曼玉}

        //boolean containsKey(Object key) 判断集合是否包含指定的键
        System.out.println(hm.containsKey("001"));//false
        System.out.println(hm.containsKey("002"));//true

        //boolean containsValue(Object value) 判断集合是否包含指定的值
        System.out.println(hm.containsValue("张曼玉"));//true
        System.out.println(hm.containsValue("林青霞"));//false

        //boolean isEmpty() 判断集合是否为空
        System.out.println(hm.isEmpty());//false

        //void clear()删除所有键值对元素
        hm.clear();
        System.out.println(hm);//{}
    }
}

5.3 Map集合获取功能

方法名说明
V get(Object key)根据键获取值
Set keySet()获取所有键的集合
Collection values()获取所有值的集合
Set<Map.Entry<K,V>> entrySet()所有键值对对象的集合
package com.advanced.collection;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

//Map集合获取功能
public class MapDemo03 {
    public static void main(String[] args) {
        //创建集合对象
        Map<String,String> map = new HashMap<String, String>();

        //添加元素
        map.put("张无忌","赵敏");
        map.put("郭靖","黄蓉");
        map.put("杨过","小龙女");

        //V get(Object key) 根据键获取值
        System.out.println(map.get("张无忌"));//赵敏
        System.out.println(map.get("张三丰"));//null

        //Set<K> keySet() 获取所有键的集合
        Set<String> keySet = map.keySet();
        System.out.println(keySet);//[杨过, 郭靖, 张无忌]

        //Collection<V> values() 获取所有值的集合
        Collection<String> values = map.values();
        System.out.println(values);//[小龙女, 黄蓉, 赵敏]
    }
}

5.4 Map集合遍历(一)

我们刚才存储元素都是成对出现的,所以我们可以把Map看成是一个夫妻对集合

遍历思路:

  1. 把所有丈夫集中起来

  2. 遍历丈夫集合,获取每一个丈夫

  3. 根据丈夫去找对应的妻子

转换为Map集合中的操作:

  1. 获取所有键的集合,用keySet()方法实现
  2. 遍历键的集合,获取到每一个键,用增强for实现
  3. 根据键去找值,用get(Oject key)方法实现
package com.advanced.collection;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

//Map集合遍历(方式1)
public class MapDemo04 {
    public static void main(String[] args) {
        //创建集合对象
        Map<String, String> map = new HashMap<String, String>();

        //添加元素
        map.put("张无忌", "赵敏");
        map.put("郭靖", "黄蓉");
        map.put("杨过", "小龙女");

        //遍历元素(方式1)
        Set<String> keySet = map.keySet();//获取所有键的集合
        for (String s : keySet) {//遍历键的集合
            String s1 = map.get(s);//通过键获取值
            System.out.println(s + "," + s1);
        }
    }
}

5.5 Map集合遍历(二)

我们刚才存储的元素都是成对出现的,所以我们可以把Map看成是一个夫妻对的集合

遍历思路:

  1. 获取所有结婚证的集合

  2. 遍历结婚证的集合,得到每一个结婚证

  3. 根据结婚证获取丈夫和妻子

转换Map集合中的操作:

  1. 获取所有键值对对象

    Set<Map.Entry<K,V>> entrySet():获取所有的键值对对象的集合

  2. 遍历键值对对象的集合,得到每一个键值对

    用增强for实现,得到每一个Map.Entry

  3. 根据键值对对象获取键和值

    用getKey()得到键

    用getValue()得到值

package com.advanced.collection;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

Map集合遍历(方式2)
public class MapDemo05 {
    public static void main(String[] args) {
        //创建集合对象
        Map<String, String> map = new HashMap<String, String>();

        //添加元素
        map.put("张无忌", "赵敏");
        map.put("郭靖", "黄蓉");
        map.put("杨过", "小龙女");

        //遍历元素(方式2)
        Set<Map.Entry<String, String>> entrySet = map.entrySet();//获取键值对对象
        for (Map.Entry<String,String> entry : entrySet) {//遍历键值对对象的集合
            String key = entry.getKey();//通过对象获取键
            String value = entry.getValue();//通过对象获取值
            System.out.println(key + "," + value);
        }
    }
}

5.6 案例

HashMap集合存储学生对象并遍历

需求:创建一个HashMap集合,键是学号(String),值是学生对象(Student)。存储三个键值对元素,并遍历

package com.advanced.collection;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

//HashMap集合存储学生对象并遍历
//需求:创建一个HashMap集合,键是学号(String),值是学生对象(Student)。存储三个键值对元素,并遍历
public class HashMapDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        HashMap<String, Student> hm = new HashMap<String, Student>();

        //创建学生对象
        Student s1 = new Student("林青霞", 30);
        Student s2 = new Student("张曼玉", 35);
        Student s3 = new Student("王祖贤", 33);

        //添加元素
        hm.put("001", s1);
        hm.put("002", s2);
        hm.put("003", s3);

        //遍历元素(方式1)
/*
        Set<String> keySet = hm.keySet();
        for (String s : keySet) {
            Student student = hm.get(s);
            System.out.println(s + "," + student.getName() + "," + student.getAge());
        }
*/
        //遍历元素(方式2)
        Set<Map.Entry<String, Student>> entrySet = hm.entrySet();
        for (Map.Entry<String, Student> entry : entrySet) {
            String key = entry.getKey();
            Student value = entry.getValue();
            String name = value.getName();
            int age = value.getAge();
            System.out.println(key + "," + name + "," + age);
        }
    }
}

5.7 案例

HashMap集合存储学生对象并遍历

需求:创建一个HashMap集合,键是学生对象(Student),值是居住地(String)。存储多个键值对元素,并遍历

​ 要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象

package com.advanced.collection;

import java.util.HashMap;
import java.util.Set;

//HashMap集合存储学生对象并遍历
    //需求:创建一个HashMap集合,键是学生对象(Student),值是居住地(String)。存储多个键值对元素,并遍历
        //要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象
public class HashMapDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        HashMap<Student, String> hm = new HashMap<Student,String>();

        //创建学生对象
        Student s1 = new Student("林青霞", 30);
        Student s2 = new Student("张曼玉", 35);
        Student s3 = new Student("王祖贤", 33);
        Student s4 = new Student("王祖贤", 32);

        //添加元素
        hm.put(s1,"北京");
        hm.put(s2,"上海");
        hm.put(s3,"香港");
        hm.put(s3,"成都");
        hm.put(s4,"深圳");

        //遍历元素
        Set<Student> keySet = hm.keySet();
        for (Student s : keySet){
            System.out.println(s.getName()+","+s.getAge()+","+hm.get(s));
        }
    }
}

输出结果:

王祖贤,33,成都
张曼玉,35,上海
王祖贤,32,深圳
林青霞,30,北京

5.8 案例

ArrayList集合存储HashMap元素并遍历

需求:创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap,每一个HashMap的键和值都是String,并遍历

package com.advanced.collection;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

//ArrayList集合存储HashMap元素并遍历
//需求:创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap,每一个HashMap的键和值都是String,并遍历
public class HashMapDemo03 {
    public static void main(String[] args) {
        //创建ArrayList集合对象
        ArrayList<HashMap<String, String>> arrayList = new ArrayList<HashMap<String, String>>();

        //创建HashMap集合对象
        HashMap<String, String> hashMap1 = new HashMap<>();
        HashMap<String, String> hashMap2 = new HashMap<>();
        HashMap<String, String> hashMap3 = new HashMap<>();

        //添加HashMap集合元素
        hashMap1.put("孙策", "大乔");
        hashMap1.put("周瑜", "小乔");
        hashMap2.put("郭靖", "黄蓉");
        hashMap2.put("杨过", "小龙女");
        hashMap3.put("令狐冲", "任盈盈");
        hashMap3.put("林平之", "岳灵珊");

        //添加元素
        arrayList.add(hashMap1);
        arrayList.add(hashMap2);
        arrayList.add(hashMap3);

        //遍历元素(嵌套循环遍历)
        for (HashMap<String, String> hashMap : arrayList) {//外层遍历ArrayList集合
            Set<String> keySet = hashMap.keySet();
            for (String key : keySet) {	//内层遍历HashMap集合
                String value = hashMap.get(key);
                System.out.println(key + "," + value);
            }
        }
    }
}

输出结果:

孙策,大乔
周瑜,小乔
杨过,小龙女
郭靖,黄蓉
令狐冲,任盈盈
林平之,岳灵珊

5.9 案例

HashMap集合存储ArrayList元素并遍历

需求:创建一个HashMap集合,存储三个键值对元素,每个键值对元素键是String,值是ArrayList,每个ArrayList元素是String,并遍历

package com.advanced.collection;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

//HashMap集合存储ArrayList元素并遍历
//需求:创建一个HashMap集合,存储三个键值对元素,每个键值对元素键是String,值是ArrayList,每个ArrayList元素是String,并遍历
public class HashMapDemo04 {
    public static void main(String[] args) {
        //创建HashMap集合对象
        HashMap<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();

        //创建ArrayList集合
        ArrayList<String> arrL1 = new ArrayList<>();
        ArrayList<String> arrL2 = new ArrayList<>();
        ArrayList<String> arrL3 = new ArrayList<>();

        //添加ArrayList集合元素
        arrL1.add("诸葛亮");
        arrL1.add("周瑜");
        arrL2.add("孙悟空");
        arrL2.add("唐僧");
        arrL3.add("林冲");
        arrL3.add("武松");

        //添加HashMap集合元素
        hm.put("三国演义",arrL1);
        hm.put("西游记",arrL2);
        hm.put("水浒传",arrL3);

        //遍历元素
        Set<String> keySet = hm.keySet();
        for (String key : keySet){
            ArrayList<String> strings = hm.get(key);
            for (String value : strings){
                System.out.println(key+","+value);
            }
        }
    }
}

输出结果:

水浒传,林冲
水浒传,武松
三国演义,诸葛亮
三国演义,周瑜
西游记,孙悟空
西游记,唐僧

5.10 案例

统计字符串中每个字符出现的次数

需求:键盘录入一个字符串,要求统计字符串中每个字符出现的次数

举例:键盘录入”aababcabcdabcde“ 在控制台输出:a(5)b(4)c(3)d(2)e(1)

package com.advanced.collection;

import java.util.Collection;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeMap;

//统计字符串中每个字符出现的次数
//需求:键盘录入一个字符串,要求统计字符串中每个字符出现的次数
//举例:键盘录入”aababcabcdabcde“		在控制台输出:a(5)b(4)c(3)d(2)e(1)
public class TreeMapDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        TreeMap<Character, Integer> treeMap = new TreeMap<Character, Integer>();

        //创建扫描对象
        Scanner scanner = new Scanner(System.in);

        //提示输入字符串
        System.out.println("请输入字符串:");

        //获取录入字符串
        String line = scanner.nextLine();

        //遍历字符串,得到每个字符
        for (int i = 0; i < line.length(); i++) {
            char key = line.charAt(i);//将得到的字符作为键

            Integer value = treeMap.get(key);//通过键获取值

            if (value == null){//如果值为null,说明集合中没有该字符,就把该字符作为键,值为1
                treeMap.put(key,1);
            }else{//如果值不为null,说明集合中有该字符,就把该字符作为键,值+1
                value++;
                treeMap.put(key,value);
            }
        }

        //遍历集合,得到键和值,按要求拼接
        StringBuilder sb = new StringBuilder();

        Set<Character> keySet = treeMap.keySet();
        for (Character key:keySet){
            Integer value = treeMap.get(key);
            sb.append(key).append("(").append(value).append(")");
        }
        String result = sb.toString();//将StringBuilder类型转换为String类型

        //输出结果
        System.out.println(result);

        scanner.close();
    }
}

6.Collections

6.1 概述

Collections类的概述

是针对集合操作的工具类

Collections类的常用方法

public static <T extends Comparable<? super T>> void sort(List list):将指定的列表按升序排序

public static void reverse(List list):反转指定列表中元素的顺序

public static void shuffle(List list):使用默认的随机源随机排列指定的列表

package com.advanced.collection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

//Collections类常用方法
public class CollectionsDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        List<Integer> list = new ArrayList<Integer>();

        //添加元素
        list.add(20);
        list.add(40);
        list.add(10);
        list.add(50);
        list.add(30);

        //输出元素
        System.out.println(list);//[20, 40, 10, 50, 30]

        //public static <T extends Comparable<? super T>> void sort(List<T> list):将指定的列表按升序排序
        Collections.sort(list);
        System.out.println(list);//[10, 20, 30, 40, 50]

        //public static void reverse(List<T> list):反转指定列表中元素的顺序
        Collections.reverse(list);
        System.out.println(list);//[50, 40, 30, 20, 10]

        //public static void shuffle(List<T> list):使用默认的随机源随机排列指定的列表
        Collections.shuffle(list);
        System.out.println(list);//[40, 20, 30, 10, 50] 随机排序,每次运行结果会不同
    }
}

6.2 案例

ArrayList存储学生对象并遍历

需求:ArrayList存储学生对象,使用Collections对ArrayList进行排序

​ 需求:按年龄从小到大进行排序,年龄相同时,按姓名字母排序

package com.advanced.collection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

//ArrayList存储学生对象并遍历
//需求:ArrayList存储学生对象,使用Collections对ArrayList进行排序
//​	需求:按年龄从小到大进行排序,年龄相同时,按姓名字母排序
public class CollectionsDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        ArrayList<Student> arrayList = new ArrayList<Student>();

        //创建学生对象
        Student s1 = new Student("linqingxia", 30);
        Student s2 = new Student("zhangmanyu", 33);
        Student s3 = new Student("wangzuxian", 32);
        Student s4 = new Student("liuyan", 32);

        //添加学生对象到集合
        arrayList.add(s1);
        arrayList.add(s2);
        arrayList.add(s3);
        arrayList.add(s4);

        //按年龄排序,年龄相同则按姓名字母排序
        Collections.sort(arrayList, new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int num1 = s1.getAge() - s2.getAge();
                int num2 = num1 == 0 ? s1.getName().compareTo(s2.getName()) : num1;

                return num2;
            }
        });

        //遍历集合
        for (Student s : arrayList) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

输出结果:

linqingxia,30
liuyan,32
wangzuxian,32
zhangmanyu,33

6.3 案例:模拟斗地主

需求:通过程序实现斗地主过程的洗牌,发牌和看牌

package com.advanced.collection;

import java.util.ArrayList;
import java.util.Collections;

//案例:模拟斗地主
//需求:通过程序实现斗地主过程的洗牌,发牌和看牌
public class PokerDemo01 {
    public static void main(String[] args) {
        //创建一个牌盒,也就是定义一个集合对象,用ArrayList集合实现
        ArrayList<String> array = new ArrayList<String>();

        //往牌盒里面装牌
        /*
            ♦2,♦3,...,♦K,♦A
            ♣2,...
            ♥2,...
            ♠2,...
            小王,大王
         */
        //定义花色数组
        String[] colors = {"♦", "♣", "♥", "♠"};
        //定义点数数组
        String[] numbers = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};
        for (String color : colors) {
            for (String number : numbers) {
                array.add(color + number);
            }
        }
        array.add("小王");
        array.add("大王");

        //洗牌,也就是把牌打散,用Collections的shuffle()方法实现
        Collections.shuffle(array);

        //发牌,也就是遍历集合,给三个玩家发牌
        ArrayList<String> p1Array = new ArrayList<String>();
        ArrayList<String> p2Array = new ArrayList<String>();
        ArrayList<String> p3Array = new ArrayList<String>();
        ArrayList<String> dpArray = new ArrayList<String>();

        for (int i = 0; i < array.size(); i++) {
            String poker = array.get(i);

            if (i >= array.size() - 3) {
                dpArray.add(poker);
            } else if (i % 3 == 0) {
                p1Array.add(poker);
            } else if (i % 3 == 1) {
                p2Array.add(poker);
            } else if (i % 3 == 2) {
                p3Array.add(poker);
            }
        }

        //看牌,也就是三个玩家分别遍历自己的牌
        lookPoker("玩家1",p1Array);
        lookPoker("玩家2",p2Array);
        lookPoker("玩家3",p3Array);
        lookPoker("底牌",dpArray);
    }

    //看牌的方法
    public static void lookPoker(String name, ArrayList<String> array){
        System.out.print(name+"的牌:");
        for (String poker: array){
            System.out.print(poker+" ");
        }
        System.out.println();
    }
}

输出结果:

玩家1的牌:♥9 ♠Q ♦K ♣6 ♦5 ♣Q ♠10 ♣10 ♦8 ♠5 ♠6 ♣9 ♠2 ♦7 ♦2 ♣8 ♦10
玩家2的牌:♥J ♣4 ♦9 ♠9 ♠8 ♠4 大王 ♥8 ♥2 ♦Q ♥A ♣5 ♥Q ♦6 ♥3 ♣2 ♠A
玩家3的牌:♠J ♦A ♥K ♣J ♥4 ♣A ♦3 ♣K ♥10 ♥6 ♠K ♠3 ♣3 ♣7 ♠7 ♦4 小王
底牌的牌:♥7 ♦J ♥5

6.4 案例:模拟斗地主升级版

需求:通过程序实现斗地主过程的洗牌,发牌和看牌。要求:对牌进行排序

package com.advanced.collection;

import java.util.*;

//需求:通过程序实现斗地主过程的洗牌,发牌和看牌。要求:对牌进行排序
public class PokerDemo02 {
    public static void main(String[] args) {
        //创建HashMap集合存储,键是索引(Integer),值是牌(String)
        HashMap<Integer, String> hm = new HashMap<Integer, String>();

        //创建ArrayList集合存储索引
        ArrayList<Integer> array = new ArrayList<Integer>();

        //创建花色数组
        String[] colors = {"♦", "♣", "♥", "♠"};
        //创建点数数组(按斗地主规则点数从小到大排序)
        String[] numbers = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
        //从0开始往HashMap中存储编号,并存储对应的值,同时往ArrayList里面存储编号
        Integer index = 0;
        for (String number : numbers) {
            for (String color : colors) {
                hm.put(index, color + number);
                array.add(index);
                index++;
            }
        }
        hm.put(index, "小王");
        array.add(index);
        index++;
        hm.put(index, "大王");
        array.add(index);

        //洗牌(洗的是编号),将索引顺序打散
        Collections.shuffle(array);

        //发牌(发的是编号,为了保证编号是有序的,创建TreeSet集合接收)
        TreeSet<Integer> player1 = new TreeSet<Integer>();
        TreeSet<Integer> player2 = new TreeSet<Integer>();
        TreeSet<Integer> player3 = new TreeSet<Integer>();
        TreeSet<Integer> dplayer = new TreeSet<Integer>();

        for (int i = 0; i < array.size(); i++) {
            Integer key = array.get(i);

            if (i >= array.size() - 3) {
                dplayer.add(key);
            } else if (i % 3 == 0) {
                player1.add(key);
            } else if (i % 3 == 1) {
                player2.add(key);
            } else if (i % 3 == 2) {
                player3.add(key);
            }
        }

        //调用看牌方法
        lookPoker("玩家1", player1, hm);
        lookPoker("玩家2", player2, hm);
        lookPoker("玩家3", player3, hm);
        lookPoker("底牌", dplayer, hm);
    }

    //定义看牌方法(遍历TreeSet集合,获取编号,通过编号到HashMap中找对应值)
    public static void lookPoker(String name, TreeSet<Integer> ts, HashMap<Integer, String> hm) {
        System.out.print(name + "的牌:");
        for (Integer key : ts) {
            String value = hm.get(key);
            System.out.print(value + " ");
        }
        System.out.println();
    }
}

输出结果:

玩家1的牌:♠3 ♣4 ♥4 ♠4 ♦5 ♦6 ♦8 ♣8 ♣9 ♣10 ♠10 ♦J ♣Q ♠Q ♥K ♦A ♣A
玩家2的牌:♦3 ♥3 ♦4 ♣5 ♥5 ♣7 ♠8 ♥9 ♠9 ♥10 ♣J ♥Q ♣K ♠A ♦2 ♣2 小王
玩家3的牌:♣3 ♠5 ♣6 ♠6 ♥7 ♠7 ♥8 ♦10 ♥J ♠J ♦Q ♦K ♠K ♥A ♥2 ♠2 大王
底牌的牌:♥6 ♦7 ♦9

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值