集合

集合

第一章 Collection集合(单列集合)

1.1集合概述


  • 集合:集合是Java中提供的一种容器,可以用来存储多个数据。

集合和数组的区别:

1.数组的长度是固定的。

集合的长度是可变的。

2.数组中存储的是同一种元素,可以存储基本类型数据值。

集合存储的都是对象,而且对象的类型可以不一致,在开发中一般当对象多的时候,使用集合进行存储。


1.2集合框架


集合按照其存储结构可以分为两大类:分别是==单列集合==(java.util.Collection)和==双列集合==(java.util.Map)

  • Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是java.util.Listjava.util.Set,其中List的特点是元素有序、元素可重复。Set的特点是元素无序,而且不可重复。List接口的主要实现类有java.util.ArrayListjava.util.LinkedList,Set接口的主要实现类有java.util.HashSetjava.util.TreeSet

  • 橙色框里填写的都是接口类型,而蓝色框里填写的都是具体的实现类
  • 集合本身是一个工具,它存放在java.util包中。在Collection接口定义着单列集合框架中最最共性的内容

1.3Collection常用功能


Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。

  • public boolean add(E e):把给定的对象添加到当前集合中

  • public void clear():清空集合中所有的元素

  • public boolean remove(E e):把给定的对象在当前集合中删除

  • public boolean contains(E e):判断当前集合中是否包含给定的对象

  • public boolean isEmpty():判断当前集合是否为空

  • public int size():返回集合中元素的个数

  • public object[ ] toArray():把集合中的元素,存储到数组中

    package Rosyday01;
    
    import java.util.ArrayList;
    import java.util.Collection;
    
    public class Rosy10Collection {
        public static void main(String[] args) {
            Collection<String> coll=new ArrayList<>();
            System.out.println(coll);//重写了toString方法 []
            /*
            1.public boolean add(E e):把给定的对象添加到当前集合中
             */
            boolean b1=coll.add("张三");
            coll.add("李四");
            coll.add("王五");
            coll.add("赵六");
            coll.add("田七");
            coll.add("孙八");
            System.out.println("b1:"+b1);//b1:true
            System.out.println(coll);//[张三, 李四, 王五, 赵六, 田七, 孙八]
            /*
            2.public boolean remove(E e):把给定的对象在当前集合中删除
             */
            boolean b2=coll.remove("张三");
            System.out.println("b2:"+b2);//b2:true
            System.out.println(coll);//[李四, 王五, 赵六, 田七, 孙八]
            /*
            3.public boolean contains(E e):判断当前集合中是否包含给定的对象
             */
            boolean b3=coll.contains("赵六");
            System.out.println("b3:"+b3);//b3:true
            /*
            4.public int size():返回集合中元素的个数
             */
            int b4=coll.size();
            System.out.println("集合中元素的个数是:"+b4);//集合中元素的个数是:5
            /*
            5.public boolean isEmpty():判断当前集合是否为空
             */
            boolean b5=coll.isEmpty();
            System.out.println("b5:"+b5);//b5:false
            /*
            6.public object[ ] toArray():把集合中的元素,存储到数组中
             */
            Object[] arr = coll.toArray();
            for (int i = 0; i < arr.length; i++) {
                System.out.println(arr[i]);
            }
            /*
            7.public void clear():清空集合中所有的元素,但集合还在
             */
            coll.clear();
            System.out.println(coll);//[]
        }
    }

第二章 Iterator迭代器


2.1Iterator接口


​ 在程序开发中,要遍历集合中的所有元素,jdk专门提供了一个接口java.util,IteratorColectionMap接口主要用于存储数据,而Iterator接口主要用于迭代访问(即遍历)Colection中的元素,因此Iterator对象也被称作迭代器。

1.获取迭代器的方法:

  • **public Iterator iterator( )**:获取集合对应的迭代器,用于遍历集合中的元素

    2.迭代的概念:

  • 迭代:即Collection集合元素的通用获取方法。在取元素之前要判断集合中有没有元素,如果有就把这个元素取出来,继续再判断,如果还有就再取出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

​ 3.Iterator接口的常用方法如下:

  • **public E next( )**:返回迭代的下一个元素
  • **public boolean hasNext( )**:如果仍有元素可以迭代,则返回true

​ 4.迭代器的使用步骤(==重点==):

​ 1.使用集合中的方法iterator( )获取迭代器的实现类对象,使用Iterator接口接收(多态)

​ 2.使用Iterator接口中的方法hasNext判断还有没有下一个元素

​ 3.使用Iterator接口中的方法next取出集合中的下一个元素

package Demo02;

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

public class Demo01Iterator {
    public static void main(String[] args) {
        Collection<String>coll=new ArrayList<>();//使用多态方式创建对象
        //添加元素到集合
        coll.add("叽叽叽");
        coll.add("哈哈哈");
        coll.add("啦啦啦");
        coll.add("略略略");
        coll.add("啧啧啧");
        coll.add("呵呵呵");
        Iterator<String> iterator=coll.iterator();
        // 1.使用集合中的方法iterator( )获取迭代器的实现类对象,使用Iterator接口接收(多态)
        //注意:Iterator<E>接口也是有泛型的,泛型的类型跟集合泛型的类型保持一致
        while (iterator.hasNext()){//2.使用Iterator接口中的方法hasNext判断还有没有下一个元素
            System.out.println(iterator.next());//3.使用Iterator接口中的方法next取出集合中的下一个元素
        }
    }
}

2.2迭代器的实现原理


  • java.util.Iterator接口:迭代器(对集合进行遍历)
    1.boolean hasNext() 如果仍有元素可以迭代,则返回true,没有返回false
    2.E next() 返回迭代的下一个元素
    取出集合中的下一个元素
    Iterator迭代器,是一个接口,我们无法直接使用,需要使用Iterator接口的实现类对象,获取实现类的方法比较特殊
    Collection接口中有一个方法,叫iterator(),这个方法返回的就是迭代器的实现类对象
    Iterator iterator() 返回在此 collection的元素上进行迭代的迭代器

  • 当没有元素可取时,iterator.hasNext()返回值为false,再输出iterator.next()时程序会报错,显示Exception in thread “main” java.util.NoSuchElementException,==NoSuchElementException没有元素异常==

  • Iterator iterator===coll.iterator();==高亮部分是实现类对象,获取迭代器的实现类对象,并把指针(索引)指向集合的-1索引

  • iterator.next() 1.取出下一个元素

    ​ 2.会把指针向后移动一位

2.3增强for


​ 增强for循环(也称for each循环)jdk1.5以后,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历过程中,不能对集合中的元素进行增删操作。

for(集合/数组元素的数据类型  变量名:集合名/数组名){
        //写操作代码
    System.out.println(变量名);
}
  • 底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写

  • **Collectionextends Iterable**:所有单列集合都可以使用增强for

    **public interface Iterable**实现了这个接口,允许对象成为”foreach”语句的目标

例一:遍历数组

package Demo02;

public class Demo01foreach {
    public static void main(String[] args) {
        int []arr={1,4,6,8,5};
        //使用增强for遍历数组
        for (int a:arr) {//a代表数组中的每个元素
            System.out.println(a);
        }
    }
}

例二:遍历集合

package Demo02;

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

public class Demo01foreach {
    public static void main(String[] args) {
        Collection<String> coll=new ArrayList<>();
        coll.add("科比");
        coll.add("艾弗森");
        coll.add("奥尼尔");
        coll.add("姚明");
        coll.add("乔丹");
        //使用增强for遍历
        for (String a:coll) {//接收变量a代表  代表被遍历到的集合元素
            System.out.println(a);
        }
    }
}

tips:增强for循环必须有遍历的目标。目标只能是Collection集合或者数组。增强for循环仅仅作为遍历出现

第三章 泛型


3.1泛型概述


​ 集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升为Object类型,当我们在取出每一个对象,并进行相应的操作,这时必须采用类型转换。

3.2使用泛型的好处


1.创建集合对象,不使用泛型

  • 好处:集合不使用泛型,默认类型就是Object类型,可以存储任意类型的数据
  • 弊端:不安全,会引发异常

观察以下代码(==创建集合对象,不使用泛型==):

public class GenericDemo{
    public static void main(String []args){
        Collection coll=new ArrayList();
        coll.add("abc");
        coll.add("itcast");
        coll.add(5);//由于集合没有做任何限定,任何类型都可以在此存放
        Iterator it=coll.iterator();
        while(it.hasNext){
            //需要打印每个字符串的长度,使用String类特有方法length获取字符串长度,就要把迭代出的对象转成String类型即向下转型
            String str=(String)it.next();
            System.out.println(str.length());//会抛出ClassCastException类型转换异常,不能把Integer类型转换成String类型
        }
    } 
}
  • 5(Integer类型)无法转化成String(字符串类型)

    2.==创建集合对象,使用泛型==

  • 好处:1.避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型

    ​ 2.把运行期异常(代码运行之后会抛出的异常),提升到了编译期异常(写代码的时候会报错)

  • 弊端:泛型是什么类型,只能存储什么类型的数据

package Example;

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

class Text{
    public static void main(String[] args) {
        ArrayList<String> list=new ArrayList<>();
        list.add("abb");
        list.add("cdd");
        list.add("ddd");
        Iterator<String> iterator=list.iterator();
        while (iterator.hasNext()){
            String s=iterator.next();//使用泛型,不需要进行类型转换
            System.out.println(s.length());
    }
}

tips:泛型是数据类型的一部分,我们将类名与泛型合并一起看作数据类型。

3.3泛型的定义与使用


​ 泛型,用来灵活地将数据类型应用到不同的类,方法,接口当中。将数据类型作为参数进行传递。

定义和使用含有泛型的类

定义格式:

修饰符 class 类名<代表泛型的变量> {  }

例如,API中的ArrayList集合:

class ArrayList<E>{
    pubilc boolean add(E e){ }
    
    public E get(int index){ }
    ....
}

使用泛型:即什么时候确定泛型。

在创建对象的时候确定泛型

package Demo02;

public class Demo02Generic {
    public static void main(String[] args) {
        //不写泛型默认Object类型
        Name a1=new Name();
        a1.setName(1);
        System.out.println(a1.getName());

        //创建Name对象,泛型使用Integer类型
        Name<Integer> a2=new Name<>();//Integer类型
        a2.setName(331);
        System.out.println(a2.getName());

        //创建Name对象,泛型使用String类型
        Name<String> a3=new Name<>();//字符串类型
        a3.setName("小明");
        System.out.println(a3.getName());
    }

}
    class Name<E>{
    private E name;

    public E getName(){
        return name;
    }

    public void setName(E name){
        this.name=name;
    }
}

含有泛型的方法

定义格式:

修饰符 <代表泛型的变量> 返回值类型 方法名(参数){   }

例如:

public class MyGenericMethod{
    public <MVP> void show1(MVP mvp){
       System.out.println(mvp.getClass());
    }
    public <MVP> void show2(MVP mvp){
       return mvp;
    }
}

使用格式:调用方法时,确定泛型的类型

package Demo02;

public class Demo02GenericMethod {
    public static void main(String[] args) {
        GenericMethod it=new GenericMethod();
        it.method01(331);
        it.method01("aaa");
        it.method01(8.8);
        it.method01(true);
        System.out.println("----------------------------------");
        it.method02("静态方法不建议创建对象使用");
        GenericMethod.method02(331);
        GenericMethod.method02("aaa");
        GenericMethod.method02("8.8");
        GenericMethod.method02(true);
    }

}
class GenericMethod{
    //定义一个含有泛型的方法
    public <M> void method01(M m){
        System.out.println(m);
    }
    //定义一个含有泛型的静态方法
    public static <S> void method02(S s){
        System.out.println(s);
    }
}

含有泛型的接口

定义格式:

修饰符 interface接口名<代表泛型的变量> {  }

例如:

public interface MyGenericInterface<E>{
    public abstract void add(E e);
    
    public abstract E getE();
}

使用格式:

1.定义类时确定泛型的类型

2.接口使用什么泛型实现类就使用什么泛型

例如

package Demo02;
/*
    测试含有泛型的接口
 */
public class Demo06GenericInterface {
    public static void main(String[] args) {
        //创建GenericInterfaceImpl1对象
        GenericInterfaceImpl1 it1=new GenericInterfaceImpl1();
        it1.method("sss");
        //创建GenericInterfaceImpl2对象
        GenericInterfaceImpl2<Integer> it2=new GenericInterfaceImpl2<>();
        it2.method(331);
        GenericInterfaceImpl2<Double> it3=new GenericInterfaceImpl2<>();
        it3.method(8.8);
        GenericInterfaceImpl2<Boolean> it4=new GenericInterfaceImpl2<>();
        it4.method(true);
        GenericInterfaceImpl2<String> it5=new GenericInterfaceImpl2<>();
        it5.method("sss");
    }
}
interface GenericInterface<I>{
      public abstract void method(I i);
}
 /*
     含有泛型的接口,第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型
     public interface Iterator<E>{
     E next();
     }
     Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写的next方法泛型默认就是String
     public final class Scanner implements Iterator<String>{
           public String next() { }
        }
  */
class GenericInterfaceImpl1 implements GenericInterface<String>{


     @Override
     public void method(String s) {
         System.out.println(s);
     }
 }
 /*
       含有泛型的接口,第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走
       就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型
       public interface list(E){
           boolean add(E e);
           E get(int index);
     }
       public class ArrayList<E> implements List<E>{
           public boolean add(E e) { }
           public E get(int index) { }
     }
  */
class GenericInterfaceImpl2<I> implements GenericInterface<I> {

    @Override
    public void method(I i) {
        System.out.println(i);
    }
}

3.4泛型通配符


当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示,但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。

通配符基本使用

泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。?:代表任意的数据类型

此时只能接受数据,不能往该集合中存储数据。

  • 使用方式:不能创建对象使用,只能作为方法的参数使用

tips:泛型是没有继承概念的

package Demo02;

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

public class Demo07Generic {
    public static void main(String[] args){
        ArrayList<Integer> a1=new ArrayList<>();
        a1.add(3);
        a1.add(2);

        ArrayList<String> a2=new ArrayList<>();
        a2.add("a");
        a2.add("b");

        printArray(a1);
        printArray(a2);
    }
    public static void printArray(ArrayList<?> list){//不知道集合里存储的是什么类型的数据时,我们使用泛型的通配符来接受数据
        Iterator<?> iterator=list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

通配符高级使用—受限泛型

在java的泛型中可以指定一个泛型的上限和下限

泛型的上限

  • 格式:类型名称 <? extends 类> 对象名称
  • 意义:只能接收该类型及其子类

泛型的下限

  • 格式:类型名称 <? super类> 对象名称
  • 意义:只能接收该类型及其父类

比如:现已知Object类,String类,Number类,Integer类,Number是Integer的父类

package Demo02;

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

public class Demo08Generic {
    public static void main(String[] args) {
        Collection<Integer> list1=new ArrayList<>();
        Collection<String> list2=new ArrayList<>();
        Collection<Number> list3=new ArrayList<>();
        Collection<Object> list4=new ArrayList<>();

        getElement1(list1);
        getElement1(list2);//报错
        getElement1(list3);
        getElement1(list4);//报错

        getElement2(list1);//报错
        getElement2(list2);//报错
        getElement2(list3);
        getElement2(list4);
    }
    public static void getElement1 (Collection<? extends Number> cool){  }
    //泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
    public static void getElement2 (Collection<? super Number> cool){  }
    //泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
}

类的继承关系:

Integer extends Number extends Object

String extends Object

第四章 集合综合案例


4.1案例介绍


按照斗地主的规则,完成发牌洗牌的动作。

具体规则:

使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。

4.2案例分析


  • 准备牌:

    牌可以设计为一个ArrayList,每个字符串为一张牌。

    每张牌由花色数字两部分组成,我们可以使用花色集合和数字集合嵌套迭代完成每张牌的组装。

  • 牌由Collection类的shuffle方法进行随机排序。

    使用集合工具类Collections的方法

    static void shuffle(List<?> list)使用指定的随机源对指定列表进行置换。

    会随机的打乱集合中元素的顺序

  • 发牌:

    将每个人以及底牌设计为ArrayList,将最后3张牌直接存放于底牌,剩余牌通过对3取模一次发牌。

  • 看牌:直接打印每一个集合。

代码实现:

package Demo02;

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

public class Demo09Game {
    public static void main(String[] args) {
        ArrayList<String> poker=new ArrayList<>();
        String[] colors={"♠","♥","♣","♦" };
        String[] numbers={"2","A","K","Q","J","10","9","8","7","6","5","4","3"};
        poker.add("大王");
        poker.add("小王");
        for (String color:colors) {
            for (String number:numbers) {
                poker.add(color+number);
            }
        }
        Collections.shuffle(poker);
        ArrayList<String> player01=new ArrayList<>();
        ArrayList<String> player02=new ArrayList<>();
        ArrayList<String> player03=new ArrayList<>();
        ArrayList<String> dipai=new ArrayList<>();
        for (int i = 0; i <poker.size() ; i++) {
            String s = poker.get(i);
            if(i>=51){
                dipai.add(s);
            }
            else if(i%3==0){
                player01.add(s);
            }
            else if (i%3==1){
                player02.add(s);
            }
            else if (i%3==2){
                player03.add(s);
            }
        }
        System.out.println("玩家一"+player01);
        System.out.println("玩家二"+player02);
        System.out.println("玩家三"+player03);
        System.out.println("底牌"+dipai);
    }

}

第五章数据结构


5.1常见的数据结构


1.栈

  • 栈:stack,又称堆栈,它是运算受限的线性表,其限制是允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。

简单的说:采用该结构的集合,对元素的存取有如下的特点

  • ==先进后出==(即,存进去的元素,要在它后面的元素依次取出后,才能取出该元素)。
  • 栈的入口、出口的都是栈的顶端位置。

2.队列

  • 队列:queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。

简单的说,采用该结构的集合,对元素的存取有如下特点:

  • 先进先出:(即,存进去的元素,要在它前面的元素依次取出后,才能取出该元素)。例如:火车进山洞
  • 队列的入口、出口各站一侧。如下图:左侧为入口,右侧为出口。

3.数组

  • 数组:Array:是有序的元素序列,数组是在内存中开辟一段连续的空间,并在空间存放元素。就像酒店,有100个房间,从001到100每个房间都有固定编号,通过编号就可以迅速找到住在房间里的人。

    特点:1.查询快:数组的地址是连续的,我们通过数组的首地址可以找到数组,通过数组的索引可以快速查找某一元素

    ​ 2.增删慢:数组的长度是固定的,我们想要增加/删除一个元素,必须创建一个新数组,把源数组的数据复制过来

  • 查找元素快:通过索引,可以快速访问指定位置的元素

  • 增删元素慢

    指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把元素组元素根据索引,复制到新数组对应索引的位置。

4.链表

  • 链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时i动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的链表结构有单向链表与双向链表,这里记录的是单向链表

简单的说,采用该结构的集合,对元素的存取有如下特点:

  • 多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下一个人的左手,以此类推,这样多个人就在一起了。

  • 查找元素慢:想查找某个元素,需要通过连接的结点,依次向后查找指定元素,链表中地址不是连续的,每次查询元素,都必须从头开始查询

  • 增删元素快:链表结构,增加/删除一个元素,对链表的整体结构没有影响,所以增删快

5.红黑树

  • 二叉树:binary tree,是每个结点不超过二的有序**树(tree)**。

简单的理解,就是一种类似于我们生活中的树的结构,只不过每个结点上都最多只能有两个子结点。

二叉树是每个结点最多有两个子树的树结构。顶上:根节点,两侧:“左子树”,“右子树”。

  • 排序树/查找树

    在二叉树的基础上,元素是有大小顺序的,左子树小,右子树大。

  • 平衡树

  • 不平衡树

  • 红黑树

    特点:趋近于平衡树,查询的速度非常的快,查询叶子节点最大次数和最小次数不能超过二倍(找到一个元素的次数不能是其他的二倍)

    性质1:每个节点要么是黑色,要么是红色。

    性质2:根节点是黑色。

    性质3:每个叶子节点(NIL)是黑色。

    性质4:每个红色结点的两个子结点一定都是黑色。

    性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

    性质5.1:如果一个结点存在黑子结点,那么该结点肯定有两个子结点

第六章 List集合


6.1List接口介绍


java.util.List接口继承自Collection接口,是单列集合的重要分支,实现了List接口的对象称为List集合。在List集合中允许出现重复的元素,所有元素以一种线性方式进行存储,在程序中可以通过索引来访问集合中的指定元素。List集合的特点:元素有序,即元素的存入顺序和取出顺序一致。

List接口的特点:

1.元素存取有序的集合。(存储123,取出123)

2.带索引的集合,通过索引就可以精确的操作集合中的元素。

3.集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。

6.2List接口中常用方法


List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还新增了一些根据元素索引来操作集合的特有方法,如下:

  • **public void add (int index,E element)**:将指定的元素,添加到该集合的指定位置上。
  • **public E get(int index)**:返回集合指定位置上的元素。
  • **public E remove(int index)**:移除列表中指定位置的元素,返回的是被移除的元素。
  • **public E set(int index,E element)**:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。

第七章 List的子类


7.1ArrayList集合


​ 对象数组 :直接打印得到的是null

​ 数组里放的是对象的地址值。

缺点:一旦创建,程序运行期间数组的长度不可以改变。(为了解决这个问题,我们推出了ArralyList 即长度可变的数组)

ArralyList集合的概述和基本使用

​ 1.对于ArrayList集合来说,有一个尖括号代表泛型。

​ 2.泛型:也就是装在集合当中的所有元素,全都是统一的什么类型。

​ 3.注意:泛型只能是引用类型,不能是基本类型。

  • 对于ArrayList集合来说,直接打印得到的不是地址值而是内容。

​ 如果内容是空,得到的是空的中括号[ ]

package Demo;

import java.util.ArrayList;

public class Deom02ArrayList {
    public static void main(String[] args) {
        ArrayList<String> list=new ArrayList<>();//创建一个集合,集合里元素的数据类型是String类型
//        System.out.println(list);//[]
        //如何向集合当中添加数据
        list.add("西瓜");
        System.out.println(list);
    }
}

ArralyList集合的常用方法

  • public boolean add(E e):向集合当中添加元素,参数的类型和泛型一致。返回值代表添加是否成功。

    备注:对于ArralyList集合来说add动作一定会成功,但是对于其他集合来说add动作不一定成功。

  • public E get(int index):从集合当中获取元素,参数是索引编号,返回值就是对应位置的元素。

  • public E remove(int index):从集合当中删除元素,参数是索引编号,返回值就是被删除掉的元素。

  • public int size():获取集合的尺寸长度,返回值是集合当中包含的元素个数。

    package Rosy;
    
    import java.util.ArrayList;
    
    public class Rosy03ArrayListMethod {
        public static void main(String[] args) {
            ArrayList<String> list=new ArrayList<>();
            //向集合中添加元素   add 是有返回值的
            boolean success=list.add("西瓜");//返回值代表添加是否成功
            System.out.println(list);
            System.out.println(success);//ture
            list.add("苹果");
            list.add("芒果");
            list.add("火龙果");
            list.add("柚子");
            list.add("哈密瓜");
            System.out.println(list);//[西瓜, 苹果, 芒果, 火龙果, 柚子, 哈密瓜]
            //从集合中获取元素   get    索引值从0开始
            String fruit=list.get(2);
            System.out.println("第二号索引位置:"+fruit);
            //从集合中删除元素   remove 索引值从0开始
            String whoremoved=list.remove(3);
            System.out.println("被删除的元素为:"+whoremoved);//火龙果
            System.out.println(list);//[西瓜, 苹果, 芒果,  柚子, 哈密瓜]
            //获取集合的长度尺寸,也就是其中元素的个数     size
            int size=list.size();
            System.out.println("此时集合的长度为:"+size);
        }
    }

遍历集合

==ArralyListEach==

package Rosy;

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

public class RosyArrayListEach {
    public static void main(String[] args) {
        ArrayList<String>list=new ArrayList<>();
        list.add("苹果");
        list.add("火龙果");
        list.add("芒果");
        list.add("哈密瓜");
        list.add("木瓜");
//for循环
        for (int i=0;i< list.size();i++){
            System.out.println(list.get(i));
        }

//增强for循环
        for(String str : list){
            System.out.println(str);
        }
//迭代器
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

ArrayList集合存储基本数据

  • 如果希望向集合ArrayList中存储基本数据类型,必须使用基本类型对应的“包装类”。

基本类型 包装类(引用类型,包装类都位于java.lang包下)

byte Byte

short Short

int Integer 【特殊】

long Long

float Float

double Double

char Character 【特殊】

boolean Boolean

package Rosy;

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

public class RosyArrayListBasic {
    public static void main(String[] args) {
        ArrayList<Integer> listA=new ArrayList<>();
        listA.add(3);
        listA.add(8);
        listA.add(7);
        Iterator<Integer>iterator=listA.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

==从jdk1.5开始,支持自动装箱拆箱==

自动装箱

基本数据类型自动转换为包装类型

自动拆箱

包装数据类型自动转换为基本类型

例一:生成六个1~33之间的随机数,并放在集合中,且遍历集合

package Example;

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

class Example1{
    public static void main(String[] args) {
        Random random=new Random();
        ArrayList<Integer>list=new ArrayList<>();
        for (int i = 0; i < 6; i++) {
            int r=random.nextInt(33)+1;
            list.add(r);
        }
        Iterator<Integer>iterator=list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

7.2LinkedList集合


java.util.LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。

java.util.LinkedList集合 implement List接口

LinkedList集合的特点:

  • 底层是一个双向链表结构,查询慢,增删快

  • 里面包含了大量操作首尾元素的方法

    注意:使用LinkedList集合特有的方法,不能使用多态

LinkedList是一个双向链表

实际开发中对一个集合元素的增添和删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法

  • public void addFirst(E e)====push()压栈:将指定元素插入此列表的开头。
  • public void addLast(E e):将指定元素添加到此列表的结尾。
  • getFirst() getLast():返回此列表的第一个元素/返回此列表的最后一个元素
  • removeFirst()==pop() 弹栈 removeLast():移除并返回此列表的第一个元素/移除并返回此列表的最后一个元素

代码如下:

package Demo02;

import java.util.Iterator;
import java.util.LinkedList;

public class Demo10LinkedList {
    public static void main(String[] args) {
        LinkedList<String> list01=new LinkedList<>();
        list01.add("小明");
        list01.add("小华");
        list01.add("小王");
        System.out.println(list01);
        /*
        想添加【小金】到表头
        使用addFirst();或push();方法
         */
//        list01.addFirst("小金");
        list01.push("小金");//进栈
        Iterator<String> iterator01=list01.iterator();
        while (iterator01.hasNext()){
            System.out.println(iterator01.next());
        }
        System.out.println(list01);
        /*
         想添加【小许】到表尾
         使用addLast();方法
         */
        list01.addLast("小许");
        Iterator<String> iterator02=list01.iterator();
        while (iterator02.hasNext()){
            System.out.println(iterator02.next());
        }
        System.out.println(list01);
        /*
           返回列表的第一个和最后一个元素   getFirst();和getLast()方法
      */
        System.out.println(list01.getFirst());
        System.out.println(list01.getLast());
        /*
        移除列表的第一个元素
        使用removeFirst();或pop();方法 弹栈
         */
//        list01.removeFirst();
        list01.pop();
        System.out.println(list01);
        /*
        移除列表的最后一个元素
        使用removeLast();方法
         */
        list01.removeLast();
        System.out.println(list01);
    }
    
}

7.3Vector集合

Vector 类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。(了解即可) 单线程,速度慢


第八章 Set集合

java.util.Set接口和java.util.List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格了。与List接口不同的是,Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。

Set集合有多个子类,我们介绍的是java.util.HashSetjava.util.LinkedHashSet这两个集合

tips:Set集合取出元素的方式可以采用:迭代器、增强for。

8.1HashSet集合


java.util.HashSetSet接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。java.util.HashSet底层其实是一个java.util.HashMap支持。

HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此和具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCodeequals方法

特点:

  • 不允许存储重复的元素
  • 没有索引,没有带索引的方法,也不能使用普通的for循环遍历
  • 是一个无序的集合,存储元素和取出元素的顺序有可能不一致
  • 底层是一个哈希表结构(查询的速度非常的快)
package Demo02;

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

public class Demo11Set {
    public static void main(String[] args) {
        Set<Integer> set=new HashSet<>();
        set.add(1);
        set.add(3);
        set.add(2);
        set.add(1);
        Iterator<Integer> iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());//1,2,3
        }
        System.out.println("----------------------------------------");
        for (Integer a:set) {
            System.out.println(a);
        }
    }
}

8.2哈希表


HashSet集合存储数据的结构

JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过Key值依次查找的效率较低。

JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查询时间。

哈希值(Hash Code):是一个十进制的整数,由系统随即给出(就是对象的地址值,是一个逻辑地址,是模拟出来的,不是数据实际存储的物理地址)

在Object类有一个hashCode();方法,返回该对象的哈希码值。

源码:public native int hashCode( ); native:代表该方法调用的是本地操作系统的方法

tips:toString方法返回的就是哈希值的十六进制值,哈希值和toString返回的值,都不是真正物理意义上的地址值,是模拟的假的地址值

package Demo02;

public class Demo12HashCode {
    public static void main(String[] args) {
        persion p1=new persion();
        persion p2=new persion();
        int code1 = p1.hashCode();
        int code2 = p2.hashCode();
        System.out.println(code1);//重写前:460141958     重写后:1
        System.out.println("--------------------------");
        System.out.println(code2);//重写前:1163157884    重写后:1
        System.out.println(p1);//重写前:Demo02.persion@1b6d3586  重写后:Demo02.persion@1
        System.out.println(p2);//重写前:Demo02.persion@4554617c  重写后:Demo02.persion@1
        System.out.println(p1==p2);//false  即真正意义上的地址值不相等
    }
}
/*
     1b6d3586(十六进制)--->(十进制)460141958
     4554617c(十六进制)--->(十进制)1163157884
 */
class persion extends Object{
        @Override
    public int hashCode() {
//    return super.hashCode();源码
        return 1;//重写hashCode方法
    }
}

tips:String类里重写了hashCode方法

hashCode

public int hashCode()

返回此字符串的哈希码。String 对象的哈希码根据以下公式计算:

**s[0]31^(n-1) + s[1]31^(n-2) + … + s[n-1]

使用 int 算法,这里 s[i] 是字符串的第 i 个字符,n 是字符串的长度,^ 表示求幂。(空字符串的哈希值为 0。)


哈希冲突

两个元素不同,但是哈希值相同

数组超过8位

少6变回数组

解决办法:

  • 拉链法
  • 开放寻址

8.3Set集合存储元素不重复原理


哈希表中,没有重复元素的原因:集合在调用add方法时,add方法会调用元素的hashCode方法和equals方法。

package Demo02;

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

public class Demo13HashSetSaveString {
    public static void main(String[] args) {
        Set<String> set=new HashSet<>();
        String s1=new String("abc");
        String s2=new String("abc");
        set.add(s1);
        set.add(s2);
        set.add("重地");
        set.add("通话");
        set.add("abc");
        System.out.println(set);//[重地, 通话, abc]
    }
}


8.4HashSet存储自定义类型元素

给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一。

package Demo02;

import java.util.HashSet;
import java.util.Objects;

public class Demo14HashSetSavePersion {
    public static void main(String[] args) {
        HashSet<persions> set=new HashSet<>();
        persions p1=new persions("小明",21);
        persions p2=new persions("小明",21);
        persions p3=new persions("小明",24);
        set.add(p1);
        set.add(p2);
        set.add(p3);
        System.out.println(p1.hashCode());//hashCode和equals方法重写前:460141958
        //重写后:23458775
        System.out.println(p2.hashCode());//hashCode和equals方法重写前:1163157884
        //重写后:23458775
        System.out.println(p1==p2);//hashCode和equals方法重写前:false
        //重写后:false
        System.out.println(p1.equals(p2));//hashCode和equals方法重写前:false
        //重写后:true
        System.out.println(set);//hashCode和equals方法重写前:[persions{name='小明', age=24}, persions{name='小明', age=21}, persions{name='小明', age=21}]
        //重写后:[persions{name='小明', age=21}, persions{name='小明', age=24}]
    }
}

class persions{
    private String name;
    private int age;

    public persions(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        persions persions = (persions) o;
        return age == persions.age &&
                Objects.equals(name, persions.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "persions{" +
                "name='" + name + '\'' +
                ", 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;
    }
}

8.5LinkedHashSet


我们知道HashSet保证元素唯一,但是元素存进去是没有顺序的,要保证有序如何实现。

在HashSet下面有一个子类java.util.LinkedHashSet,它是链表和哈希表组合的一个数据存储结构。

代码演示如下:

package Demo02;

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

public class Demo15LinkedHashSet {
    public static void main(String[] args) {
        Set<String> set=new LinkedHashSet<>();
        set.add("ddd");
        set.add("bbb");
        set.add("ccc");
        set.add("aaa");
        set.add("sss");
        Iterator<String> iterator=set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。注意,插入顺序 受在 set 中重新插入的 元素的影响。(如果在 s.contains(e) 返回 true 后立即调用 s.add(e),则元素 e 会被重新插入到 set s 中。)

此实现可以让客户免遭未指定的、由 HashSet 提供的通常杂乱无章的排序工作,而又不致引起与 TreeSet 关联的成本增加。使用它可以生成一个与原来顺序相同的 set 副本,并且与原 set 的实现无关:

void foo(Set s) {
    Set copy = new LinkedHashSet(s);
    ...
}

如果模块通过输入得到一个 set,复制这个 set,然后返回由此副本决定了顺序的结果,这种情况下这项技术特别有用。(客户通常期望内容返回的顺序与它们出现的顺序相同。)

此类提供所有可选的 Set 操作,并且允许 null 元素。与 HashSet 一样,它可以为基本操作(addcontainsremove)提供稳定的性能,假定哈希函数将元素正确地分布到存储段中。由于增加了维护链接列表的开支,其性能很可能会比 HashSet 稍逊一筹,不过,这一点例外:LinkedHashSet 迭代所需时间与 set 的大小 成正比,而与容量无关。HashSet 迭代很可能支出较大,因为它所需迭代时间与其容量 成正比。

链接的哈希 set 有两个影响其性能的参数:初始容量加载因子。它们与 HashSet 中的定义极其相同。注意,为初始容量选择非常高的值对此类的影响比对 HashSet 要小,因为此类的迭代时间不受容量的影响。

注意,此实现不是同步的。如果多个线程同时访问链接的哈希 set,而其中至少一个线程修改了该 set,则它必须 保持外部同步。这一般通过对自然封装该 set 的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装”该 set。最好在创建时完成这一操作,以防止意外的非同步访问:

Set s = Collections.synchronizedSet(new LinkedHashSet(...));

此类的 iterator 方法返回的迭代器是快速失败 的:在迭代器创建之后,如果对 set 进行修改,除非通过迭代器自身的 remove 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。

注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何强有力的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。

此类是 Java Collections Framework 的成员。

8.6 可变参数


tips:当使用参数类型确定,而个数不确定时,可以使用可变参数

JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下格式:

修饰符 返回值类型 方法名(参数类型...形参名){ }

等价于

修饰符 返回值类型 方法名(参数类型[] 形参名){ }

只是后面这种定义,在调用时必须传递数组,而前者可以直接传递数据即可。

JDK1.5以后。出现了简化操作。…用在参数上,称之为可变参数。

同样是代表数组,但是在调用这个带有可变参数的方法时,不用创建数组(这就是简单之处),直接将数组中的元素作为实际参数进行传递,其实编译成的class文件,将这些元素先封装到一个数组中,在进行传递。这些动作都在编译.class文件时,自动完成了。

原理:可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数,传递的参数个数,可以是0个(不传递),1,2. . .多个

package Demo02;

public class Demo16VarArgs {
    public static void main(String[] args) {
//        int i = add();
//        System.out.println(i);[I @1b6d3586  [代表数组        I代表int类型
        int s=add(10,20,30,40);
        System.out.println(s);//100
    }
    public static int add(int...arr){
        System.out.println(arr.length);//4
        int sum=0;
        for (int i:arr) {
            sum+=i;
        }
        return sum;
    }
}

注意事项:1.一个方法的参数列表,只能有一个可变参数

//    public static void method(int...a,String...b){  会报错
//    
//    }
   public static void method(String...b){

}

​ 2.如果方法的参数有多个,那么可变参数必须写在参数列表的末尾

//    public static void method(int...i,int a,String b,double c){  会报错
//
//    }
    public static void method(String b,int a,double c,int...i){

    }

第九章 Collections


9.1常用功能


  • java.util.Collections是集合工具类,用来对集合进行操作,部分方法如下:
  • public static <T> boolean addAll(Collection<T> c,T... elements):向集合中添加一些元素。
  • public static void shuffle(List<?> list):打乱集合元素的顺序。
  • public static <T> void sort(List<T> list):将集合中元素按照默认规则排序
  • public static <T> void sort(List<T> list,Comparator<? super T>):将集合中元素按照指定规则排序

tips:sort(List list)使用前提:被排序的集合里的元素,必须实现Comparable,重写接口中的方法comparTo定义排序的规则

package Demo02;

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

public class Demo17Collections {
    public static void main(String[] args) {
        ArrayList<String> list=new ArrayList<>();
        /*
        向集合中添加多个元素
         */
        Collections.addAll(list,"a","b","c","d","e");
        System.out.println(list);//[a, b, c, d, e]
        /*
        打乱集合中元素的顺序
         */
        Collections.shuffle(list);
        System.out.println(list);
        /*
        集合中元素按照默认方式排序
         */
        Collections.sort(list);//默认是升序
        System.out.println(list);
        /*
        集合中元素按照指定方式排序
         */
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return 0;
            }
        });
        System.out.println(list);
    }
}

自定义类型

Comparable接口的排序顺序:

自己(this)-参数:升序

参数-自己(this):降序

Comparator和Comparable的区别

Comparable:自己(this )和别人(参数)比较,自己需要实现comparable接口,重写比较的规则compareTo方法

Comparator:相当于找—个第三方的裁判,比较两个

package Demo02;

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

public class Demo18Sort {
    public static void main(String[] args) {
        ArrayList<Persion> list=new ArrayList<>();
        list.add(new Persion("小明",21));
        list.add(new Persion("a小李",19));
        list.add(new Persion("小张",29));
        list.add(new Persion("b小王",19));
        Collections.sort(list);
        System.out.println(list);//[Persion{name='小明', age=21}, Persion{name='小李', age=22}, Persion{name='小张', age=29}, Persion{name='小王', age=19}]
        System.out.println("---------------------------");
        Collections.sort(list, new Comparator<Persion>() {
            @Override
            public int compare(Persion o1, Persion o2) {
//                return o1.getAge()-o2.getAge();按年龄升序排序
//                return o2.getAge()-o1.getAge();按年龄降序排序
                int result=o1.getAge()-o2.getAge();//按年龄升序排序
                if(result==0){
                    result= o1.getName().charAt(0)-o2.getName().charAt(0);//首字母排序
                }
                return result;
            }
        });
        System.out.println(list);
    }
}

class Persion implements Comparable<Persion>{
    private String name;
    private int age;


    public Persion(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Persion{" +
                "name='" + name + '\'' +
                ", 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 int compareTo(Persion o) {
        return 0;//认为元素都是相同的
        /*
        可以自定义比较方法 比较两人的年龄(this,参数Person)
        return this.getAge()-o.getAge()//按年龄升序排序
        return o.getAge()-this.getAge()//按年龄降序排序
         */
    }
}

第十章 Map集合(双列集合)


10.1概述

前几章我们学习的都是单列集合,而Map是双列集合。

Map<K,V>,K我们称为键(此映射所维护的键的类型),V我们称为值(映射值的类型),键是不允许重复的,value可以重复。

tips:Map集合中K和V的关系类似于函数自变量和函数值的关系。

在现实生活中,我们常会看到这样一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类来存放这种对象关系的对象,即java.util.Map接口。

对比Collection接口与Map接口的不同:

10.2Map常用子类


  • HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。

    java.util.HashMap<K,V>集合 implements Map<K,V>接口

    特点:1. HashMap集合底层是哈希表:查询速度特别快

    ​ JDK1.8之前:数组+单向链表

    ​ JDK1.8之后:数组+单向链表/红黑树(当链表长度超过8):提高查询的速度

    ​ 2.HashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致

  • LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。

    java.util.LinkedHashMap<K,V>集合 extends HashMap<K,V>集合

    特点:1. LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)

    ​ 2.LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的

tips:Map接口中的集合都有两个泛型变量<K,V>,在使用时,要为两个泛型变量赋予数据类型。两个泛型变量<K,V>的数据类型可以相同,也可以不同。

10.3Map接口中的常用方法


  • public V put(k Key,V value):把指定的键与指定的值添加到Map集合中

    返回值:V

    ​ 存储时,Key不重复,返回值V时null

    ​ 存储时,Key重复,会使用新的value替换Map中重复的value,返回被替换的value值

  • public V remove(Object key):把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。

    返回值:V

    ​ Key存在,V返回被删除的值

    ​ Key不存在,V返回null

  • public V get(Object Key):根据指定的键,在Map集合中获取对应的值。

    返回值:V

    ​ Key存在,V返回该K对应的值

    ​ Key不存在,V返回null

  • boolean containsKey(Object Key):判断集合中是否包含指定的键。

    ​ Key存在,返回true

    ​ Key不存在,返回false

  • public Set<K> KeySet():获取Map集合中所有的键,存储到Set集合中。

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

package Demo03;

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

public class Demo01Map {
    public static void main(String[] args) {
//      show01();
//      show02();
        show03();
    }
    /*
       public Set<Map.Entry<K,V>> entrySet():获取到Map集合中所有的键值对对象的集合(Set集合)。
     */
    public static void show03(){
        HashMap<String,Integer> map03= new HashMap<>();
        map03.put("小明",176);
        map03.put("小华",178);
        map03.put("小金",169);
        map03.put("小林",183);
        /*
        boolean containsKey(Object Key):判断集合中是否包含指定的键。
         */
        boolean b1 = map03.containsKey("小金");
        System.out.println(b1);//true
        boolean b2 = map03.containsKey("小李");
        System.out.println(b2);//false
        System.out.println(map03);//{小林=183, 小明=176, 小华=178, 小金=169}
        System.out.println("---------------------");
        Set<Map.Entry<String, Integer>> set = map03.entrySet();
        System.out.println(set);//[小林=183, 小明=176, 小华=178, 小金=169]
    }
    /*
    public V remove(Object key):把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
     */
    public static void show02(){
        HashMap<String,Integer> map02= new HashMap<>();
        map02.put("小明",87);
        map02.put("小华",97);
        map02.put("小金",87);
        map02.put("小林",99);
        System.out.println(map02);
        Integer a1 = map02.remove("小李");
        System.out.println(a1);//null
        System.out.println("----------------");
        Integer a2 = map02.remove("小金");
        System.out.println(a2);//87
        /*
        public V get(Object Key):根据指定的键,在Map集合中获取对应的值。
         */
        Integer a3 = map02.get("小林");
        System.out.println(a3);//99
        Integer a4 = map02.get("小琳");
        System.out.println(a4);//null
    }
    /*
    public V put(k Key,V value):把指定的键与指定的值添加到Map集合中
     */
    public static void show01(){
        HashMap<String,String> map01= new HashMap<>();
        String s1 = map01.put("计算机", "鼠标");
        System.out.println(s1);//null
        String s2 = map01.put("计算机", "键盘");
        System.out.println(s2);//鼠标   返回的是被替换的值
        map01.put("硬件","软件");
        map01.put("java","C++");
        map01.put("C","C++");
        System.out.println(map01);//{java=C++, C=C++, 硬件=软件, 计算机=键盘}
        /*
        public Set<K> KeySet():获取Map集合中所有的键,存储到Set集合中。
         */
        System.out.println("----------------------------");
        Set<String> set = map01.keySet();
        System.out.println(set);//[java, C, 硬件, 计算机]

    }
}

10.4 Map集合遍历键找值方式


键找值方式:即通过元素中的键,获取键所在的值

分析步骤:

1.获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。方法提示KeySet()

2.遍历键的Set集合,得到每一个键

3.根据键,获取键所对应的值。方法提示get(K key)

package Demo03;

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

public class Demo02KeySet {
    public static void main(String[] args) {
       show01();
    }

    public static void show01() {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("小明", 176);
        map.put("小华", 178);
        map.put("小金", 169);
        map.put("小林", 183);
        /*
        public Set<K> KeySet():获取Map集合中所有的键,存储到Set集合中。
         */
        Set<String> set = map.keySet();
        System.out.println(set);//[小林, 小明, 小华, 小金]
        System.out.println("----------------------------");
        /*
       迭代器遍历集合
         */
        Iterator<String> iterator=set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());

            /*
            增强for
             */
            for (String Key:set) {
                Integer integer01 = map.get(Key);//通过Key找Value
                System.out.println(integer01);
            }
            for (String Key:map.keySet()) {
                Integer integer02 = map.get(Key);//通过Key找Value
                System.out.println(integer02);
            }
        }
    }
}

10.5Entry键值对对象


Map中存放的是两种对象,一种称为Key(键),一种称为value(值),它们在Map中是一一对应关系,这一对对象又称做Map中的一个Entry(项)。Entry将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键和对应的值。

获取对应的键和对应的值的方法:

  • public K getKey( ):获取Entry对象中的键。
  • public V getValue( ):获取Entry对象中的值。

获取所有Entry对象的方法:

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

10.6Map集合遍历键值对方式


步骤:

1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中

2.遍历Set集合,获取每一个Entry对象

3.使用Entry对象中的方法getKey()和getValue()获取键与值

package Demo03;

import java.security.Key;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Demo03EntrySet {
    public static void main(String[] args) {
       show01();
    }
    public static void show01(){
        HashMap<String,Integer> map=new HashMap<>();
        map.put("小明",87);
        map.put("小华",97);
        map.put("小金",87);
        map.put("小林",99);
        Set<Map.Entry<String, Integer>> set = map.entrySet();
        System.out.println(set);//[小林=99, 小明=87, 小华=97, 小金=87]
        System.out.println("------------------------------------");
        Iterator<Map.Entry<String,Integer>> iterator=set.iterator();
        while (iterator.hasNext()){
            Map.Entry<String,Integer> entry01=iterator.next();
            String key01 = entry01.getKey();
            Integer value01 = entry01.getValue();
            System.out.println(key01+"="+value01);
//            小林=99
//            小明=87
//            小华=97
//            小金=87
            /*
            通过增强for
             */
            System.out.println("--------------------------------");
            for (Map.Entry<String,Integer> entry02:set) {
                String key02 = entry02.getKey();
                Integer value02 = entry02.getValue();
                System.out.println(key02+"="+value02);
            }
        }
    }
}

10.7HashMap存储自定义类型键值


练习:每位学生(姓名,年龄)都有自己的家庭地址。那么,既然有对应关系,则将学生对象和家庭住址存储到Map集合中。学生作为键,家庭住址作为值。

tips:学生姓名相同并且年龄相同视为同一名学生

package Demo03;

import jdk.internal.org.objectweb.asm.tree.analysis.Value;

import java.security.Key;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public class Demo04HashMapSavePerson {
    public static void main(String[] args) {
//      show01();
        show02();
    }

    public static void show02(){
        HashMap<Student,Integer> map=new HashMap<>();
        map.put(new Student("小明",18),178);
        map.put(new Student("小李",19),175);
        map.put(new Student("小林",20),179);
        map.put(new Student("小金",18),183);
        map.put(new Student("小笠",18),178);
        map.put(new Student("小笠",18),178);
        Set<Map.Entry<Student, Integer>> set = map.entrySet();
        for (Map.Entry<Student,Integer> a:set) {
            Integer value = a.getValue();
            Student key = a.getKey();
            System.out.println(key+"---->"+value);
        }
    }

    /*
   HashMap存储自定义类型键值
   key : string类型
         string类重写hashcode方法和equals方法,可以保证key唯一
   value : Person类型
    value可以重复(同名同年龄的人视为同一个)
 */
    public static void show01(){
        HashMap<String,Student> map=new HashMap<>();
        map.put("北京",new Student("小明",18));
        map.put("上海",new Student("小李",19));
        map.put("北票",new Student("小林",20));
        map.put("沈阳",new Student("小金",18));
        map.put("哈尔滨",new Student("小笠",18));
        map.put("哈尔滨",new Student("小笠",18));
        Set<String> set = map.keySet();
        for (String Key: set) {
            Student value=map.get(Key);
            System.out.println(Key+"--->"+value);
        }
    }
}

class Student{
    private String name;
    private int age;

     @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    public Student(){

    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", 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;
    }
}

10.7LinkedHashMap


HashMap保证元素唯一,并且查询速度很快,可是我们要保证元素有序,还要速度快,就要了解LinkedHashMap,它是HashMap下面有一个子类,它是链表和哈希表组合的一个数据存储结构。

package Demo03;

import java.security.Key;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

public class Demo05LinkedHashMap {
    public static void main(String[] args) {
        show01();
    }
    public static void show01(){
        LinkedHashMap<String,Integer> map=new LinkedHashMap<>();
        map.put("小明",18);
        map.put("小李",20);
        map.put("小林",18);
        Set<Map.Entry<String, Integer>> set = map.entrySet();
        for (Map.Entry<String, Integer> entry:set) {
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key+"--->"+value);
//            小明--->18
//            小李--->20
//            小林--->18
        }
    }
}

10.8Hashtable集合


Hashtable<k,v> implements Map<K,V> 双列集合(JDK1.0)比较早

此类实现了一个哈希表,该哈希表将键映射到相应的值。任何非null对象都可以用做键或值。Hashtable是同步的(单线程):速度慢

  • HashtabLe:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢,不能存储null值, null键

  • HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快,可以存储null值, null键

Hashtable和vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap , ArrdyList)取代了

HashtabLe的子类Properties依然活跃在历史舞台 Properties集合是一个唯—和I0流相结合的集合

package Demo03;

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

public class Demo05Hashtable {
    public static void main(String[] args) {
//      show02();
        show01();
    }
    public static void show02(){
        HashMap<String,String> map=new HashMap<>();
        map.put(null,"a");
        map.put("a",null);
        map.put(null,null);
        System.out.println(map);//{null=null, a=null}
    }
    public static void show01(){
        Hashtable<String,String> hashtable=new Hashtable<>();
        hashtable.put(null,"a");//NullPointerException
        hashtable.put("a",null);//NullPointerException
        hashtable.put(null,null);//NullPointerException
        System.out.println(hashtable);
    }
}

10.9Map集合练习


需求:

计算一个字符串中每个字符出现次数

分析

​ 1.获取一个字符串对象

​ 2.创建一个Map集合,键代表字符,值代表次序

​ 3.遍历字符串得到每个字符

​ 4.判断Map中是否有该键

​ 5.如果没有,第一次出现,存储次数为1;如果有,则说明已经出现过,获取到对应的值进行++,再次存储

​ 6.打印最终结果

分析:
1.使用scanner获取用户输入的字符串
2.创建Map集合,key是字符串中的字符, value是字符的个数

3.遍历字符串,获取每一个字符
4.使用获取到的字符,去Nap集合判断key是否存在
key存在:
通过字符(key) ,获取value(字符个数)value++
put( key , value)把新的vatue存储到Map集合中

key不存在:
put( key ,1)
5.遍历Map集合,输出结果

package Demo03;

import java.security.Key;
import java.util.HashMap;
import java.util.Scanner;

public class Demo06MapTest {
    public static void main(String[] args) {
      show01();
    }
    public static void show01(){
        Scanner in=new Scanner(System.in);
        System.out.println("请输入字符串:");
        String s=in.next();
        HashMap<Character,Integer> map=new HashMap<>();
        for (char c:s.toCharArray()) {
            if (map.containsKey(c)){
                Integer value = map.get(c);
                value++;
                map.put(c,value);
            }else {
                map.put(c,1);
            }
        }
        for (Character Key:map.keySet()) {
            Integer value = map.get(Key);
            System.out.println(Key+"--->"+value);
        }
    }
}

10.10JDK9对集合添加的优化


添加元素时,List和Set集合使用add方法,Map集合使用put方法

JDK9时,新的List、Set、Map的静态工厂方法可以更方便地创建集合的不可变实例。

of方法(了解即可)


附加:Debug追踪

Debug调试程序:

​ 可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug。

使用方式:

​ 在行号的右边,鼠标左键单击,添加断点(每个方法的第一行,哪里有bug添加到哪里)

​ 右键,选择Debug执行程序

​ 程序就会停留在添加的第一个断点处

执行程序:

​ f8:逐行执行程序

​ f7:进入到方法中

​ shift+f8:跳出方法

​ f9:跳到下一个断点,如果没有下一个断点,那么就借书程序

​ ctrl+f2:退出debug模式,停止程序

​ Console:切换到控制台