Java基础进阶 自定义泛型、Map集合

自定义泛型

1.1、泛型的定义介绍

在集合中,不管是接口还是类,它们在定义的时候类或接口名的后面都使用<标识符>,当我们在使用的时候,可以指定其中的类型。如果当前的类或接口中并没有<标识符>,我们在使用的时候也不能去指定类型。

举例:比如我们之前所学习的集合ArrayList类:


1.png

说明:在ArrayList类上有一个泛型的参数:E
设计API的时候,设计者并不知道我们要用ArrayList存储哪种数据类型,所以他定义了一个泛型。
然后当我们使用ArrayList的时候,我们知道要存储的类型,比如要存储String类型:

2.png

当我们new对象的时候,就把泛型E替换成了String,于是JVM就知道我们要存储的其实是String。

泛型:如果我们不确定数据的类型,可以用一个标识符来代替。

如果我们在使用的时候已经确定要使用的数据类型了,我们在创建对象的时候可以指定使用的数据类型。

泛型自定义格式:
<标识符>
这里的标识符可以是任意的字母、数字、下划线和 $ 。但是这里一般规范使用单个大写字母。
注意:自定义泛型也属于标识符,满足标识符的命名规则。1)数字不能开始;2)关键字不能作为标识符;

根据以上分析我们可以思考一个问题:既然我们学习过的集合类可以定义泛型,那么我们自己在描述类或接口的时候,是否也可以在自己的类或接口上定义泛型,然后别人使用的时候也可以指定这个类型呢?
答案当然是可以的。

1.2、自定义泛型类(掌握)

泛型类:
在定义类的时候,在类名的后面书写泛型。
格式:

 class 类名<泛型参数>
 {
 }

泛型参数其实就是标识符。

分析和步骤:
1)创建测试类GenericDemo1 ,在这个类中定义一个main函数;
2)定义一个泛型类Demo<ABC>;
3)在这个类中定义一个成员变量name,类型是泛型ABC类型;
4)在定义一个非静态成员函数show,接收参数给name属性赋值,局部变量name的类型是ABC类型;
5)在main函数中创建Demo类的对象,并指定泛型分别是String 和Integer类型;

package cn.xuexi.generic.demo1;
/*
 * 自定义泛型类演示
 * 在类上定义的泛型,当外界创建这个对象的时候,由创建者指定泛型的类型
 * 在类上定义的泛型,类中的成员变量和函数都可以使用
 */
class Demo<ABC>
{
     ABC name;
    public void show(ABC name)
    {
        this.name=name;
    }
}
public class GenericDemo1 {
    public static void main(String[] args) {
        //创建Demo类的对象
        /*
         * 注意如果这里创建Demo类的对象时没有指定泛型类的类型时,这里ABC默认是Object类型
         * 如下代码所示创建Demo类的对象的时候,指定的泛型类类型是String类型,
         * 那么Demo类中的泛型类ABC就是String类
         * 同理,如果指定的数据类型是Integer,那么ABC就是Integer类
         */
                 //Demo<String> d=new Demo<String>();
        Demo<Integer> d=new Demo<Integer>();
                //d.show("哈哈");
        d.show(123);
        System.out.println(d.name);
    }
}

说明:
1)在类上定义的泛型,当外界在创建这个类的对象的时候,需要创建者自己来明确当前泛型的具体类型;
2)在类上定义的泛型,在类中的方法上和成员变量是可以使用的;
3)上述代码中如果创建Demo类的对象时没有指定泛型类的类型时,那么ABC默认是Object类型;
4)上述代码中如果创建Demo类的对象时指定的泛型类类型是String类型,那么ABC默认是String类型;

自定义泛型类的练习:
需求:使用自定义泛型模拟队列数据结构。

分析:
由于队列是集合中的一种,可以存储任意类型的数据,而对于队列类我们不确定具体存储什么数据类型,所以可以在队列类QueueDemo中指定泛型,根据创建者来决定什么数据类型。

步骤:
1)自定义一个类QueueDemo<E>,在这个类中创建集合类LinkedList<E>的对象list;
2)在QueueDemo类中定义一个添加数据的函数addElement(E e),在这个函数中使用对象list调用LinkedList集合类中addLast(e)函数向队列中添加数据;
3)定义一个函数getElement用来获得数据,返回值类型是E,并使用list对象调用LinkedList集合类中的removeFirst()函数;
4)在定义一个函数isNull()判断队列中是否还含有数据,返回值类型是布尔类型;
5)创建一个测试类GenericDemo,在这个类中创建队列类QueueDemo的对象qd,并在泛型位置指定数据类型String;
6)使用对象qd调用添加数据的函数addElement(),向队列中添加几个数据;
7)使用while循环遍历集合并取出;

自定义的队列类:

package cn.xuexi.generic.demo1;
import java.util.LinkedList;
/*
 *  模拟队列
 * 由于队列是集合中的一种,可以存储任意类型的数据,而对于队列类我们不确定具体存储什么
 * 数据类型,所以可以在队列类QueueDemo中指定泛型,根据创建者来决定什么类型
 */
public class QueueDemo<E> {
    //创建LinkedList集合的对象
    LinkedList<E> list=new LinkedList<E>();
    //自定义函数向队列中添加数据
    public void addElement(E e)
    {
        list.addLast(e);
    }
    //自定义函数取出队列中的数据
    public E getElement()
    {
        return list.removeFirst();
    }
    //自定义函数判断队列中是否还含有数据
    public boolean isNull()
    {
        /*
         * list.isEmpty() 表示集合中没有数据返回true 有数据返回false
         * 我们这里这么书写:!list.isEmpty() 表示集合中有数据返回true 没有数据返回false
         */
        return !list.isEmpty();
    }
}

定义测试类:

package cn.xuexi.generic.demo1;
/*
 * 测试模拟队列的类
 */
public class GenericDemo2 {
    public static void main(String[] args) {
        //创建队列类的对象,并指定数据类型
        QueueDemo<String> qd = new QueueDemo<String>();
        //使用对象调用函数向队列中添加数据
        qd.addElement("abc");
        qd.addElement("hello");
        qd.addElement("java");
        //取出数据
        while(qd.isNull())
        {
            //表示有数据
            String s = qd.getElement();
            System.out.println(s);
        }
    }
}
3.png

注意:对于自定义泛型类只有在创建这个类的对象的时候才可以指定泛型的类型。

1.3、在函数上使用泛型(掌握)

我们不仅可以在自己定义的类或接口上使用泛型,还可以在自定义的函数上使用泛型。
虽然可以在类上定义泛型,但是有时类中的方法需要接收的数据类型和类上外界指定的类型不一致。也就是说对于某个函数而言参数的数据类型和所属类的泛型类型不一致了,这时我们可以在这个类中的这个方法上单独给这个方法设定泛型。

在函数上使用泛型的格式:

  函数修饰符 <泛型名>  函数返回值类型  方法名( 泛型名 变量名 )
       {
          函数体;
   }

说明:函数返回值类型前面的<泛型名>相当于定义了方法参数列表中泛型的类型。

代码演示如下图所示:


4.png

说明:
1)类上的泛型是在创建这个类的对象的时候明确具体是什么类型;
2)方法上的泛型是在真正调用这个方法时,传递的是什么类型,泛型就会自动变成什么类型;
举例:上述代码中当调用者传递的是2,那么这个Q就代表Integer类型。
如果调用者传递的是new Student(“班长”,19),那么这个Q类型就是Student类型。
3)上述method函数中<Q>表示定义的泛型,而参数列表中的Q q是在使用泛型类型,而这里的Q类型具体由调用者来指定;

自定义泛型的练习:
需求:自定义泛型方法,定义一个方法,可以打印任意一个数组的内容。
打印String数组
打印Integer数组

分析和步骤:
1)分别定义一个String类型和Integer类型的数组,并分别存入数据;
2)定义两个函数都是printArray,但是参数列表不同,一个接收String类型的数组,另一个接收是Integer类型的数组;
3)分别遍历两个数组;
4)虽然上述做法可以,但是只要遍历不同类型的数组就得定义一个重载的函数,麻烦,我们可以使用在函数上自定义泛型<T>,函数的参数也是T[]类型的数组;

package cn.xuexi.generic.demo1;
/*
 * 需求:自定义泛型方法,定义一个方法,可以打印任意一个数组的内容。
     打印String数组
     打印Integer数组
 */
public class GenericDemo4 {
    public static void main(String[] args) {
        //定义数组
        String[] arr={"hello","world","java"};
        Integer[] arr2={10,20,30};
        //调用自定义函数分别打印数组
        /*printArray(arr);
        printArray(arr2);*/
        printArray2(arr);//传递String[]类型的数组的时候,T就变成了String类型
        printArray2(arr2);//传递Integer[]类型的数组的时候,T就变成了Integer类型
        /*
         * 上述做法可以定义两个函数来遍历不同数据类型的数组,这两个函数以函数重载的形式存在,但是相对
         * 来说比较麻烦,如果定义不同类型的数组,都会定义一个相对应的函数,太麻烦,这里我们可以借助
         * 泛型方法来简化代码开发
         */
    }
    /*
     * 由于要打印不同类型的数组,所以这里参数列表的类型不确定了,既然参数列表类型不确定了,我们可以使用
     * 泛型 在函数返回值类型前面还得定义相应的泛型
     */
    public static <T> void printArray2(T[] arr)
    {
        /*//使用for循环遍历数组
        for (int i = 0; i < arr.length; i++)
        {
            System.out.print(arr[i]+" ");
        }*/
        for (T t : arr) {
            System.out.print(t+" ");
        }
        //换行
        System.out.println("");
    }
    /*//遍历整数
    public static void printArray(Integer[] arr2) {
        //使用for循环遍历数组
        for (int i = 0; i < arr2.length; i++)
        {
            System.out.print(arr2[i]+" ");
        }
    }
    //遍历字符串
    public static void printArray(String[] arr) {
        //使用for循环遍历数组
        for (int i = 0; i < arr.length; i++)
        {
            System.out.print(arr[i]+" ");
        }
        //换行
        System.out.println("");
    }*/
}

说明:因为参数中的数组类型不确定,遍历时候的数据类型也不确定,所以都用泛型T来代替。

总结:
1)自定义泛型方法格式:
** 修饰符 <泛型> 返回值 函数名(参数列表 ){}**
2)自定义泛型方法,泛型必须在函数返回值之前和函数修饰之后定义,否则报错;
3)泛型方法中泛型的确定:在方法被调用的时候,传递参数的时候才确定;

注意事项:
静态函数上的泛型:
静态函数不能使用类上定义的泛型。
因为静态方法的运行是不需要对象的,而类上的泛型必须在创建这个类对象的时候才能明确具体是什么类型。
而静态函数运行的时候是没有对象的,也就是说类上的泛型在静态函数运行的时候还不知道是什么类型。
如果一个静态方法,也需要泛型,请使用自定义泛型方法。

静态函数的使用注意事项代码演示如下:

5.png

1.4、泛型接口和泛型传递(掌握)

通过查阅API得知,类支持泛型,那么接口也可以支持泛型,比如集合中的接口:
接口Collection<E>
接口List<E>

那么既然API中的接口支持泛型,自己定义的接口同样也可以使用泛型。

泛型接口的格式:
修饰符 interface 接口名<泛型>{}

问题:泛型类的泛型,在创建类的对象时确定。
那么接口又无法创建对象,什么时候确定泛型的类型呢?有两种方式可以实现。

方式1:类实现接口的时候,直接明确泛型类型。

7.png

源代码中的String类的的实现:

8.png

方式2:类实现接口的时候,还不确定数据类型,这时可以在实现类上随便先定义个泛型,当这个类被创建对象的时候,

就明确了类上的泛型,于是接口上的泛型也明确了。
我们管方式2这种方式叫做泛型的传递。

代码实现如下:

9.png

举例,API中集合的泛型使用情况解释如下所示:

比如:
interface Collection<E>{
}
interface List<E> extends Collection<E>{
}
class ArrayList<E> implements List<E>{
}
ArrayList<String> list = new ArrayList<String>();

结论:通过以上操作,上述集合接口中的泛型类型是String类型。

1.5、泛型通配符(掌握泛型中的通配符的使用)

需求:定义功能,打印集合中的对象。

分析和步骤:
1)定义一个类GenericDemo6,在这个类中的主函数中创建ArrayList<String>集合的对象list;
2)使用对象list调用add()函数向集合中添加几个字符串数据;
3)再创建一个HashSet<Integer>集合类的对象set;
4)使用对象set调用add()函数向集合中添加几个整数数据;
5)分别定义两个函数来对上述的集合进行遍历;

10.png

上述定义了2个打印集合的方法,方法体中都是在使用迭代器遍历集合。区别是方法上接收的集合类型不同,还有集合中保存的元素的数据类型不同。这样使用迭代器遍历的时候,迭代器的泛型需要根据集合中保存元素的具体的数据类型才能确定,其他的代码书写都相同。这样会导致上述代码重复冗余,可以对上述代码进行进一步的优化。

换句话说:通过观察发现上述的两个函数以函数重载的形式存在,几乎一模一样,还有泛型的数据类型不一致,还有集合的类型不一致,一个是ArrayList,另一个是HashSet,所以代码冗余,我们可以简化代码的开发,我们可以只定义一个函数来遍历集合。既然两个集合不一样,我们这里可以书写集合的父类或者父接口Collection。由于集合中保存的数据类型也不一致,这里也没法写,写String 不行 Integer 也不可以。

说明:这里书写Object不可以,因为泛型格式要求两侧必须类型一致,这里不一致了,会报错
Collection<Object> coll=new ArrayList<String>()

所以这里我们只能借助泛型中通配符可以实现 : ?
? 表示泛型的通配符,表示集合中的任意数据类型,传递过来的表示什么数据类型,?就表示什么数据类型

分析:
现在需要定义一个方法既可以接收ArrayList,又可以接收HashSet集合容器对象,只能使用它们的父类或接口类型来接收,这样就可以使用Collection接口类型接收。
在写方法的时候,如果要接收别人的数据,但是使用泛型不知道具体的数据类型,可以使用泛型的通配符 ? 来表示。
? :表示的是任意的数据类型。当真正传递过来的集合中的数据是什么类型,这个?就会自动的变成什么类型。

11.png

完整代码如下所示:

package cn.xuexi.generic.demo1;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
/*
 * 需求:定义功能,打印集合中的对象。
 */
public class GenericDemo6 {
    public static void main(String[] args) {
        //创建ArrayList集合对象
        ArrayList<String> list=new ArrayList<String>();
        //向集合中添加数据
        list.add("aaa");
        list.add("ccc");
        list.add("bbb");
        list.add("ghfas");
        //创建HashSet集合
        HashSet<Integer> set=new HashSet<Integer>();
        //向集合中添加数据
        set.add(123);
        set.add(567);
        set.add(7843);
        set.add(45);
        //调用自定义函数分别遍历上述两个集合
        printCollection(list);
        printCollection(set);
    }
    /*
     * 通过观察发现以下的两个函数以函数重载的形式存在,几乎一模一样,还有泛型的数据类型不一致,
     * 还有集合的类型不一致,一个是ArrayList,另一个是HashSet,
     * 所以代码冗余,我们可以简化代码的开发,我们可以只定义一个函数来遍历集合
     * 既然两个集合不一样,我们这里可以书写集合的父类或者父接口Collection
     * 由于集合中保存的数据类型也不一致,这里也没法写,写String 不行 Integer 也不可以
     * 说明:这里书写Object不可以,因为泛型格式要求两侧必须类型一致,这里不一致了,会报错
     * Collection<Object> coll=new ArrayList<String>()
     * 所以这里我们只能借助泛型中通配符可以实现 : ? 
     * ? 表示泛型的通配符,表示集合中的任意数据类型,传递过来的表示什么数据类型,?就表示什么数据类型
     */
    public static void printCollection(Collection<?> coll)
    {
        //迭代集合
        for (Iterator<?> it = coll.iterator(); it.hasNext();) {
            System.out.println(it.next());
        }
    }
    /*//自定义函数遍历ArrayList函数
    public static void printCollection(ArrayList<String> list) {
        //迭代集合
        for (Iterator<String> it = list.iterator(); it.hasNext();) {
            String str =  it.next();
            System.out.println(str);
        }
    }
    //自定义函数遍历HashSet函数
    public static void printCollection(HashSet<Integer> set) {
        for (Iterator<Integer> it = set.iterator(); it.hasNext();) {
            Integer i=  it.next();
            System.out.println(i);
        }
    }*/
}

注意:泛型的通配符虽然可以简化代码的开发,但是在开发中要慎用,因为如果使用通配符,那么函数就可以接收任意的数据类型,这样会导致代码的不安全。

有的人会有疑问,我就是一个迭代遍历有什么不安全的,注意啦,我们在开发中可不是只有遍历这么简单,对接收的数据有可能进行其他的相关操作(增删改),结果就会导致对任何数据都可以操作,这样才会导致不安全。所以我们如果要想使用泛型的通配符那么必须得使用通配符的限定。目的是考虑数据的安全性。

1.6、泛型限定(了解,能看懂即可)

泛型的限定:包括上限限定和下限限定。
上限限定:在定义泛型的通配符的时候,如果需要对这个?号接收的类型进行限定,并且只知道具体的父类是什么,而所有的子类都需要接收,这时就使用泛型的上限限定。
上限限定的格式:
? extends 父类类型或父接口类型
例如:? extends Person :?代表的是一种类型,当前这个类型可以是Person本身,也可以是Person的子类。
下限限定:在定义泛型的通配符的时候,如果需要对这个?号接收的类型进行限定,并且只知道当前的某个子类,
而泛型需要接收的是当前的这个子类,或者子类的父类类型。可以使用泛型的下限限定
格式:? super 子类类型或实现类类型
例如: ? super Student :?代表当前的类型可以是Student类型,也可以是Student的父类类型。
但不能是Student 的子类,或者Student的兄弟。

2、Map集合

2.1、Map集合介绍

2.1.1 为什么要学习Map集合

Java中集合的关系图如下图所示:

12.png

说明:Collection接口下的所有集合中保存的对象都是孤立的。对象和对象之间并没有任何关系存在。
在生活中对象和对象之间必然会一定的联系存在。而我们学习的Collection接口下的所有子接口或者实现类(集合)它们中的确可以保存对象,但是没有办法维护这些对象之间的关系。
而学习的Map集合,它也是存放对象的,但是可以对其中的对象进行关系的维护。

Map引入的图解:
需求:定义一个集合存储班级学生的学生姓名。

13.png

补充:把Collection集合称为单列集合。Map被称为双列集合。

2.1.2 Map集合的特点

14.png

特点:
1)Map集合可以一次性存储两个对象;
2)在Map集合中保存的key和value这样的具备一定对应关系的一组(一对)数据,Map集合中存储的是两个对象的对应关系(映射关系)。[ key-value映射关系 ];
3)Map集合中的key必须保证唯一(存储的key元素不能重复,value可以重复,但是一个key只能对应一个value);
4)Map集合中的key和value属于一一对应关系(一个key只能对应一个value)

15.png

2.1.3 Map和Collection的区别

区别:
1)Map中键唯一,值没有要求。Collection中只有Set体系要求元素唯一;
2)Map的数据结构针对键而言,Collection的数据结构针对元素而言;
3)Map是双列集合顶级接口,Collection是单列集合顶级接口,他们两个分别是两种集合的顶级接口,之间没有必然的联系;

16.png

说明:
Collection集合中只能存储一个对象元素,称为单列集合。
Map集合中其实存储的是两个单列集合,称为双列集合。

2.2、Map集合中的方法(掌握)

1)增加元素:

17.png

在使用put存储一对元素(key-value)对象时,会先拿key去判断Map集合中是否已经存在。
如果Map集合中没有相同的key存在:就把key-value存储到Map集合中,并返回null值。
如果Map集合中有相同的key存在:会把之前存储的value对象覆盖。并返回之前的value对象(旧value对象)。
这里可以理解为修改value,但是不能修改key。

注意:由于Map是接口,不能创建对象,只能使用Map下面的子类HashMap或者TreeMap创建对象。

分析和步骤:
1)创建集合HashMap<String,String>集合对象map,返回值类型是Map<String,String>;
2)使用map对象调用put函数向集合中添加学生的学号作为key,姓名作为value;
3)输出对象map;

package cn.xueximap.function.demo;
import java.util.HashMap;
import java.util.Map;
/*
 * Map中的put函数的演示
 *  V put(K key, V value) 
          将指定的值与此映射中的指定键关联(可选操作)。 
 */
public class MapPutDemo {
    public static void main(String[] args) {
        // 创建集合对象
        Map<String, String> map = new HashMap<String,String>();
        //给集合中添加数据
        map.put("001","张三");
        map.put("002","李四");
        map.put("003","王二");
        map.put("004","麻子");
        map.put("005","王五");
        map.put("006","麻子");
        String name = map.put("006","老麻子");
        //输出集合中的数据
        System.out.println(map);
        System.out.println(name);
    }
}

2)删除元素:


18.png

清空集合。

19.png

remove是根据key删除当前key对应的value这一组数据:
如果Map集合中有与指定的key相同的元素存在:则删除一对key-value对象,而不是只删除value,并返回删除这组的value值;
如果Map集合中没有与指定的key相同的元素存在:没有删除操作,返回null;

问题:为什么不可以通过value来删除一组数据?
因为key是唯一的,而value可以有多个,所以不能通过value来删除一组数据,只能通过key值进行删除。

分析和步骤:
1)创建集合HashMap<String,String>集合对象map,返回值类型是Map<String,String>;
2)使用map对象调用put函数向集合中添加学生的学号作为key,姓名作为value;
3)使用map对象调用remove函数根据键key=004进行删除键值对,并使用String类型接收返回值value
4)输出对象map和删除的返回值value;
5)使用对象map调用clear()函数清除Map集合中的元素数据;

package cn.xuexi.map.function.demo;
import java.util.HashMap;
import java.util.Map;
/*
 * Map中的remove函数的演示
 *   V remove(Object key) 
          如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。 
 */
public class MapRemoveDemo {
    public static void main(String[] args) {
        // 创建集合对象
        Map<String, String> map = new HashMap<String,String>();
        //给集合中添加数据
        map.put("001","张三");
        map.put("002","李四");
        map.put("003","王二");
        map.put("004","麻子");
        map.put("005","王五");
        map.put("006","麻子");
        //调用remove函数删除key为004的键值对组合
        String value = map.remove("004");//返回删除key为004的value值麻子
        //输出集合中的数据
        System.out.println(map);
        System.out.println(value);
        //调用clear()函数清空map集合
        //注意清空指的是将map集合中的所有元素清空,集合还在,还可以使用集合对象向集合中添加数据
        map.clear();
        System.out.println(map);
        map.put("009", "黑旋风");
        System.out.println(map);
    }
}

3)改变集合中的元素:使用put函数就可以根据集合的key改变集合中的value值。
4)获取集合中的元素:

20.png

根据指定的key元素对象,去 Map集合获取相应的value对象。
如果Map集合中没有指定的key元素存在,则返回null。

注意:获取也只能根据key获取value,不能通过value获取key。

分析和步骤:
1)创建集合HashMap<String,String>集合对象map,返回值类型是Map<String,String>;
2)使用map对象调用put函数向集合中添加学生的学号作为key,姓名作为value;
3)使用map对象调用get函数根据键key=003获取value值,并使用String类型接收返回值value;
4)使用map对象调用get函数根据键key=007获取value值,并使用String类型接收返回值value1;
5)输出对象map和获得的value、value1;

package cn.xueximap.function.demo;
import java.util.HashMap;
import java.util.Map;
/*
 * Map中的get函数的演示
 *   V get(Object key) 
          返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。 
 */
public class MapGetDemo {
    public static void main(String[] args) {
        // 创建集合对象
        Map<String, String> map = new HashMap<String,String>();
        //给集合中添加数据
        map.put("001","张三");
        map.put("002","李四");
        map.put("003","王二");
        map.put("004","麻子");
        map.put("005","王五");
        map.put("006","麻子");
        //通过集合对象调用get函数根据key获取value值
        String value = map.get("003");
        String value1 = map.get("007");
        //输出集合中的数据
        System.out.println(map);
        System.out.println(value);
        //由于集合中没有key为007的数据,所以返回null
        System.out.println(value1);
    }
}

5)判断方法:

21.png

boolean containsKey(Object key) 判断Map集合中是否包含指定的key元素存在,包含返回true,否则返回false;
boolean containsValue(Object value) 判断Map集合中是否包含指定的value元素存在,包含返回true,否则返回false;

22.png

判断集合是否为空,集合中没有元素返回true,有元素返回false;

分析和步骤:
1)创建集合HashMap<String,String>集合对象map,返回值类型是Map<String,String>;
2)使用map对象调用put函数向集合中添加学生的学号作为key,姓名作为value;
3)使用map对象调用containsKey()函数根据键key=002来判断map是否含有002这个键,并使用boolean类型接收返回值boo,并输出boo;
4)使用map对象调用containsValue()函数根据值value=”田七”来判断map是否含有”田七”这个值,并使用boolean类型接收返回值boo1,并输出boo1;
5)map调用clear()函数清空集合;
6)map调用isEmpty()函数判断集合是否为空,为空返回true,否则返回false;

package cn.xuexi.map.function.demo;
import java.util.HashMap;
import java.util.Map;
/*
 * Map中的判断函数讲解
 */
public class MapBooleanDemo {
    public static void main(String[] args) {
        // 创建集合对象
        Map<String, String> map = new HashMap<String,String>();
        //给集合中添加数据
        map.put("001","张三");
        map.put("002","李四");
        map.put("003","王二");
        map.put("004","麻子");
        map.put("005","王五");
        map.put("006","麻子");
        //判断集合中是否包含指定的key值
        boolean boo = map.containsKey("002");
        System.out.println(boo);
        //判断集合中是否包含指定的value值
        boolean boo1 = map.containsValue("田七");
        System.out.println(boo1);
        //清除集合
        map.clear();
        //判断集合是否为空 集合中没有元素返回true,由元素返回false
        boolean boo2 = map.isEmpty();
        System.out.println(boo2);
    }
}

6)其他方法:


23.png

size() 获取Map集合中存储的key-value对应关系的个数。获取集合长度。


24.png

Set<K> keySet() 返回此映射中包含的所有键的 Set集合

25.png

Collection<V> values() 返回此映射中包含的所有值的 Collection 集合

分析和步骤:
1)创建集合HashMap<String,String>集合对象map,返回值类型是Map<String,String>;
2)使用map对象调用put函数向集合中添加学生的学号作为key,姓名作为value;
3)使用map对象调用size()函数获取map集合中键值对个数,并输出结果;
4)使用map对象调用values()函数获取map集合中value值的个数,使用Collection集合进行接收并遍历输出结果;
5)使用map对象调用keySet()函数获取map集合中key值的个数,使用Set集合进行接收并遍历输出结果;

package cn.xuexi.map.function.demo;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/*
 * Map中的其他函数讲解
 */
public class MapOtherFunctionDemo {
    public static void main(String[] args) {
        // 创建集合对象
        Map<String, String> map = new HashMap<String,String>();
        //给集合中添加数据
        map.put("001","张三");
        map.put("002","李四");
        map.put("003","王二");
        map.put("004","麻子");
        map.put("005","王五");
        map.put("006","麻子");
        //返回map集合中的键值对的个数
        int size = map.size();
        System.out.println(size);
        //获取map集合中value值
        Collection<String> values = map.values();
        //遍历集合
        for (String value : values) {
            System.out.println(value);
        }
        //获取map集合中的所有键集合
        Set<String> keys = map.keySet();
        //遍历set集合
        for (String key : keys) {
            System.out.println(key);
        }
    }
}

2.3、Map集合的遍历

通过前面的学习我们已经知道,针对Collection集合,它的遍历可以使用Iterator迭代器进行遍历。
但是Map集合不能直接使用Iterator迭代器来遍历,因为在Map集合根本就没有提供获得迭代器对象的函数。
既然不能通过Iterator迭代器直接来遍历,那是否可以间接使用Iterator迭代器来遍历Map集合呢?
之前讲Map集合时,Map集合是用来存储一对key-value两个对象,既然存储的是两个对象,那么能不能只获取其中一个所有的对象?
可以。Map集合属于双列集合,里面有两个单列集合。由于Map集合中的key是唯一的,所以我们我可以先获取Map集合中的key,然后根据key值来获取Map集合中的value值。

问题1:Map中的key对象,应该使用哪个集合存储?
key必须保证唯一性(不能重复),使用Set集合存储。

问题2:如何将Map中的key存储到Set集合呢?
可以使用Map中的keySet方法:获取Map集合中所有的key对象并存储到Set集合中。

26.png

这样通过上述操作就可以将Map集合中的key存放到Collection集合下面的set中,然后使用Iterator遍历Map集合中的key所在的Set集合,进而可以使用

Map集合中的
27.png

通过key间接的获取每个value。

注意:Map接口中提供了2个方法可以获取到Map中的key 或者key和value关系对象

keySet():Map中的key存储到Set集合中。

entrySet():Map中的key和value关系对象存储到Set集合中。

注:由于Map中key是唯一的,不能重复的,所以Map集合中键值对整体也是不能重复的,但是单独value值是可以重复的。

2.3.1、使用keySet方法来遍历Map集合(根据键来遍历)必须掌握

使用keySet方法来遍历集合的图解如下图所示:

28.png

]

代码如下所示:

需求:Map的遍历方式1:keySet遍历
向Map集合中存储几个key-value对象,即夫妻键值对。

分析:
A:找到所有丈夫的集合
B:让丈夫一个一个出来
C:让丈夫去找老婆

思路:
A:找到所有键的集合:keySet()
B:循环遍历,取出每个键
C:根据键找值:get(Object key)

分析和步骤:
1)创建Map集合对象map;
2)使用集合对象map调用put函数向集合中添加几个数据,丈夫作为key,妻子作为value;
3)使用集合对象map调用keySet函数获取Map集合中所有的key,把所有的key保存到Set集合中,并获取对象set;
4)使用迭代器Iterator来对Set集合中的key进行迭代,使用迭代器对象it调用next()函数来获取key值;
5)然后使用Map集合的对象map调用get()函数,迭代的key值作为参数获得key值对应的value,并输出打印key和value值;

package cn.xuexi.map.iterator.demo;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import java.util.Set;
/*
 *  使用keySet函数遍历Map集合
 */
public class MapKeySetDemo {
    public static void main(String[] args) {
        // 创建集合对象
        Map<String, String> map = new HashMap<String,String>();
        //向集合中添加数据
        map.put("李晨","范冰冰");
        map.put("邓超","孙俪");
        map.put("文章","马伊琍");
        map.put("汪峰","章子怡");
        /*
         * 通过Map的对象调用keySet函数获得Map中的key值并保存到Set集合
         * 所有的key键都保存到以下的set集合中了
         */
        Set<String> keys = map.keySet();
        //使用迭代器迭代Set集合
        for (Iterator<String> it = keys.iterator(); it.hasNext();) {
            //分别取出每个key值
            String key = it.next();
            //使用Map的对象调用get函数根据key获得value
            String value = map.get(key);
            //输出key和value
            System.out.println(key+"===="+value);
        }
    }
}

2.3.2、使用entrySet遍历(会使用即可,开发使用的较少)

在Map集合中提供的entrySet方法,它可以获取到当前Map集合中的key和value的对应关系对象(映射对象)。

在Map集合中把key和value看成一组数据,然后Java中把这一组数据又一次封装成了一个新的对象,这个对象就是key和value的对应关系对象。然后把这些对象保存到了Set集合中。Set集合中保存的是key和value对应关系对象。

说明:

Set集合中保存的是key和value对应关系对象属于Map.Entry类型。

29.png

注意:
通过查阅API得知Map.Entry类型表示在Map接口中还有一个内部接口Entry。
而在内部接口Entry中有如下方法:

30.png

1)getKey()方法表示可以通过Map.Entry类型的对象调用此函数来获得在Set集合中保存的是key和value对应关系对象中的key值;
2)getValue()方法表示可以通过Map.Entry类型的对象调用此函数来获得在Set集合中保存的是key和value对应关系对象中的value值;
3)setValue()方法表示可以通过Map.Entry类型的对象调用此函数来修改在Set集合中保存的是key和value对应关系对象中的value值;
使用Map集合中的entrySet()函数遍历集合的过程图解如下图所示:

31.png

代码实现如下所示:
需求:演示:Map的遍历方式2:entrySet遍历。
向Map集合中存储几个key-value对象,即夫妻键值对。

分析:
A:首先拿到所有夫妻的结婚证集合
B:取出每个结婚证
C:从结婚证中拿到丈夫和妻子

问题:结婚证是什么呢?
Map中存储的元素,其实是成对的键值对数据。结婚证就是 一个键值对 对象。每一个键值对对象中有一个键和一个值。

思路:
A:获取所有的键值对对象的集合
B:循环遍历,拿到每个键值对对象
C:从键值对对象中取出键和值

问题:如何获取键值对对象?键值对对象是个什么类型呢?
Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的 键值对对象 的 Set集合。
Map.Entry<K,V> 就是键值对对象
K getKey() 返回与此项对应的键。
V getValue() 返回与此项对应的值。

分析和步骤:
1)创建Map集合对象map;
2)使用集合对象map调用put函数向集合中添加几个数据,丈夫作为key,妻子作为value;
3)使用集合对象map调用entrySet函数获取Map集合中所有的key和value对应关系的对象,把所有的对应关系的对象保存到Set集合中,并获取对象keyValues;
4)使用迭代器Iterator来对Set集合中的的对应关系的对象keyValues进行迭代,使用迭代器对象it调用next()函数来获取单个独立的key-value对应关系对象keyValue;
5)然后使用keyValue对象调用Map.Entry中的getKey()和getValue()函数来获得每个对应关系对象中的key和value,最后输出key和value值;

package cn.xuexi.map.iterator.demo;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/*
 * 使用Map集合中的entrySet函数遍历集合
 */
public class MapEntrySetDemo {
    public static void main(String[] args) {
        // 创建集合对象
        Map<String, String> map = new HashMap<String,String>();
        //向集合中添加数据
        map.put("李晨","范冰冰");
        map.put("邓超","孙俪");
        map.put("文章","马伊琍");
        map.put("汪峰","章子怡");
        /*
         * 使用entrySet()函数获得键值对对应关系的对象
         * KeyValues表示键值的对应关系的对象
         */
        Set<Map.Entry<String, String>> keyValues = map.entrySet();
        //迭代Set集合 
        /*
         * Iterator<Map.Entry<String, String>> 表示每次迭代出来的是Map.Entry类型,
         * 并且对应关系对象中的key和value都是String类型
         */
        /*for (Iterator<Map.Entry<String, String>> it = keyValues.iterator(); it.hasNext();) {
            Map.Entry<String, String> keyValue =  it.next();
            //keyValue表示每个独立的key-value对应关系的对象,现在分别取出对应关系中的key和value
            String key = keyValue.getKey();
            String value = keyValue.getValue();
            //输出key和value
            System.out.println(key+"---"+value);
        }*/
        //使用foreach循环遍历Set集合
        for (Map.Entry<String, String> keyValue : keyValues) {
            /*
             * keyValue表示单个独立的映射关系对象,现在分别取出对应关系中的key和value
             */
            String key = keyValue.getKey();
            String value = keyValue.getValue();
            //输出key和value
            System.out.println(key+"。。。。"+value);
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容