Java学习笔记-集合


在这里插入图片描述

Collection集合

Collection接口

集合与数组

使用数组的局限性

如果要存放多个对象,可以使用数组,但是数组有局限性,比如 声明长度是10的数组,不用的数组就浪费了超过10的个数,又放不下。

ArrayList存放对象

为了解决数组的局限性,引入容器类的概念。 最常见的容器类就是ArrayList

  • 容器的容量"capacity"会随着对象的增加,自动增长

  • 只需要不断往容器里增加元素即可,不用担心会出现数组的边界问题。

集合框架

List

特点:有索引、可以存储重复元素,存储有序。

  • Vector集合实现类。被ArrayList取代
  • ArrayList集合实现类:底层时数组实现的,查询快、增删慢
  • LinkedList集合实现类:底层时链表实现的,查询慢、增删快
Set

特点:无索引、不可存储重复元素、存储无序

  • HashSet集合实现类:底层是哈希表+(红黑树)实现的,无索引、不可存储重复元素、存储无序
  • LinkedHashSet集合实现类:底层是哈希表+链表实现的,无索引、不可存储重复元素、存取无序
  • TreeSet集合实现类:底层是二叉树实现的,一般用于排序

常用方法

  • boolean add(E e);:向集合中添加元素
  • boolean remove(E e);:删除集合中的某个元素
  • void clear();:清空集合中所有元素
  • boolean contains(E e);:判断集合中是否包含某个元素
  • boolean isEmpty();:判断集合中是否为空
  • int size();:获取集合长度
  • Object[] toArray(); :将集合转成一个数组

Iterator迭代器

Iterator接口

在程序开发过程中,经常需要遍历集合中的所有元素,

迭代:即Collection集合元素的通用获取方式,在去元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续判断,如果还有就再取出来。一直把集合中的多有元素全部取出,这种方式就叫迭代

常用方法:

  • public boolean hasNext(); 如果集合中下一个仍有元素,返回True
  • public E next(); 返回迭代的下一个元素

使用步骤

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

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

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

示例

public class Demo01Iterator {
    public static void main(String[] args) {
        // 创建一个集合对象
        Collection<String> coll = new ArrayList<>();
        // 往集合中添加元素
        coll.add("张三");
        coll.add("李四");
        coll.add("王五");
        coll.add("赵六");
        /*
        * 1 使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
        * 注意:
        *       Iterator<E> 接口也是有泛型的,迭代器的泛型跟着集合走,集合是什么泛型,迭代器就是什么泛型
        * */
        // 多态方式创建一个集合的迭代器实现类对象it
        Iterator<String> it = coll.iterator();
        // 2 使用Iterator接口中的hasNext判断还有没有下一个元素
        boolean b = it.hasNext();
        System.out.println(b); // true
        // 3 使用Iterator接口中的方法next取出集合中的下一个元素
        String s = it.next();
        System.out.println(s);
        System.out.println("=========循环输出剩余元素:========");
        while(it.hasNext()){
            s = it.next();
            System.out.println(s);
        }
    }
}

增强for循环

jdk1.5开始有的高级for循环,专门来遍历数组和集合的,他的内部原理就是个Iterator迭代器,所以遍历过程中,不能对集合中的元素进行增删操作。

格式:

for(元素的数据类型  变量 : Collection集合or数组){
    // 操作规则
}

练习1 遍历数组

public class ForeachDemo1 {
    public static void main(String[] args) {
        int[] arr = {3,2,4,5,1};
        // 使用增强for循环遍历数组
        for (int a : arr) {
            System.out.println(a);
        }
    }
}

练习2 遍历集合

public class ForeachDemo2 {
    public static void main(String[] args) {
        // 创建一个集合对象
        Collection<String> coll = new ArrayList<>();
        // 往集合中添加元素
        coll.add("张三");
        coll.add("李四");
        coll.add("王五");
        coll.add("赵六");
        // 使用增强for循环遍历数组
        for (String e : coll) {
            System.out.println(e);
        }
    }
}

泛型

可以看作一种未知的类型,当我们不知道使用什么类型的时候,可以使用泛型

在这里插入图片描述

不指定泛型容易报错

泛型的定义和使用

定义和使用含有泛型的类
/*
	定义一个含有泛型的类
*/
public class GenericClass<E> {
    public E getName() {
        return name;
    }

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

    private E name;
}

在main中调用创建和使用

public class Demo01Main {
    public static void main(String[] args) {
        // 不写泛型默认是Object类型
        GenericClass gc = new GenericClass();
        gc.setName("啊哈");
        Object obj = gc.getName();
        
        // 创建GenericClass对象,泛型使用String类型
        GenericClass<String> gc2 = new GenericClass<>();
        gc2.setName("啊哈");
        String name = gc2.getName();
        System.out.println(name);
    }
}
含有泛型的方法

定义格式:

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

示例:

/*
*   定义含有泛型的方法:泛型定义在方法的修饰符和返回值类型之间
*   格式:
*       修饰符 <泛型> 返回值类型 方法名(参数){  }
*   含有泛型的方法,在调用方法的时候确定泛型的数据类型
*   传递什么类型的参数,泛型就是什么类型
* */
public class GenericMethod {
    //定义一个含有泛型的普通方法
    public <M> void method01(M m){
        System.out.println(m);
    }

    //定义一个含有泛型的静态方法
    public static  <S> void method02(S s){
        System.out.println(s);
    }
}
public class GenericMethodMain {
    public static void main(String[] args) {
        // 创建GenericMethod的对象
        GenericMethod gm = new GenericMethod();
        // 调用含有泛型方法的method01
        gm.method01("asd");
        gm.method01(2);
        gm.method01(true);

        gm.method02("静态方法不建议创建对象使用");
        // 静态方法,通过 【类名.静态方法名(参数)】 可直接使用
        GenericMethod.method02("通过类名调用静态方法");
        GenericMethod.method02(3);
        GenericMethod.method02(false);
    }
}
含有泛型的接口

主要有两种

  • 首先定义一个含有泛型的接口

    // 定义一个含有泛型的接口
    public interface GenericInterface<I> {
        public abstract void method(I i);	// 一个抽象方法
    }
    
  • 定义接口的实现类,有两种方式:

    第一种:定义接口实现类接口的实现类,在实现类中指定接口的泛型,即在实现类中先确定好接口的泛型,如字符串类型

    // GenericInterface接口的实现类,重写接口中方法,泛型在实现类中指定
    public class GenericInterfaceImpl_1 implements GenericInterface<String> {
    
        @Override
        public void method(String s) {
            System.out.println(s);
        }
    }
    

    第二种:定义接口实现类接口的实现类,在实现类中也先不确定接口的泛型,在创建实现类对象的时候再指定。灵活性更好

    // GenericInterface接口的实现类,重写接口中方法
    public class GenericInterfaceImpl_2<I> implements GenericInterface<I> {
    
        @Override
        public void method(I i) {
            System.out.println(i);
        }
    }
    
  • main中使用和调用

    public class GenericInterfaceMain {
        public static void main(String[] args) {
            // 创建第一种GenericInterfaceImpl_1对象
            GenericInterfaceImpl_1 gi1 = new GenericInterfaceImpl();
            gi1.method("字符串");	// 因为实现类中指定了为String类型,所以此次参数必须是String类型的
            
            // 第二种
            GenericInterfaceImpl_2<Integer> gi2_1 = new GenericInterfaceImpl<>();	// 指定泛型为Integer
            gi2_1.method("haha");
            GenericInterfaceImpl_2<String> gi2_2 = new GenericInterfaceImpl<>();	// 指定泛型为String
            gi2_2.method(23);
            GenericInterfaceImpl_2<Boolean> gi2_3 = new GenericInterfaceImpl<>();	// 指定泛型为Boolean
            gi2_3.method(true);
        }
    }
    

泛型通配符

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

通配符的基本使用

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

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

示例:

public static void main(String[] args){
    Collection<Integer> list1 = new ArrayList<Integer>();
    getElement(list1);
    Collection<String> list2 = new ArrayList<String>();
    getElement(list2);
}
public static void getElement(Collection<?> coll){ }	// ? 代表可以接受任意类型
通配符的高级使用----受限泛型
  • 泛型的上线限定:? extends E 代表使用泛型只能是E类型及其子类
  • 泛型的下线限定:? super E 代表使用泛型只能是E类型及其父类
public static void getElement1(Collection<? extends Number> coll){ }	// 泛型的上线限定:此时的泛型?,必须是Number类型或是Number类型的子类
public static void getElement2(Collection<? super Number> coll){ }	// 泛型的上线限定:此时的泛型?,必须是Number类型或是Number类型的父类

集合案例的综合案例-斗地主

案例介绍

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

具体规则:使用54张牌,打乱顺序,三人参与游戏,三人交替摸牌,没人17张牌,最后三张作为底牌

案例分析

  • 准备牌:

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

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

    牌有Collections类的shuffle方法进行随机打乱排序。

  • 发牌

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

  • 看牌

    直接打印每个集合

代码示例

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

public class DouDiZhu {
    public static void main(String[] args) {
        // 1 准备牌
        // 	定义一个存储54张牌的ArrayList集合,泛型使用String
        ArrayList<String> poker = new ArrayList<>();
        String[] colors = {"♥","♣","♦","♠"};
        String[] numbers = {"2","A","K","Q","J","10","9","8","7","6","5","4","3"};
        // 	先把大王小王存储到poker中
        poker.add("大王");
        poker.add("小王");
        // 	循环嵌套遍历两个数组,组装52张牌
        for (String number : numbers) {
            for (String color : colors) {
                poker.add(color + number);
            }
        }
        // 2 洗牌
        //   使用集合工具类 Collections中的方法
        //   static void shuffle(List<?> list)  使用默认随机源对指定列表进行置换
        Collections.shuffle(poker);
        // 3 洗牌
        //      定义4个集合,存储玩家的牌和底牌
        ArrayList<String> player1 = new ArrayList<>();
        ArrayList<String> player2 = new ArrayList<>();
        ArrayList<String> player3 = new ArrayList<>();
        ArrayList<String> dipai = new ArrayList<>();
        /*
        *   遍历poker集合,获取每张牌
        *   使用poker集合的索引%3给3个玩家轮流发牌
        *   剩余3张给底牌
        *   注意:
        *       先判断底牌(i>=52),否则牌就没了
        * */
        for (int i = 0; i < poker.size(); i++) {
            String p = poker.get(i);
            if(i>=51){
                dipai.add(p);
            }else if(i%3==0){
                player1.add(p);
            }else if(i%3==1){
                player2.add(p);
            }else if(i%3==2){
                player3.add(p);
            }
        }

        // 4 看牌
        System.out.println("玩家1的牌:" + player1);
        System.out.println("玩家2的牌:" + player2);
        System.out.println("玩家3的牌:" + player3);
        System.out.println("底牌:" + dipai);
    }
}

常见的Collection集合

数据存储的常用结构有:栈、队列、数组、链表(单链表、双链表、环形链表)、树(二叉树、B+树、红黑树…)、堆、散列表、图

List接口

java.util.List接口

List接口的特点:

  • 有序的集合 ,存储元素和取出元素的顺序是一致的
  • 有索引,包含了一些带索引的方法
  • 允许存储重复的元素

List接口常用方法

  • public void add(int index, E element):将指定元素element插入指定index位置。
  • public E get(int index):返回集合中指定位置上的元素
  • public E remove(int index):移除列表指定位置上的元素,并将被移除的元素返回
  • public E set(int index, E element):修改集合指定位置index上的值为element。返回更新前的元素

代码示例

public class ListDemo01 {
    public static void main(String[] args) {
        // 多态的方式创建一个List集合对象
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        // 在 张三 和 李四 之间加一个
        list.add(1,"新添加");
        String g = list.get(2);
        String rm = list.remove(2); // 删除索引为2的元素,并将这个元素保存至rm变量中
        String a = list.set(3,"小二");    // 将索引为3个元素修改为“小二”,原元素被返回给a

        // List集合遍历的3中方式
        //  1 索引遍历
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
        }
        //  2 使用迭代器访问
        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            String s = it.next();
        }
        //  3 foreach 遍历
        for (String s : list) {
            System.out.println(s);
        }
    }
}

List接口的子类

ArrayList集合

java.util.ArrayList集合数据存储的结构是数组结构,元素增删慢,查找快

LinkedList集合

java.util.LinkedList集合数据存储的结构是链表结构,元素查询慢、增删快。是一个双向链表

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

常用方法
  • public void addFirst(E e):将指定元素添加在此列表的开头
  • public void addLast(E e):将指定元素添加在此列表的结尾。
  • public E getFirst():返回列表的第一个元素。// 如果列表为空时调用此方法会报错
  • public E getLast():返回列表的最后一个元素。// 如果列表为空时调用此方法会报错
  • public E removeFirst():移除并返回列表的第一个元素。
  • public E removeLast():移除并返回列表的最后一个元素
  • public E pop():从此列表所表示的堆栈处弹出一个元素。 // 等价于removeFirst(E e) 方法
  • public void push(E e):将元素推入此列表所表示的堆栈。
  • public boolean isEmpty():判断列表是否为空。
public static void main(String[] args) {
    LinkedList<String> linked = new LinkedList<>();
    linked.add("a");
    linked.add("b");
    linked.add("c");

    linked.addFirst("dd");  // 等效于push(E e) 方法,在开头处添加元素
    linked.addLast("ee");  // 等效于add(E e) 方法,在末尾处添加元素
    System.out.println(linked); // [dd, a, b, c, ee]

    if(!linked.isEmpty()){
        // 如果列表为空时,调用下面几个方法都会报错
        String first = linked.getFirst(); 
        String last = linked.getLast(); 
        String rm_first = linked.removeFirst(); // 等价于 pop(E e) 方法
        String rm_last = linked.removeLast();
    }
    linked.clear(); // 清空列表
}

Set接口

java.util.Set

一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1e2,并且最多包含一个 null 元素。

HashSet集合

java.util.HashSet集合特点:

  • 不允许存储重复的元素
  • 没有索引,没有带索引的方法,也不能使用普通的for循环遍历,可以使用迭代器增强for遍历
  • 是一个无序的集合,存储数据和取出数据的顺序可能不一致
  • 底层是一个哈希表结构(查询数据非常快)底层是一个哈希表(数组+链表/红黑树)
public static void main(String[] args) {
    Set<Integer> set = new HashSet<>();
    set.add(1);
    set.add(2);
    set.add(3);
    // 使用迭代器遍历
    Iterator<Integer> it = set.iterator();
    while(it.hasNext()){
        Integer n = it.next();
        System.out.println(n);
    }
    // 使用增强for循环遍历集合
    for (Integer i : set) {
        System.out.println(i);
    }
}
HashSet集合存储数据的结构(哈希表)

先了解一个名词:哈希值

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

在Object类中有一个方法,可以获取对象的哈希值:int hashCode() 返回该对象的哈希值

HashSe存储元素不重复的原理

Set集合在调用add()方法的时候,add方法会调用元素的hashCode方法和equals方法,判断元素是否重复。

举例说明:

  • Set.add(s1):(假设s1=“abc”)

    1. add方法会先调用hashCode方法,计算字符串 “abc” 的哈希值,如为:96354,
    2. 在集合中找有没有96354这个哈希值,发现没有
    3. 就把 s1 存储到集合中。
  • Set.add(s2):(假设s2=“abc”)

    1. add方法会先调用hashCode方法,计算字符串 “abc” 的哈希值,如为:96354,
    2. 在集合中找有没有96354这个哈希值,发现有(哈希冲突)
    3. s2 会再调用equals方法和哈希值相同的元素进行比较,s2.equals(s1),返回true
    4. 两个元组的哈希值相同,equals方法返回true,认定两个元素相同。
    5. 就不会把s2存储到集合中。
HashSet存储自定义类型元素

例如自定义Person类,对于同名同年龄的人,视为同一个人,只能存储一次。在Person类中对hashCode和equals方法

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

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

LinkedHashSet集合

java.util.LinkedHashSet

public class LinkedHashSet<E> extends HashSet<E>implements Set<E>, Cloneable, Serializable

继承了HashSet

LinkedHashSet的特点
  • 底层是一个哈希表(数组+链表/红黑树)+ 链表。多的这个链表用于记录元素的存储顺序,保证元素有序(什么顺序存储,就是什么顺序取出)
public static void main(String[] args) {
    HashSet<String> set = new HashSet<>();
    set.add("aaa");
    set.add("sss");
    set.add("ddd");
    set.add("fff");
    System.out.println(set);    // 结果可能是:[aaa, ddd, sss, fff]
    LinkedHashSet<String> linked = new LinkedHashSet<>();
    linked.add("aaa");
    linked.add("sss");
    linked.add("ddd");
    linked.add("fff");
    System.out.println(linked); // 结果是:[aaa, sss, ddd, fff]
}

Collections

java.util.Collections是集合工具类,用于对集合进行操作,部分方法如下:

  • public static boolean addAll(Collection<T> c, T... elements):往集合中添加一些元素
  • public static void shuffle(List<?> list):打乱孙旭,打乱集合顺序。
  • public static void sort(List<T> list):将集合中的元素按照默认规则排序。
  • public static void sort(List<T> list, Comparator<? super T> ):将集合中的元素按照指定规则排序。

代码示例

public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<>();
    /*
        // 逐个添加
        list.add(3);
        list.add(2);
        list.add(5);
        list.add(1);
        */
    // 一次性添加
    Collections.addAll(list, 3, 2, 5, 1);
    System.out.println(list);    // [3, 2, 5, 1]
    // 将list集合打乱
    Collections.shuffle(list);
    System.out.println(list);   // 可能是:[3, 5, 1, 2]
    // 对集合排序
    Collections.sort(list);
    System.out.println(list);   // [1, 2, 3, 5]
    
    /***************************************************
    	如何对自定义类进行排序?
    		法1:让自定义类实现Comparable接口,并重写compareTo方法
    		法2:Comparator:调这个排序方法:public static <T> void sort(List<T> list, Comparator<? super T> )
    		
    	用Comparable和用Comparator的区别
    		用Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法。
    		用Comparator:相当于找个第三方的裁判,比较两个
    */    
    ArrayList<Person> list2 = new ArrayList<>();
    list2.add(new Person("张三",21));
    list2.add(new Person("李四",25));
    list2.add(new Person("王五",18));
    System.out.println(list2);	// [Person{name='张三', age=21}, Person{name='李四', age=25}, Person{name='王五', age=18}]
    // ************
    // 法1:让自定义类实现Comparable接口,并重写compareTo方法
    // 按照Person类中重写的compareTo方法规则进行排序
    // Collections.sort(list2);
    // System.out.println(list2);	// [Person{name='李四', age=25}, Person{name='张三', age=21},Person{name='王五', age=18}]
    // *************
    // 法2:用Comparator:调这个排序方法:public static <T> void sort(List<T> list, Comparator<? super T> )
    // Comparator的排序规则:
   	//		o1 - o2 : 升序,反之降序
    // 【先将Person类中实现Comparable接口及重写的compareTo方法注销掉】
    Collections.sort(list2, new Comparator<Person>() {
        @Override
        public int compare(Person o1, Person o2) {
            int result = o1.getAge() - o2.getAge(); // 年龄升序,反之降序
            // 如果两人年龄相同,再使用姓名的第一个字比较
            if(result==0){
                result = o1.getName().charAt(0) - o2.getName().charAt(0);
            }
            return result;
        }
    });
    System.out.println(list2);	// [Person{name='王五', age=18}, Person{name='张三', age=21}, Person{name='李四', age=25}]
}

注意在Person类中要

  • 让Person实现Comparable接口:public class Person implements Comparable<Person> { }
  • Person类要重写compareTo方法。
public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person() {
    }

    public Person(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 "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    // 重写排序规则,比如按照年龄排序
    @Override
    public int compareTo(Person o) {
        // return 0;   // 认为元素是相同的
        // 自定义比较的规则,比较两个人的年龄(this 和 参数Person)
        // return this.age - o.age;  // 按年龄升序
		return o.age - this.age;  // 按年龄降序
    }
}

可变参数

需求:方法需要接收的参数不知道具体是多少,比如计算参数列表的和,有时候传过来的是两个数,有时候传过来的是5个数。

格式:修饰符 返回值类型 方法名(数据类型...变量名){}

public static void main(String[] args) {
    int s1 = mySum(3,4,6);  // 13
    int s2 = mySum(5,6);    // 11
    int s3 = mySum(1,2,3,4,5,6);    // 21
}
public static int mySum(int... arr){
    int sum = 0;
    for (int i : arr) {
        sum += i;
    }
    return sum;
}

注意事项:

  • 一个方法的参数列表,只能有一个可变参数。

  • 如果方法的参数有多个,那么可变参数必须卸载参数列表的末尾。

    public static int mySum(String arg1, int... arr){}

Map集合

java.util.Map。public interface Map<K,V>。将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。

Map集合的特点

  • Map集合是一个双列集合,一个集合包含两个值(一个key,一个value)
  • Map集合中的元素,key和value的数据类型可以相同也可以不同
  • Map集合中的元素,key是不允许重复的,value是可以重复的
  • Map集合中的元素,key和value是一一对应的。

Map常用子类(HashMap、LinkedHashMap)

通过Map接口描述,看到Map有多个子类,这里我们主要讲解常用的HashMap集合、LinkedHashMap集合。

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

Map中常用的方法

  • public V get(K key):获取map中key键所对应的value值。(有就返回value,没有就返回null)

  • public V put(K key, V value):把指定的键和值添加到Map集合中,并将K对应的旧值返回,若map原本中无K,则返回null(添加键值对的时候,集合中若不存在key,返回值V是null;存在key时,会使用新的value替换map中已有key的value,返回被替换的value值,即旧值。)

  • public V remove(Object key):把指定的键 所对应的键值对元素 在map中删除,返回被删除元素的值。(key若存在,返回被删除的值;key若不存在,返回null)

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

  • int size():返回此映射中的键-值映射关系数。

  • boolean isEmpty():判断map是否为空。

  • Set keySet() 返回map中所有键,存储在一个set集合中。

  • Set<Map.Entry<K, V>> entrySet():返回此Map中包含的映射关系的Set视图

public static void main(String[] args) {
    Map<String, String> map = new HashMap<>();
    String v1 = map.put("a", "A");
    System.out.println("v1:" + v1);
    String v2 = map.put("a", "AA");
    System.out.println("v2:" + v2);
    map.put("b", "B");
    map.put("c", "C");
    System.out.println(map);

    // 删除:remove(K);
    String v3 = map.remove("a");
    System.out.println("v3:" + v3); // v3:AA

    // 遍历map
    // 1 使用Map集合中的keySet()方法,把Map集合中所有的key取出来,存储到一个Set集合中。
    Set<String> set = map.keySet();
    // 2 使用迭代器遍历Set,从而获取map集合中的每个key,进而获取每个value
    Iterator<String> it = set.iterator();
    while(it.hasNext()){
        String k = it.next();
        // 3 通过Map集合中的法get(key),通过key得到value
        String value = map.get(k);
        System.out.println(k + "=" + value);
    }
    // 使用增强for循环
    for (String key : map.keySet()) {
        System.out.println(key + "=" + map.get(key));
    }
}

Map集合遍历的第二种方法,使用Entry对象遍历

  • 说明,在Map中,每个键值对组成了一个Map.Entry<key, value> 对象,有多少个键值对,就有多少个Entry对象,Entry对象有getKey()和getValue()两个方法。

  • 示例:

    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("a", 1);
        map.put("b", 2);
        map.put("c", 3);
        System.out.println(map);
        // 1 使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出,存入一个Set集合中。
        Set<Map.Entry<String, Integer>> set = map.entrySet();
        // 2 遍历set集合,获取每个Entry对象
        // 使用迭代器遍历Set集合
        Iterator<Map.Entry<String, Integer>> it = set.iterator();
        while(it.hasNext()){
            Map.Entry<String, Integer> entry = it.next();
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + "=" + value);
        }
        // 使用增强for循环
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + "=" + value);
        }
    }
    

HashMap存储自定义类型的键值

Map集合保证key是唯一的:

​ 所以,对于自定义类型,要保证key是唯一的,必须重写hashCode()和equals()方法

示例:

public static void main(String[] args) {
    show01();
    // 对Person类重写hashCode和equals()方法。
    show02();
}
private static void show01(){
    /*
    	key:String类型
    		String类已经重写过hashCode和equals方法,可以保证key唯一
    	value:Person类型
    		value可以重复,Person类可以不重写hashCode和equals方法。
    */
    HashMap<String, Person> map = new HashMap<>();
    map.put("工号001", new Person("张三", 21));
    map.put("工号002", new Person("李四", 18));
    map.put("工号001", new Person("王五", 24)); // 会把原来工号为001的张三覆盖掉
    Set<Map.Entry<String, Person>> set = map.entrySet();
    // 遍历set集合,获取每个Entry对象
    // 使用迭代器遍历Set集合
    Iterator<Map.Entry<String, Person>> it = set.iterator();
    while(it.hasNext()){
        Map.Entry<String, Person> entry = it.next();
        String key = entry.getKey();
        Person value = entry.getValue();
        System.out.println(key + "=" + value);
    }
}
private static void show02(){
    /*
    	key:Person类型
    		Person类必须重写hashCode和equals方法,以可以保证key唯一
    	value:String类型
    		可以重复
    */
    HashMap<Person, String> map = new HashMap<>();
    map.put(new Person("张三", 24), "米国");
    map.put(new Person("吉田", 18), "日本");
    map.put(new Person("张三", 24), "中国"); // 会把原来是米国的Mark覆盖掉
    // 使用增强for循环
    for (Map.Entry<Person, String> entry : map.entrySet()) {
        Person key_person = entry.getKey();
        String value_country = entry.getValue();
        System.out.println(key_person + "=" + value_country);
    }
}
public class Person {
    private String name;
    private Integer age;
    public Person() {
    }

    public Person(String name, Integer 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;
        Person person = (Person) o;
        return Objects.equals(name, person.name) &&
                Objects.equals(age, person.age);
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

小案例:计算一个字符串中每个字符出现的次数

   /*
   * 练习:
   *       计算要给字符串中每个字符出现的次数
   * 分析:
   *       1 使用Scanner获取用户输入的字符串
   *       2 创建Map集合,key是字符串中的字符,value是字符的个数
   *       3 遍历字符串,获取每个字符,
   *       4 使用获取到的字符,去map集合中判断key是否存在
   * */
   public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);
       System.out.println("请输入一个字符串:");
       String str = sc.next();

       HashMap<Character, Integer> map = new HashMap<>();
       for (char c : str.toCharArray()) {
           if(map.containsKey(c)){
               Integer value = map.get(c);
               map.put(c, value+1);
           }else{
               map.put(c, 1);
           }
       }
       for (Character key : map.keySet()) {
           Integer value = map.get(key);
           System.out.println(key + ":" + value);
       }
   }

JDK9对集合添加操作的优化(of方法)

在jdk9之前,对List、Set集合的添加是add()方法,对Map集合的添加是put()方法。每次只能添加一个元素

JDK9的新特性:

  • List接口、Set接口、Map接口:增加了一个静态方法of(),可以一次性添加多个元素。
  • static List of(E… elements)。使用前提:当集合中存储的元素的个数已经确定了,不改变时使用
  • 注意:
    • of() 方法只适用于List、Set、Map三个接口,不适用于接口实现类。
    • of() 方法的返回值是一个不能改变的集合,该集合不能再使用add()put()方法添加元素。
    • Set接口和Map接口再调用of()方法的时候,不能有重复元素,否则异常。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值