Java——集合框架(Map)

简介: Java——集合框架(Map)

6 Map


6.1 Map 特点和基本操作


Map 接口与 Collection 接口不同,这个接口的元素是“键值对”。其中,键值对的特点是:键不可以重复,值可以重复。


Map 接口中的一些基本操作罗列如下:


Object get(Object key)


这个方法完成的功能是,通过键对象 key,来找到相应的值对象。


put(Object key, Object value)


这个方法是把一个键值对放入 Map 中。如果键不存在,则在 Map 中新增一个键值对。如果键已存在,则把新值替换旧值。

System.out.println(map.get(“2002”));
map.put(“2002”, “Brazil”);
System.out.println(map.get(“2002”));
map.put(“2002”, “China”);
System.out.println(map.get(“2002”));
remove(Object key)


这个方法根据一个键,删除一个键值对。



Set keySet()


这个方法返回所有键的集合。由于在 Map 中,键没有顺序,且不可以重复,因此所有的键对象组成的就是一个 Set。也就是说,


keySet 方法返回的是一个 Set,这个


Set 就是所有键对象的集合。


Collection values()


values 方法返回类型是一个 Collection,返回的是所有值对象的集合。


containsKey / containsValue


这两个方法用来判断在 Map 中键是否存在,或者值是否存在。


size()


这个方法返回 Map 中键值对的个数


isEmpty()


判断 Map 是否为空


clear()


清空 Map


entrySet


这个方法返回值类型是一个 Set 集合,集合中放的是 Map.Entry 类型。这个方法是用来做键值对遍历的,在讲解遍历的时候还会给大家讲到。


6.2 遍历


首先先使用一个 Map 接口的实现类:HashMap。创建相应的 HashMap 对象,并放入一些初始值

import java.util.*;
public class TestMap {
     public static void main(String args[]){
         Map map = new HashMap();
         map.put("2006", "Italy");
         map.put("2002", "Brazil");
         map.put("1998", "France");
         map.put("1994", "Brazil");
     }
}

在这个 Map 的基础上,我们开始对 Map 进行遍历。由于 Map 管理的是键值对,因此对于 Map 而言,有多种遍历的方式:键遍历、键值遍历、利用 Map.Entry 进行键值遍历。



6.2.1 键遍历与键值遍历


键遍历指的是遍历所有的键。键遍历的实现非常简单:通过调用 Map 接口中的 keySet方法,就能获得所有键的集合。然后,就可以像遍历普通 Set 一样遍历所有键对象的集合。键遍历参考代码如下:

Set set = map.keySet();
Iterator iter = set.iterator();
while(iter.hasNext()){
    System.out.println(iter.next());
}

键遍历输出结果如下:


2006


1998


2002


1994



可以看到,键遍历输出了集合中所有的键,并且,键并没有顺序。


在键遍历的基础上更进一步,能够遍历所有的键值对。思路如下:利用键遍历能够遍历所有的键,而在遍历键的时候,可以使用get 方法,通过键找到对应的值。键值遍历的参考代码如下:

Set set = map.keySet();
Iterator iter = set.iterator();
while(iter.hasNext()){
    Object key = iter.next();
    Object value = map.get(key);
    System.out.println(key + "--->" + value);
}

键值遍历的结果如下:


2006--->Italy


1998--->France


2002--->Brazil


1994--->Brazil


可以看到,键值遍历时能够输出键值对这种一一对应的关系。



6.2.2 值遍历


除了键遍历以及键值遍历之外,Map 接口还有一种遍历方式:值遍历。值遍历表示的是遍历 Map 中所有的值对象。与键遍历类似,我们对 Map 进行值遍历的思路也很简单:首先利用 Map 的 values()方法获得 Map 中所有值的集合。需要注意的是,values()方法返回的是一个 Collection 类型的对象,因此,应当用迭代遍历的方式,遍历这个 Collection。参考代码如下:

Collection conn = map.values();
Iterator iter = conn.iterator();
while(iter.hasNext()){
    System.out.println(iter.next());
}

这样,我们就遍历了 Map 中的所有值。输出结果如下:


Italy


France


Brazil


Brazil



6.2.3 利用 Map.Entry 进行遍历


在 Map 接口中,有一个方法叫做 entrySet。这个方法返回一个 Set 集合,这个集合中装的元素的类型是 Map.Entry 类型。Map.Entry 是 Map 接口的一个内部接口。这个接口封装了 Map 中的一个键值对。在这个接口中,主要定义了这样几个方法:


getKey() : 获得该键值对中的键


getValue(): 获得该键值对中的值


setValue():修改键值对中的值


因此,利用 Map.Entry 也可以进行遍历。相关代码如下:

Set set = map.entrySet();
Iterator iter = set.iterator();
while(iter.hasNext()){
    Map.Entry entry = (Map.Entry) iter.next();
    System.out.println(entry.getKey() + "-->" + 
    entry.getValue());
}

注意,在赋值的时候,应当把 iter.next 的返回值强转成 Map.Entry 类型才可以。结果如下:



2006-->Italy


1998-->France


2002-->Brazil


1994-->Brazil


可以看到,用 Map.Entry 进行遍历,以及使用 keySet()方法以及 get()方法进行键值,这两种遍历的结果是一样的。


6.3 实现类


Map 接口主要的实现类就是 HashMap 和 LinkedHashMap,此外还有一个使用较少的Hashtable。HashMap 的特点是:在判断键是否重复的时候,采用的算法是 Hash 算法,因此要求作为 HashMap 的键的对象,也应该正确覆盖 equals 方法和 hashCode 方法。LinkedHashMap 和 HashMap 之间的区别有点类似于 LinkedHashSet 和 HashSet 之间的区别:LinkedHashMap 能够保留键值对放入 Map 中的顺序。


image.png


要注意,HashMap 和 Hashtable 有两方面的区别。一方面,这两个实现类一个是重量级,一个是轻量级。这类似于 ArrayList 和 Vector 的区别,也就是说,Hashtable 中的所有方法都是同步方法,因此是线程安全的。另一方面,在于对 null 值的处理。


7 Comparable 与排序


7.1 Collections 类与 Comparable


在 java.util 包中,提供了一个 Collections 的类(注意这个类的名字,与我们的 Collection接口只相差最后一个字母 s)。这个类中所有的方法都是静态方法,也就是说,Collections类所有方法都能够通过类名直接调用。这个类为我们提供了很多使用的功能,例如:sort 方法,这个方法能够对 List 进行排序。再例如,max 方法可以找到集合中的最大值,而 min方法可以找到集合中的最小值,等等。


7.2 TreeSet 与 TreeMap


Set 接口有一个子接口:SortedSet,这个接口的特点是:元素不可重复,且经过排序。这个接口有个典型的实现类:TreeSet。要注意的是,因为 TreeSet 中要对对象进行排序,因此要求放入 TreeSet 接口中的对象都必须实现 Comparable 接口。


8 5.0 新特性:foreach 循环


foreach 循环主要要解决的是遍历的问题。对于 List 来说,我们可以采用 for 循环遍历,而对于 Set 而言,我们只能采用迭代遍历。相对 for 循环遍历而言,迭代遍历的代码比较繁琐和复杂。例如,对于一个 Set 而言,采用迭代遍历的代码如下:

Iterator iter = set.iterator();
while(iter.hasNext()){
    Object value = iter.next();
    System.out.println(value);
}

为了简化遍历的代码,在 5.0 中引入了 foreach 循环。基本语法如下:

for(变量 : 集合){
    循环体;
}
for(Object value : set){
    System.out.println(value);
}
此外,foreach 循环还能够用来遍历数组。例如下面的代码:
String[] ss = new String[]{“hello”, “world”, “java”};
for(String obj : ss){
    System.out.println(obj);
}

9 5.0 新特性:泛型


ArrayList 为了设计的更加通用,其内部保存数据的时候,保存数据的时候都采用的是 Object 类型。因为只有设计成这样,ArrayList 才能用来保存所有的 Java 对象。但是,如果把 ArrayList 设计成这样的话,就会造成之前的两个问题:我们无法象定义某个类型的数组那样,定义一个专门用于存放某个类型对象的集合。因此,我们称传统的集合对象是:类型不安全的。5.0 引入的泛型机制能够很好的解决我们上面所说的问题。



9.1 泛型的基本使用


使用 5.0 的泛型机制非常简单。例如,我们希望创建一个只能用来保存 Dog 对象的 List,可以使用如下代码:


List<Dog> list = new ArrayList<Dog>();

注意,跟原有的代码相比,在 List 接口和 ArrayList 后面,都有个后缀:。这表明,创建的 ArrayList 只能够放置 Dog 类型。此时,如果对 list 放入非 Dog 类型对象,则会产生一个编译错误,示例如下:

list.add(new Dog()); //OK
list.add(new Cat()); //!编译错误

对 Set 集合使用泛型的方法,和 List 接口使用泛型的方法类似,在此不再赘述。


9.2 泛型与多态


请看下面的例子:


List dogList = new ArrayList();


List aniList = dogList; //! 编译出错!


这两行代码中,第一行编译正确,第二行编译出错。在第一行代码中,把一个 ArrayList直接赋值给一个 List类型的引用。在这个赋值过程中,类型里有多态(把 ArrayList 赋值给 List),但是泛型是一样的(均是 Dog 的泛型)。在第二行代码中,把一个 List赋值给一个 List,这样赋值是错误的!在这个过程中,类型中没有多态(List 是相同的),而泛型有多态(一个是 Dog 的泛型,一个是 Animal 的泛型)。这句话会导致一个编译错误!


请记住这个结论:类型可以有多态,但是泛型不能够有多态!


9.3 自定义泛型化类型


现在我们定义一个类,用来表示“一对”这个概念。如果不用泛型的话,示例代码如下:

class Pair{
     private Object valueA;
     private Object valueB;
     public Object getValueA() {
         return valueA;
     }
     public void setValueA(Object valueA) {
         this.valueA = valueA;
     }
     public Object getValueB() {
         return valueB;
     }
     public void setValueB(Object valueB) {
         this.valueB = valueB;
     }
    }
public class TestPair {
    public static void main(String[] args){
         Pair p = new Pair();
         p.setValueA(new Dog());
         p.setValueB(new Dog());
     }
}

可以看到,Pair 类为了尽可能通用,使用了 Object 类型来保存一对值。但是这样就会有类型方面的问题,例如:


p.setValueA(new Dog());


p.setValueB(new Cat());


这样,这个代码就把一只猫和一条狗硬生生配成了一对,显然,我相信无论是猫还是狗都不会愿意的。

class Pair<T>{
     private T valueA;
     private T valueB;
     public T getValueA() {
         return valueA;
     }
     public void setValueA(T valueA) {
         this.valueA = valueA;
     }
     public T getValueB() {
         return valueB;
     }
     public void setValueB(T valueB) {
         this.valueB = valueB;
     }
    }
public class TestPair {
     public static void main(String[] args){
         Pair<Dog> p = new Pair<Dog>();
         p.setValueA(new Dog());
         p.setValueB(new Dog());
         // p.setValueA(new Cat()); 编译出错!
     }
}


相关文章
|
6天前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
9天前
|
Java C# Swift
Java Stream中peek和map不为人知的秘密
本文通过一个Java Stream中的示例,探讨了`peek`方法在流式处理中的应用及其潜在问题。首先介绍了`peek`的基本定义与使用,并通过代码展示了其如何在流中对每个元素进行操作而不返回结果。接着讨论了`peek`作为中间操作的懒执行特性,强调了如果没有终端操作则不会执行的问题。文章指出,在某些情况下使用`peek`可能比`map`更简洁,但也需注意其懒执行带来的影响。
Java Stream中peek和map不为人知的秘密
|
1天前
|
机器学习/深度学习 数据采集 JavaScript
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
ADR药品不良反应监测系统是一款智能化工具,用于监测和分析药品不良反应。该系统通过收集和分析病历、处方及实验室数据,快速识别潜在不良反应,提升用药安全性。系统采用Java开发,基于SpringBoot框架,前端使用Vue,具备数据采集、清洗、分析等功能模块,并能生成监测报告辅助医务人员决策。通过集成多种数据源并运用机器学习算法,系统可自动预警药品不良反应,有效减少药害事故,保障公众健康。
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
|
21天前
|
Java
用JAVA架建List集合为树形结构的代码方法
这段代码定义了一个表示树形结构的 `Node` 类和一个用于构建树形结构的 `TreeController`。`Node` 类包含基本属性如 `id`、`pid`、`name` 和 `type`,以及子节点列表 `children`。`TreeController` 包含初始化节点列表并将其转换为树形结构的方法。通过过滤和分组操作实现树形结构的构建。详情可见:[代码示例链接1](http://www.zidongmutanji.com/zsjx/43551.html),[代码效果参考链接2](https://www.257342.com/sitemap/post.html)。
28 5
|
18天前
|
Java 数据库连接 Apache
Java进阶-主流框架总结与详解
这些仅仅是 Java 众多框架中的一部分。每个框架都有其特定的用途和优势,了解并熟练运用这些框架,对于每一位 Java 开发者来说都至关重要。同时,选择合适框架的关键在于理解框架的设计哲学、核心功能及其在项目中的应用场景。随着技术的不断进步,这些框架也在不断更新和迭代以适应新的开发者需求。
34 1
|
21天前
|
存储 Java 程序员
Java中的集合框架:从入门到精通
【8月更文挑战第30天】在Java的世界里,集合框架是一块基石,它不仅承载着数据的存储和操作,还体现了面向对象编程的精髓。本篇文章将带你遨游Java集合框架的海洋,从基础概念到高级应用,一步步揭示它的奥秘。你将学会如何选择合适的集合类型,掌握集合的遍历技巧,以及理解集合框架背后的设计哲学。让我们一起探索这个强大工具,解锁数据结构的新视角。
|
22天前
|
存储 算法 Java
Java中的集合框架深度解析云上守护:云计算与网络安全的协同进化
【8月更文挑战第29天】在Java的世界中,集合框架是数据结构的代言人。它不仅让数据存储变得优雅而高效,还为程序员提供了一套丰富的工具箱。本文将带你深入理解集合框架的设计哲学,探索其背后的原理,并分享一些实用的使用技巧。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇通往高效编程的大门。
|
20天前
|
存储 算法 Java
Java中的集合框架深度解析与实践
【8月更文挑战第31天】在Java编程的海洋中,集合框架扮演着不可或缺的角色。本文将带你领略Java集合框架的魅力,从理论到实践,深入浅出地探索List、Set和Map等核心接口的使用技巧。我们将通过具体代码示例,展示如何在日常开发中高效运用这些工具,让你的代码更加优雅和高效。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往Java集合世界的大门。
|
20天前
|
存储 人工智能 Java
JAVA集合
【8月更文挑战第31天】
|
22天前
|
安全 Java API
Java 8 流库的魔法革命:Filter、Map、FlatMap 和 Optional 如何颠覆编程世界!
【8月更文挑战第29天】Java 8 的 Stream API 通过 Filter、Map、FlatMap 和 Optional 等操作,提供了高效、简洁的数据集合处理方式。Filter 用于筛选符合条件的元素;Map 对元素进行转换;FlatMap 将多个流扁平化合并;Optional 安全处理空值。这些操作结合使用,能够显著提升代码的可读性和简洁性,使数据处理更为高效和便捷。
28 0