文章目录

  • Set 接口
    • 0:接口说明
    • 1:功能概述
    • 2、实现子类
      • HashSet
      • LinkedHashSet
      • TreeSet
      • Comparable 自然排序接口
      • Comparator 比较器接口
  • Map 接口
    • 0:接口说明
    • 1:功能概述
    • 2:实现子类
      • HashMap
      • LinkedHashMap
      • TreeMap
      • 综合案例
    • 面试题
  • 集合工具类 Collections
    • 0:类的描述
    • 1:功能概述
  • 集合框架
    • 通用方法
    • 性能测试
      • List性能比较
      • Set性能比较
      • Map性能比较

Set 接口

public interface Set<E> extends Collection<E>

0:接口说明

Set 接口是对数学中的集合的抽象,满足集合的一般性质:

  • 无序性(进出顺序)
  • 唯一性(不包含重复元素)
  • 至多一个null值元素(可以没有)

1:功能概述

Set 接口完全继承,并根据集合的特点重写了 Collection的 15个方法。没有任何特有方法。

  • 添加功能 2 add、addAll
  • 删除功能 4 clear、remove、removeAll、retainAll
  • 获取功能 3 size、Iterator、hashCode
  • 判断功能 4 isEmpty、equals、contains、containsAll
  • 转换功能 2 toArray、toArray

Set VS List 异同点

不同点:

  • Set:无序的集合,不可重复,无索引,无任何特有方法
  • List:有序的集合,可重复,有索引,10个特有方法 【针对索引的 添加功能:add、addAll;针对索引的 删除功能:remove;读写器:get、set;特有遍历:listIterator、listIterator(index);获取功能:indexOf、lastIndexOf(高开销的线性搜索);获取局部视图功能:subList (操作的时候,直接映射到原来的List上面)】

相同点:

  • 两者同时继承了 Collection 的15个基本操作方法,以及作为集合的特性(可变的数组:数据类型可变、元素个数可变)
  • Set 没有 关于索引的操作,以及读写器,源于Set的无序性

2、实现子类

HashSet

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

0:类的描述

此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例【参见 构造器的底层源码】) 支持的。不保证集合的 迭代顺序,特别是不保证此顺序恒久不变,此类允许 null 元素。

此类为基本操作提供了稳定的性能,这些基本操作是 add、remove、contains 和 size。 假设 哈希函数将这些元素正确分布在桶中。对此 set 进行迭代所需时间与 HashSet 实例的大小(元素的数量)和 底层 HashMap 实例(桶的数量)的“容量”的和成正比。因此,如果迭代器的性能很重要,则不要将初始容量设置的太高。(或将加载因子设置的太低)

此类 单线程高效,需要同步进行安全访问的时候,粗腰创建 同步的集合Collections.synchroniizedSet(new HashSet(...))

请注意,并发修改异常 ConcurrentModificationException :创建迭代器之后,使用集合的方法进行集合元素的修改,Iterator抛出的异常,是快速失败。

1:功能方法

根据哈希表的结构,来实际分析 基本操作的 实现原理:

  • add:实现的过程:(1)获取需添加的元素的 hashCode() ,根据元素的 哈希值(和哈希函数) 找到 链表在数组中位置;(2)遍历链表,使用 equals() 进行判断,如果已经存在 直接返回 false;否则添加在链表最后,并返回true。

  • remove:实现过程:(1)获取需删除的元素的 hashCode() ,根据元素的 哈希值(和哈希函数) 找到 链表在数组中位置;(2)遍历链表,使用 equals() 进行判断,如果存在 直接删除,并返回true;否则直接返回 false。

  • contains:实现过程:(1)获取需判断的元素的 hashCode(),根据元素的 哈希值(和哈希函数) 找到 链表在数组中位置;(2)遍历链表,使用equals()进行判断,如果存在,则返回 true,否则返回 false

  • size:实现过程:直接遍历数组中每个链表的,返回 所有链表的元素个数之和。

package com.rupeng.set;import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;public class Demo2
{public static void main(String[] args){Set set = new HashSet();set.add("com");set.add("Rupeng");set.add("www");Iterator it = set.iterator();while (it.hasNext()){String str = (String) it.next();System.out.println(str);}/** 虽然 进出的顺序相同,但不能说明 Set 是有序的。 *//** 暂且将有序定义为:不论如何添加元素,在写代码的那一刻,你就知道取出时的顺序了 *//** 比如:数组列表、队列、栈等有序集合 */}
}

注意一个问题:虽然 Set 是无序的,但也有自身的 存储结构。所以即使是说 向 Set 中添加元素的顺序和遍历取出的顺序相同,并不能说明 Set 的有序性。

说完了 有序的问题,再来看看 Set 集合的唯一性,是如何保证的?实际上,Set 基本操作的实现过程已经说明了,Set 元素唯一性的原因:

  • 先看 hashCode() 值是否相同,不相同 直接添加到集合中(作为新链表的首元素);相同 则继续看 equals() 比较的结果,返回 true ,元素存在 不添加;返回 false ,原始不存在,添加到集合中(作为原有链表的尾元素)

源码分析:

// HashMap 类中 实现添加最核心的语句
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
{break;
}

默认情况下,hashCode()equals() 都是继承自Object超类的,进行比较的是地址值,但是有些情况下,比较地址值的意义不大,相反,使用类的成员变量的比较更有意义。比如:两个学生是否相等,如果是用地址值比较的话,因为在堆内存中不可能公用一块地址,即使是同一个人实例化两次,也会得到 为 false 的判断结果,这个时候,就需要重写 这两个方法 了。

案例1:创建字符串的 HashSet 集合

package com.rupeng.set;import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;public class Demo2
{public static void main(String[] args){Set set = new HashSet();set.add("com");set.add("Rupeng");set.add("www");set.add("Rupeng");Iterator it = set.iterator();while (it.hasNext()){String str = (String) it.next();System.out.println(str);}}
}

String 类 重写了来自 Object 的 hashCode()equals(),对于相等的比较,是按位比较 字符串中的每个字符是否相等。故此,可以保证元素的唯一性。

案例2:创建自定义学生对象的 HashSet 集合

package com.rupeng.set;import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;public class Demo3
{public static void main(String[] args){Student stu1 = new Student("Cathy", 18);Student stu2 = new Student("Cathy", 18);Student stu3 = new Student("Ian", 17);Student stu4 = new Student("Ian", 17);Set<Student> set = new HashSet<Student>();set.add(stu1);set.add(stu2);set.add(stu3);set.add(stu4);for (Iterator<Student> it = set.iterator(); it.hasNext();){Student st = it.next();System.out.println(st);}}
}

从代码的执行结果,可能会觉得错了。实际上 计算器 又被冤枉了,它只是按照 引用对象的地址值进行比较,确定两个值是不是相等的,而我们想要的是 通过对象的成员变量的比较来确定两个 对像是不是相等,这个时候就需要 在自定义类 Student 中重写 那两个关键的方法(hashCodse()equals())了。【为了更加方便地输出 学生信息,可以重写 toString() 方法】

// 只是简单的重写,程序的健壮性什么的没有考虑
// 更完善的代码,可以直接使用 快捷键生成 这两个方法
// 快捷键:ALT + SHIFT + S + 键盘键入 h或H
@Override
public int hashCode()
{return name.hashCode() + age * 2015;
}@Override
public boolean equals(Object obj)
{Student other = (Student) obj;if (other == this){return true;}if (other.getName() == name){if (other.getAge() == age){return true;}}return false;
}

综上,HashSet 如何保证 元素的唯一性

  • a、底层数据结构是 哈希表【元素是链表的 数组】
  • b、哈希表依赖于哈希值存储
  • c、添加功能底层实现依赖两个方法 (hashCodse()equals()

LinkedHashSet

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

a、可预知迭代顺序的 Set接口,底层是哈希表和链表的实现

b、元素有序且唯一,允许 null 元素【哈希表保证元素的唯一性,链表保证元素的有序性】,增删效率高

c、按照链表定义的迭代顺序与其插入顺序相同

d、单线程高效,安全需要时,使用同步Set集合方式定义 Collections.synchronizedSet(new LinkedHashSet(...)); ,同样存在快速失败的 并发修改异常 ConcurrentModificationException

TreeSet

public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, Serializable

public interface NavigableSet<E> extends SortedSet<E>

0:类的描述

根据实际的继承关系,可以知道 NavigableSet 接口实现了一个排序的接口。

TreeSet 是基于 TreeMap 的 NavigableSet 实现,实际上是 TreeMap 的一个实例。使用元素的自然顺序(Comparable)对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。

此实现为基本操作(add、remove 和 contains)提供受保证的 log(n) 时间开销。 (时间复杂度)

TreeSet 具有 唯一性和排序性。不允许 null 元素 增删效率高

因为二叉树对于位置的定位很快,所以无论是添加 删除 效率都很高。
添加时:只需要定位添加的位置
删除时:只需要定位待删除元素的位置

1:数据结构

TreeSet 底层数据结构是 红黑树(Red-Blank Tree),也就是自平衡的二叉查找树(元素的快速查找

二叉查找树:每个节点有且只有一个父节点,最多存在两个子节点,并且该节点的值大于等于左子树中的所有节点,小于等于右子树的所有节点。

存储方式:

  • 第一个元素作为 Root 元素
  • 之后的元素和Root元素 比较
    • 大于 Root – 在右子树中进行存储
    • 小于 Root – 在左子树中进行存储
    • 等于 Root – 不进行存储

取出方式:
深层 -自上而下 三种主要的遍历方式

  • a、先序遍历 Root - Left - Right
  • b、中序遍历 Left - Root - Right
  • c、后序遍历 Left - Right - Root

注意:不论是存储还是取出的时候,这种定义都是 递归 的

简单元素的存取:

说明: 从上面的存储过程,证明 TreeSet 实际上在存元素时,就已经给定了一个顺序了,之后 取出的元素 是按照 从小到大的,再结合 二叉查找树的遍历结结果 可以知道,实际上 TreeSet 迭代实现是 中序遍历 。

2:功能方法

自然排序 构造器:public TreeSet()
比较器排序 构造器:public TreeSet(Comparator<? extends E> c)

其余功能方法和Set一致,不再赘述。

案例1:创建字符串的 TreeSet 集合

package com.rupeng.set;import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;public class TreeSetDemo1
{public static void main(String[] args){Set<String> set = new TreeSet<String>();set.add("Javase");set.add("Rupeng");set.add("JoianSUN");set.add("Shanhai");set.add("20150705");for (Iterator<String> it = set.iterator(); it.hasNext();){String str = it.next();System.out.println(str);}}
}

案例2:创建自定义对象的 TreeSet 集合

package com.rupeng.set;import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;public class TreeSetDemo2
{public static void main(String[] args){Set<Student> set = new TreeSet<Student>();set.add(new Student("Joian", 18));set.add(new Student("Du", 27));set.add(new Student("Yzk", 10));for (Iterator<Student> it = set.iterator(); it.hasNext();){Student stu = it.next();System.out.println(stu);}}
}

无法将 Student 转换为 Comparable :Student 不是接口 Comparable 的实现类,所以无法装换。

在构造 TreeSet 对象的时候,使用了默认的构造器,集合中元素的排序方式是自然排序,也就是说 添加到集合的元素必须是 Comparable 接口的实现类实例。否则报错。简言之:需要的 Comparable 接口的实现类实例,实际是 未实现接口的 Student 。之所以 String 类可以正常添加,是因为 String 类实现了 Comparable 接口。

如何解决呢?

  • 让自定义对象实现 Comparable 自然排序 接口;
  • 使用比较器 Comparator的构造,让集合有序

为了篇幅的整齐,直接在此处给出具体的解决方案。进行具体的问题解决之前,先去看一下 这两个 接口的相关内容吧,就在这一节的后面。

解决方案1:元素 实现 自然排序接口

// 仅仅给出了 Comparable 接口的方法实现
public class Student implements Comparable<Student>
{/** 实现的是 先按照姓名升序,姓名相同,按照年龄升序 */@Overridepublic int compareTo(Student o){int result = getName().compareTo(o.getName());if (result == 0){return getAge() - o.getAge();} else{return result;}}
}

解决方案2:TreeSet 实现 比较器接口的构造

// 使用比较器 构造 TreeSet 对象,并对代码进行了重构
/** 实现的是 先按照姓名升序,姓名相同,按照年龄升序 */
Set<Student> set = new TreeSet<Student>(new Comparator<Student>()
{@Overridepublic int compare(Student o1, Student o2){int result = o1.getName().compareTo(o2.getName());if (result == 0){result = o1.getAge() - o2.getAge();}return result;}
});

实现的是 先按照姓名升序,姓名相同,按照年龄升序,实际完成需求时,需要自行取分析主要条件和次要条件,譬如:给定的需求会是 请按照学生姓名对集合中的元素进行升序排序,那么 主要条件是 姓名,次要条件是 年龄(如果姓名相同的话,如何按照年龄排序,可升序 可降序)

案例:根据学生的姓名长度进行排序

package com.rupeng.set;import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;/*** 需求:根据学生姓名长度进行升序排序* * 分析:*    (1)主要条件:学生姓名长度*  (2)次要条件:学生姓名、学生年龄* * 编码实现:*       因为实际给定了一个 比较,使用比较器 构造 TreeSet,*      相对 使Student 实现自然排序,更有意义,自然排序 应该是只和对象成员变量本身有关,*     才更有意义,而且一般不要更改已经写好的 类*/
public class TreeSetDemo3
{public static void main(String[] args){Set<Student> set = new TreeSet<Student>(new Comparator<Student>(){@Overridepublic int compare(Student o1, Student o2){int result = o1.getName().length() - o2.getName().length();if (result == 0){result = o1.getName().compareTo(o2.getName());if (result == 0){result = o1.getAge() - o2.getAge();}}return result;}});set.add(new Student("Joian", 18));set.add(new Student("Cathy", 18));set.add(new Student("Du", 27));set.add(new Student("Du", 27));set.add(new Student("Yzk", 10));set.add(new Student("Yzk", 19));Iterator<Student> it = set.iterator();while (it.hasNext()){Student stu = it.next();System.out.println(stu);}}
}

TreeSet 的排序方式 取决于使用的构造方法:public TreeSet() 自然排序,public TreeSet(Comparator<? extends E> c) 比较器排序

public TreeSet(Comparator<? extends E> c)
/*** 接口 作为参数传递,实际需要的是 接口的实现类对象:* 主要三种方式 给定 实现类对象* (1)匿名内部类(最常用)* (2)外部类* (3)借口实现*/

TreeSet 唯一性和元素排序的原理

  • A :唯一性:底层数据结构红黑树支持,具体实现是 比较的返回值是否为 0 来决定
  • B :排序:
    • a 、自然排序(元素具有比较性)让元素所属类实现 Comparable 自然排序接口
    • b 、比较器排序(TreeSet具有比较性) 给 TreeSet 构造方法提供一个 Comparator 比较器接口实现类对

理解

  • 元素具有比较性:学生按照自身身高在教室中中坐座位
  • 集合具有比较性:学生按照教室中给定的编号坐座位

综合案例1:使用HashSet 重新实现:获取 10个 1~20 范围内的整数,且不可重复

package com.rupeng.set;import java.util.HashSet;
import java.util.Random;
import java.util.Set;public class HashSetDemo
{public static void main(String[] args){Random random = new Random();Set set = new HashSet();while (set.size() < 10){int num = random.nextInt(20) + 1;set.add(num);}System.out.println(set);}
}

综合案例2:键盘录入五个学生的成绩信息,按照总分降序排序

package com.rupeng.set;import java.util.Comparator;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;/*** 需求:键盘录入五个学生的成绩信息,按照总分降序排序 * 格式 为: 姓名 语文成绩 数学成绩 英语成绩。譬如:Joian 90 89 100*/
public class TreeSetDemo
{public static void main(String[] args){Scanner sc = new Scanner(System.in);String[] strs = new String[5];for (int i = 0; i < strs.length; i++){System.out.println("Please enter the No."+(i + 1)+" student's record:");strs[i] = sc.nextLine();}sc.close();Set<Student> set = new TreeSet<Student>(new Comparator<Student>(){public int compare(Student o1, Student o2){/* int result = o2.getTotal() - o1.getTotal();if (result == 0){result = o1.getName().compareTo(o2.getName());if (result == 0){result = o1.getChinese() - o2.getChinese();if (result == 0){result = o1.getMath() - o2.getMath();if (result == 0){result = o1.getEnglish() - o2.getEnglish();}}}}return result;*/int num1 = o2.getTotal() - o1.getTotal();int num2 = (num1 == 0) ? (o1.getName().compareTo(o2.getName())): num1;int num3 = (num2 == 0) ? (o1.getChinese() - o2.getChinese()): num2;int num4 = (num3 == 0) ? (o1.getMath() - o2.getMath()) : num3;int num5 = (num4 == 0) ? (o1.getEnglish() - o2.getEnglish()): num4;return num5;};});for (String str : strs){String[] info = str.split(" ");Student stu = new Student(info[0], Integer.parseInt(info[1]),Integer.parseInt(info[2]), Integer.parseInt(info[3]));set.add(stu);}System.out.println("----------------------------");for (Student st : set){System.out.println(st.printScore());}}
}

Comparable 自然排序接口

底层源码:

public interface Comparable<T>
{public int compareTo(T o);// 实际修饰符 应该是 public abstract
}

接口强行对实现它的每一个类进行整体排序,称为类的 自然排序。类的方法 compareTo 被称为 自然排序方法

  • 实现此接口的对象集合和数组,可以通过对应工具类中的 sort 方法(Collections.sort()Arrays.sort())进行整体排序。

  • 实现此接口的对象可以作为 有序映射(SortedMap)的键和有序集合(SortedSet)的元素,无需指定比较器(Comparator) (TreeMap 和 TreeSet 就是有序映射、集合

对于集合两个对象 e1 和 e2 ,当且仅当 e1.equals(e2)e1.compareTo(e2)==0 返回相同的 boolean 值时,类的自然排序才叫做与 equals 一致。 现阶段 我们需要这种一致,如果不一致的话,会出现很奇怪的想象,所以注意 与equals一致。实际上,所有实现 Comparable 接口的 Java 核心类都具有与 equals 一致的自然排序。 但是 java.util.BigDecimal 是个特例,它的自然排序 将值相同、精度不同的对象视为相等。

注意: null 不是任何类型的实例,即使 e.euaals(null) 返回false,e.compareTo(null) 也会抛出异常 NullPointerException。 这个就是 为什么 TreeSet 元素 和 TreeMap 的键 不允许 null 元素的原因。

功能方法
public abstract int compareTo(T o);

比较当前对象与指定对象的顺序。如果当前对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。

实现整体排序,实际的底层 是 常见的排序算法。

注意ClassCastException 异常,如果 o 无法转换为指定类的对象。

Comparator 比较器接口

底层源码:Java 1.8 之前

public interface Comparator<T>
{// Java 1.8 以前 全部的方法int compare(T o1, T o2);boolean equals(Object obj);// 实际修饰符:public abstract
}

强行对实现接口的 对象集合进行整体排序的比较器。

  • 可以将 Comparator 传递给 sort 方法 (Collections.sort()Arrays.sort()) 从而实现排序顺序的精确控制。

  • 可以将使用 Comparator 来控制某些数据结构的顺序 (有序映射 TreeMap 和 有序集合 TreeSet)

  • 可以为没有实现自然排序的对象 的集合提供排序

对于Comparator c 的两个元素 e1 和 e2 ,当且仅当 e1.equals(e2)c.compare(e1, e2)==0 返回相同的 boolean 值时,Comparator c 提供的排序才叫做与 equals 一致。

功能方法

  • int compare(T o1, T o2);

比较用来排序的两个参数。根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数。

注意ClassCastException 异常,如果 o1、o2 无法转换为指定类的对象。

  • boolean equals(Object obj);

指示某个其他对象是否“等于”此 Comparator。仅当 指定的对象也是一个 Comparator,并且强行实施与此 Comparator 相同的排序时,此方法才返回 true。

Map 接口

0:接口说明

public interface Map<K,V> java.util 包下。

Map 接口是 键映射到值的对象。 键具有唯一性,值可重复 。 Map 实际对应的是 数学中函数。键为自变量,值为因变量。 Map 中的每一个键值对,实际就是 函数中每一个自变量与所得因变量的对应关系,这种对应关系在 Map 中的体现是 就是键值对 public static interface Map.Entry<K,V> ,是以内部类(实际是接口)形式定义的 。

public static interface Map.Entry<K,V>

  • Map 的成员方法 entrySet 返回的就是 Map 的 内部类(接口)Entry 的对象集合 。

  • 获得映射项引用的唯一方法就是通过 集合视图的 迭代器 实现(新特性 增强 for实质也是迭代器实现的)

  • Map.Entry 仅仅在迭代期间有效,更确切来讲,在迭代器返回项之后,修改底层映射,会造成映射行为的不确定性。注意setValue方法例外

Map.Entry 接口成员方法:

  • boolean equals(Object e) 给定对象 为映射项且和当前对象拥有相同的映射关系,返回 true ,更确切的说,满足如下关系的 e1 和 e2 才具有相同的映射关系 (e1.getKey() == null ? e2.getKey() == null : e1.getKey().equals(e2.getKey())) && (e1.getValue() == null ? e2.getValue() : e1.getValue().equals(e2.getValue())) 也就是说 当两个 映射项 对的键和值对影响等的是时候,才说 这两个对应关系是 相同对应关系,允许 null 键值,这可以确保 equals 方法在不同的 Map.Entry 接口实现间可正确地工作。(定义中之所以使用 equals 方法 而不是 == ,是因为可能存在着需要 自定义的相等关系,而不只是 地址相等,可能会是 对象的成员变量对应相等,即视为 对象相等这样的定义,这很常见)
  • int hashCode() 返回此映射项的哈希码值。 映射项 e 的哈希码值的定义如下:(e.getKey() == null ? 0 : e.getKey().hashCode())^(e.getValue() ? 0 :e.getValue().hashCode()) 这个确保 e1.equals(e2) 因为着 对于任意的两个项 e1 和 e2 而言 e1.hashCode() == e2.hashCode(),这也正是 Object.hashCode 常规协定所要求的。
  • K getKey() 获取当前映射项的键
  • V getValue() 获取当前映射项的值
  • V setValue(V value) 设置 当前映射项的值 (注意各种异常),返回 旧的值

Map 接口提供了三个视图:

  • 键的集合:键具有唯一性 public Set<K> keySet()
  • 值的集合:值可重复 public Collection<V> values()
  • 映射项的集合:根据键值的点,键值对(映射项)具有唯一性 public Set<Map.Entry<K,V>> entrySet()

三种视图的迭代器,支持移除操作(remove、removeAll、clear),但是不支持添加操作。

映射顺序:迭代器在视图上返回元素的顺序。TreeMap 保证这种顺序,HashMap 不保证这种顺序。(对比 HashSet、TreeSet【底层实现就是 对应的HashMap、TreeMap的键集合】)

通过映射实现类应该有两种构造器:

  • 无参构造 public 实现类名()
  • 根据已有映射关系复制建立的 public 实现类名(Map<? extends K,? extends V> m)

注意:equals 和 hashCode 的定义必须是明确的。

1:功能概述

先来看看Map与Collection的区别?

  • Map 集合元素 是成对出现的 Entry 实例,Entry<K,V> 中 K 是唯一性的,V 是可重复的;Map 集合的数据额结构针对键有效,与值无关
  • Collection 集合元素 是单个出现的 Java 任意类(含有自定义类)的实例;Collection 的 子接口中 Set 是唯一的,List 是可重复的;Collection 集合的数据结构针对元素有效
  • a、添加功能

    • V put(K key, V value) 修改集合中的映射项,返回的是与 key 关联的旧的值,如果该映射项不存在,则执行添加 并返回 null(注意:如果集合 允许 null 值,返回的 null 包含两种可能性:(1)第一次添加;(2)修改之前的值 是 null )
    • void putAll(Map<? extends K, ? extends V> m) 从指定映射 m 中,将其所有得映射项 复制到 当前的映射中。相当于:从 m 中 遍历每一个映射项,并对当前的 映射 进行 put 方法的调用(修改/添加)
  • b、删除功能

    • void clear() 清楚当前映射

    • V remove(Object key) 移除 key 键所在的映射项,如果不存在这样的映射项,则返回 null,如果存在的话,执行删除 并返回 与之对应的 值。 满足的映射项的 键 k 满足以下条件:key == null ? k == null : key.equals(k) 。(注意:如果集合 允许 null 值,返回的 null 包含两种可能性:(1)不能存在对应的映射项;(2)删除的映射项的值 是 null )

  • c、判断功能

    • boolean equals(Object o) 给定的 映射 o 和当前映射相等,返回 true,相等的定义是,具有相同的映射关系,也就是 o.entrySet().equals(this.entrySet()) 返回 true 。注意重写 hashCode 方法,需要满足 Java 的常规协定,hashCode() 返回此映射的哈希码值。映射的哈希码定义为此映射 entrySet() 视图中每个项的哈希码之和。这确保 m1.equals(m2) 对于任意两个映射 m1 和 m2 而言,都意味着 m1.hashCode()==m2.hashCode(),正如 Object.hashCode() 常规协定的要求。

    • boolean isEmpty() 如果 当前的映射不包含任何的键值对(映射项)的话,返回true

    • boolean containsKey(Object key) 如果此映射包含指定键的映射关系,则返回 true。更确切地讲,当且仅当此映射包含针对满足 (key==null ? k==null : key.equals(k)) 的键 k 的映射关系时,返回 true。(最多只能有一个这样的映射关系)。

    • boolean containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true。更确切地讲,当且仅当此映射至少包含一个对满足 (value==null ? v==null : value.equals(v)) 的值 v 的映射关系时,返回 true。对于大多数 Map 接口的实现而言,此操作需要的时间可能与映射大小呈线性关系。 (Map 的数据结构只是针对 键 有效,所以这个操作是需要 遍历映射的 所有映射项

  • d、获取功能

    • V get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。 更确切地讲,如果此映射包含满足 (key==null ? k==null : key.equals(k)) 的键 k 到值 v 的映射关系,则此方法返回 v;否则返回 null。(最多只能有一个这样的映射关系)。(注意:如果集合 允许null值,返回 null 包含两种含义:(1)不存在该项;(2)该项的值是null)
    • int size() 该映射中键值对(映射项) 的个数。注意 int 值的范围
    • 键的集合:键具有唯一性 public Set<K> keySet()
    • 值的集合:值可重复 public Collection<V> values()
    • 映射项的集合:根据键值的点,键值对(映射项)具有唯一性 public Set<Map.Entry<K,V>> entrySet()

获取映射中的项,必须使用 迭代器

  • 键集合 + get 方法
  • 键值集合 + Entry<K, V> 获取功能

案例:遍历映射实例

  • 方式1:键集合 + get 方法
package com.rupeng.map;import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;/** 遍历 映射 */
public class MapDemo1
{public static void main(String[] args){Map<Integer, String> map = new HashMap<Integer, String>();map.put(1, "Joian");map.put(2, "Yzk");map.put(3, "Cathy");map.put(4, "Apple");Set<Integer> set = map.keySet();for (Integer in : set){System.out.println(in + " " + map.get(in));}}
}
  • 方式2:键值集合 + Entry<K, V> 获取功能
package com.rupeng.map;import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;/** 遍历 映射 */
public class MapDemo1
{public static void main(String[] args){Map<Integer, String> map = new HashMap<Integer, String>();map.put(1, "Joian");map.put(2, "Yzk");map.put(3, "Cathy");map.put(4, "Apple");Set<Entry<Integer, String>> set = map.entrySet();for (Iterator<Entry<Integer, String>> it = set.iterator(); it.hasNext();){Map.Entry<Integer, String> me = it.next();System.out.println(me.getKey() + " " + me.getValue());}}
}

2:实现子类

HashMap

基于哈希表的Map接口实现。四个特性:

  • Map 中键唯一,值可重复,映射项唯一,这个是所有的 Map实现类的**共性**
  • Map 的数据结构 只对 键有效,与值无关,第一点的映射项唯一就是依靠键的唯一实现
  • HashMap 允许 null 键、null值(但这样的键值对,最多出现一次)
  • HashMap 不保证 映射的顺序,特别是不保证其顺序很久不变。

注意 : 如果出现存入的顺序和取出的顺序一样,并不能说明 HashMap的有序性,只是 HashMap 也有其存储的顺序 而已。

使用四个案例 来说明,HashMap的特性