##################################################
目录
##################################################
——————————
下面是 Java 中使用非常频繁的内容 集合框架
将详细讲解 ArrayList、LinkedList 和 HashMap 三种具体的集合类
详解集合的统一遍历工具 Iterator 迭代器
最后讲解使用泛型集合改进集合的使用
如果我们想存储多个信息 可以使用数组实现
但是定义固定长度的数组只能存储固定数量的信息
数组的缺陷是很明显的:
数组长度固定不变 不能很好适应元素数量动态变化的情况
可以通过 数组名.length 获取数组的长度 但是不能直接获取数组中真实存储的元素个数
数组采用在内存中分配连续空间的存储方式 根据下标可以快速获取对应元素的信息 但是根据元素信息查找时效率低下 需要进行多次比较 在进行频繁插入删除等操作时同样效率低下
而且无法存储元素的对应关系
- 例如 在存储人的信息时 希望分别存储人的姓名和信息
- 该两个元素具有一一对应关系 人的姓名作为人的信息的关键存在 可以根据人的姓名获取人的信息
- 这显然无法通过数组来解决!
我们从这些分析中可以看出数组在处理一些问题时存在明显的缺陷
而集合完全弥补了数组的缺陷!
集合比数组更加灵活更实用 可以大大提高软件的开发效率 并且不同的集合可以适用不同的场合
如果写程序时并不知道程序运行需要多少对象 或者需要更复杂的方法存储对象 可以考虑使用 Java 集合来解决
——————————
集合弥补了数组的缺陷 她比数组更灵活实用 可以大大提高软件开发效率 而且不同的集合可以适用于不同场合
Java 集合框架为我们提供了一套性能优良、使用方便的接口和类
她们都位于该包中:
java.util
集合框架是为表示和操作集合而规定的一种统一的标准体系结构
集合框架都包含三大块内容:
对外的接口
接口表示集合的抽象数据类型
例如 Collection、List、Set、Map、Iterator
接口的实现
集合框架中接口的具体实现
如 ArrayList、LinkedList、HashMap、HashSet
对集合运算的算法
在一个实现了某个集合框架中的接口的对象身上完成某种有用的计算方法
例如查找排序等等
Java 提供了进行集合操作的工具类 Collections 注意不是 Collection 类似于 Arrays 类
她提供了对集合进行排序等多种算法实现
在使用 Collections 的时候可以查阅 JDK 帮助文档
Java 集合框架中的两大类接口是 Collection 和 Map
其中 Collection 又有两个子接口 List 和 Set
所以通常说 Java 集合框架共有 3 大类接口:
List
Set
Map
她们的共同点都是集合接口 都可以用来存储很多对象
她们的区别如下:
- Collection 接口存储一组不唯一允许重复、无序的对象
-
- Set 接口继承 Collection 接口 存储一组唯一不允许重复、无序的对象
-
- List 接口继承 Collection 接口存储一组不唯一允许重复、有序以元素插入的次序来放置元素不会重新排列的对象
-
- Map 接口存储一组成对的 键-值 对象 提供 key/键 到 value/值 的映射 Map 中的 key 不要求有序不允许重复 value 同样不要求有序但允许重复
-
- Iterator 接口是负责定义访问和遍历元素的接口
在集合框架中
List 可以理解为前面讲过的数组 元素的内容可以重复并且有序
- List 集合示例:
- 0 1 2 3 4 5 N
- aa dd cc aa ee dd XX
Set 可以理解为数学中的集合 里面数据不重复并且无序
- Set 集合示例:
- aa
- dd cc
Map 也可以理解为数学中的集合 只是其中每个元素都由 key 和 value 两个对象组成
- Map 集合示例:
- CN
- China
-
- RU
- Russia
-
- JP
- Japan
-
- FR
- France
##################################################
——————————
实现 List 接口的常用类有 ArrayList 和 LinkedList
她们都可以容纳所有类型的对象 包括
null
允许重复
保证元素的存储顺序
LinkedList 采用链表存储方式 优点在于插入、删除元素时效率比较高
- ArrayList 存储示例:
- 0 1 2 3 4 5 N
- aa dd cc aa ee dd XX
她提供了额外的 addFirst()、addLast()、removeFirst() 和 removeLast() 等方法
可以在 LinkedList 的首部或尾部进行插入或删除操作
这些方法使得 LinkedList 可以被用作 堆栈/stack 或者 队列/queue
- aa_当前地址 下一个地址_dd
- dd cc_地址
- cc ee_地址
——————————
ArrayList 就是一个可以动态修改的数组 与普通数组的区别就是没有固定大小的限制 我们可以添加或删除元素!
使用集合存储多人信息 获取存储的人的总数
如何按照存储顺序获取各个人的信息并逐条输出相关内容呢?
元素个数不确定 要求获得存储元素的实际个数
按照存储顺序获取并输出元素信息 可以通过 List 接口的实现类 ArrayList 实现该需求
通过 ArrayList 实现该需求的具体步骤如下:
创建多个人对象
创建 ArrayList 集合对象并把多个对象放入其中
输出集合中人的数量
通过遍历集合显示各人的信息
%%%%%
人类 code:
- package testt;
-
- public class Ren {
- /* 人类 */
-
- private String name = "无名氏"; // 姓名
- private Double gao = 1.89; // 身高
-
- public Ren () {
- /* 无参构造方法设置身高 */
-
- this.gao = 2.50;
- System.out.println ( "执行人类的无参构造方法.." );
- }
-
- public Ren(String name) {
- /* 带参构造设置姓名 */
-
- this.name = name;
- }
-
- public String getName() {
- /* 获取姓名方法 */
-
- return this.name;
- }
-
- public double getGao() {
- /* 获取身高方法 */
-
- return this.gao;
- }
-
- public void print () {
- /* 输出个人信息 */
-
- System.out.println ();
- System.out.printf ( "姓名 >>> [%s]\n", this.getName() );
- System.out.printf ( "身高 >>> [%f]\n", this.getGao() );
- }
- }
测试类:
- package testt;
-
- import java.util.List; /* 导入 List 包 */
- import java.util.ArrayList; /* 导入 ArrayList 包 */
-
- public class Testt {
-
- public static void main (String[] args) {
-
- /* 创建四个 人 对象 */
- Ren ren_1 = new Ren ( "李二哈" );
- Ren ren_2 = new Ren ( "赵三枪" );
- Ren ren_3 = new Ren ( "刘四刀" );
- Ren ren_4 = new Ren ( "龙六拳" );
-
- /* 创建 ArrayList 集合对象并把 4 个人对象放入其中 */
- List rens = new ArrayList ();
- rens.add ( ren_1 ); // 按照顺序从 0 下标开始存放元素
- rens.add ( ren_2 );
- rens.add ( ren_3 );
- rens.add ( 1, ren_4 ); // 指定添加 ren_4 这个对象到指定位置 1 这是第二个元素的位置 集合的下标也是从零开始
-
- /* 输出集合中人对象的数量 */
- System.out.println ( "共计有 " + rens.size () + " 个人。" ); // 使用 size 方法获取集合中元素数量
-
- /* 通过遍历集合显示每一个位置的人对象信息 */
- System.out.println ( "\n分别为 >>> " );
- for ( int i = 0; i < rens.size (); i ++ ) {
- /* 判断条件是 size 方法返回的元素数量 */
-
- Ren ren = (Ren) rens.get ( i ); // 新建一个人对象 用来存取从集合中取得的人对象元素 从集合取出的纯对象强制转换为 Ren 类
-
- System.out.printf ( "\t第 %d 个人\t%s\n", (i + 1), ren.getName () );
- }
- }
- }
List 接口的 add (Object o) 方法的参数类型是 Object
即使在调用时实参是 Ren/人 类型但是系统认为里面就是 Object 对象
所以在通过 get (int i) 方法获取元素时必须进行强制类型转换
例如
Ren ren = (Ren) rens.get ( i );
IDE demo:
- 共计有 4 个人。
-
- 分别为 >>>
- 第 1 个人 李二哈
- 第 2 个人 赵三枪
- 第 3 个人 刘四刀
- 第 4 个人 龙六拳
%%%%%
我们刚刚只是使用到了部分的方法 下面我们在这个示例的基础上扩充功能:
删除指定位置的元素 例如下标为 0 的集合中的第一个元素位置的元素
删除指定的元素 例如删除 ren_4 这第四个人对象
判断集合中是否包含指定元素
List 接口中提供了相应方法 直接使用即可!
测试类 code:
- package testt;
-
- import java.util.List;
- import java.util.ArrayList;
-
- public class Testt {
-
- public static void main (String[] args) {
-
- /* 创建四个 人 对象 */
- Ren ren_1 = new Ren ( "李二哈" );
- Ren ren_2 = new Ren ( "赵三枪" );
- Ren ren_3 = new Ren ( "刘四刀" );
- Ren ren_4 = new Ren ( "龙六拳" );
-
- /* 创建 ArrayList 集合对象并把 4 个人对象按照顺序从 0 下标开始存放元素中 */
- List rens = new ArrayList ();
- rens.add ( ren_1 );
- rens.add ( ren_2 );
- rens.add ( ren_3 );
- rens.add ( ren_4 );
-
- System.out.println ( "共计有 " + rens.size () + " 个人。" );
- System.out.println ( "\n分别为 >>> " );
- for ( int i = 0; i < rens.size (); i ++ ) {
-
- Ren ren = (Ren) rens.get ( i );
-
- System.out.printf ( "\t第 %d 个人\t%s\n", (i + 1), ren.getName () );
- }
-
- /* 删除元素 */
- System.out.println ( "\n删除指定元素.." );
- rens.remove ( 0 ); // 删除集合中第一个元素
- rens.remove ( ren_4 ); // 删除集合中指定的元素 这次指定是 ren_4 这个人对象元素
-
- System.out.println ( "删除元素后共计有 " + rens.size () + " 个人。" );
- System.out.println ( "\n分别为 >>> " );
- for ( int i = 0; i < rens.size (); i ++ ) {
-
- Ren ren = (Ren) rens.get ( i );
-
- System.out.printf ( "\t第 %d 个人\t%s\n", (i + 1), ren.getName () );
- }
-
- /* 判断集合中是否包含指定元素 */
- if ( rens.contains ( ren_4 ) )
- System.out.println ( "\n集合中存在 ren_4 对象!" );
- else
- System.out.println ( "\n集合中没有 ren_4 对象.." );
-
- if ( rens.contains ( ren_2 ) )
- System.out.println ( "\n集合中存在 ren_2 对象!" );
- else
- System.out.println ( "\n集合中没有 ren_2 对象.." );
- }
- }
IDE demo:
- 共计有 4 个人。
-
- 分别为 >>>
- 第 1 个人 李二哈
- 第 2 个人 赵三枪
- 第 3 个人 刘四刀
- 第 4 个人 龙六拳
-
- 删除指定元素..
- 删除元素后共计有 2 个人。
-
- 分别为 >>>
- 第 1 个人 赵三枪
- 第 2 个人 刘四刀
-
- 集合中没有 ren_4 对象..
-
- 集合中存在 ren_2 对象!
%%%%%
下面总结一下 List 接口中定义的各种常用方法 也包含 ArrayList 的各种常用方法:
- boolean add (Object o) 在列表末尾顺序添加元素 起始索引位置从 0 开始
- void add (int index, Object o) 在指定的索引位置添加元素 原索引位置以及其后面的元素依次后移
-
- int size () 返回列表中的元素个数
-
- Object get (int index) 返回指定索引位置处的元素 注意 取出的元素是 Object 类型 使用前需要进行强制类型转换!
-
- boolean contains (Object o) 判断列表中是否存在指定元素
-
- boolean remove (Object o) 从列表中删除元素
- Object remove (int index) 从列表中删除指定位置元素 起始索引位置从 0 开始
%%%%%
在 ArrayList 类出现之前 JDK 中存在一个和她同样分配连续存储空间实现了长度可变数组的集合类 Vector
两者实现原理相同 功能相同 在很多情况下都可以互用
Vector 的用法可以查询 API 文档
Vector 是线程安装的
ArrayList 重速度、轻安全 是线程非安全的
所以当运行到多线程环境当中时 需要程序员自己管理线程的同步问题
当长度需要增长时 Vector 默认增长为原来的一倍 而 ArrayList 只增长 50% 有利于节约内存空间
开发过程中最好使用新版本的 ArrayList
——————————
如果说 ArrayList 是高级数组则 LinkedList 就是链表!
如何在集合的头部或尾部添加、获取和删除元素呢?
如何在集合的其她位置添加获取和删除元素呢?
我们刚刚讲解 ArrayList 时涉及了集合中元素的添加、删除操作
可以通过 add ( Object o )、remove ( Object o ) 在集合尾部添加和删除元素
还可以通过 add ( int index, Object o )、remove ( int index ) 实现任意位置元素的添加和删除 当然也包括头部和尾部
但是由于 ArrayList 采用了和数组相同的存储方式 在内存中分屏连续的空间
在添加和删除非尾部元素时会导致后面所有元素的移动 性能低下
所以在插入删除操作比较频繁时 可以考虑使用 LinkedList 来提高效率
在使用 LinkedList 进行头部和尾部元素的添加和删除操作时
除了使用 List 的 add() 和 remove() 之外还可以使用 LinkedList 额外提供的方法来实现操作
%%%%%
下面试试在集合的头部或尾部添加、获取和删除元素!
测试类 code:
- package testt;
-
- import java.util.LinkedList; /* 导入 LinkedList */
-
- public class Testt {
-
- public static void main (String[] args) {
-
- /* 创建四个 人 对象 */
- Ren ren_1 = new Ren ( "李二哈" );
- Ren ren_2 = new Ren ( "赵三枪" );
- Ren ren_3 = new Ren ( "刘四刀" );
- Ren ren_4 = new Ren ( "龙六拳" );
-
- /* 创建 LinkedList 集合对象并把 4 个人对象按照顺序从 0 下标开始存放元素中 */
- LinkedList rens = new LinkedList ();
- rens.add ( ren_1 );
- rens.add ( ren_2 );
- rens.add ( ren_3 );
- rens.add ( ren_4 );
-
- Ren renFirst = (Ren) rens.getFirst (); // 获取集合中第一个元素 从集合中取出元素对象时强制转换为 Ren/人 类
- Ren renLast = (Ren) rens.getLast(); // 获取集合中最后一个元素
-
- System.out.println ( "第 1 个对象为 >>> " + renFirst.getName () );
- System.out.println ( "最后一个对象为 >>> " + renLast.getName () );
-
- System.out.println ( "共计有 " + rens.size () + " 个对象。" );
- System.out.println ( "\n分别为 >>> " );
- for ( int i = 0; i < rens.size (); i ++ ) {
-
- Ren ren = (Ren) rens.get ( i );
-
- System.out.printf ( "\t第 %d 个对象\t%s\n", (i + 1), ren.getName () );
- }
-
- System.out.println ( "\n删除集合中第一个元素" );
- rens.removeFirst();
- System.out.println ( "删除集合中最后元素" );
- rens.removeLast();
-
- System.out.println ( "删除元素后共计有 " + rens.size () + " 个对象。" );
- System.out.println ( "\n分别为 >>> " );
- for ( int i = 0; i < rens.size (); i ++ ) {
-
- Ren ren = (Ren) rens.get ( i );
-
- System.out.printf ( "\t第 %d 个人\t%s\n", (i + 1), ren.getName () );
- }
-
- /* 判断集合中是否包含指定元素 */
- if ( rens.contains ( ren_2 ) )
- System.out.println ( "\n集合中存在 ren_2 对象!" );
- else
- System.out.println ( "\n集合中没有 ren_2 对象.." );
- if ( rens.contains ( ren_4 ) )
- System.out.println ( "集合中存在 ren_4 对象!" );
- else
- System.out.println ( "集合中没有 ren_4 对象.." );
- }
- }
IDE demo:
- 第 1 个对象为 >>> 李二哈
- 最后一个对象为 >>> 龙六拳
- 共计有 4 个对象。
-
- 分别为 >>>
- 第 1 个对象 李二哈
- 第 2 个对象 赵三枪
- 第 3 个对象 刘四刀
- 第 4 个对象 龙六拳
-
- 删除集合中第一个元素
- 删除集合中最后元素
- 删除元素后共计有 2 个对象。
-
- 分别为 >>>
- 第 1 个人 赵三枪
- 第 2 个人 刘四刀
-
- 集合中存在 ren_2 对象!
- 集合中没有 ren_4 对象..
%%%%%
LinkList 除了上面使用的方法之外 还包括一些特殊的方法:
- 在列表的首部添加元素 void addFirst (Object o)
- 在列表的末尾添加元素 void addLst (Object o)
-
- 返回列表中的第一个元素 Object getFirst ()
- 返回列表中的最后一个元素 Object getLast ()
-
- 删除并返回列表中的第一个元素 Object removeFirst ()
- 删除并返回列表中的最后一个元素 Object removeLast ()
##################################################
——————————
Java 集合框架中提供了 Map 接口
专门用来处理键-值映射数据的存储
Map 中可以存储多个元素 每个元素都由两个对象组成 即一个键对象和一个值对象
可以根据键实现对应值的映射
例如 建立国家英文简称和中文全名之间的键-值映射
例如根据缩写 CN 可以查找到 中华人民共和国
通过删除 键 实现 对应值 的删除
%%%%%
测试类 code:
- package testt;
-
- import java.util.Map; /* 导入 Map */
- import java.util.HashMap; /* 导入 HashMap */
-
- public class Testt {
-
- public static void main (String[] args) {
-
- /* 使用 HashMap 存储多组国家英文简称和中文全程的 键-值对 */
- Map countries = new HashMap ();
- countries.put ( "CN", "中华人民共和国" );
- countries.put ( "RU", "俄罗斯联邦" );
- countries.put ( "FR", "法兰西共和国" );
- countries.put ( "US", "美利坚和众国" );
-
- /* 显示 CN 对应国家的中文全称 */
- String country = (String) countries.get ( "CN" ); // 从 countries 中获取 "CN" 键对应的 值 并强制转换为 String 类型
- System.out.println ( "CN 对应的国家是 >>> " + country );
-
- /* 获取集合中的元素个数 */
- System.out.println ( "\nMap 中共有 " + countries.size () + " 组数据。" );
-
- /* 两次判断 Map 中是否存在 "US" 键 */
- System.out.println ( "\nMap 中是否包含 US 的 key >>> " + countries.containsKey ( "US" ) );
- countries.remove ( "US" ); // 删除 "US" 键
- System.out.println ( "删除后 Map 中是否包含 US 的 key >>> " + countries.containsKey ( "US" ) + "\n" );
-
- /* 分别显示键集、值集和键-值对集 */
- System.out.println ( countries.keySet () ); // 输出 键集
- System.out.println ( countries.values () ); // 输出 值集
- System.out.println ( countries ); // 输出 键-值对集
-
- /* 清空 HashMap 并判断是否已清空 */
- countries.clear ();
- if ( countries.isEmpty () )
- System.out.println ( "\n已清空 Map 中数据!" );
- }
- }
IDE demo:
- CN 对应的国家是 >>> 中华人民共和国
-
- Map 中共有 4 组数据。
-
- Map 中是否包含 US 的 key >>> true
- 删除后 Map 中是否包含 US 的 key >>> false
-
- [FR, RU, CN]
- [法兰西共和国, 俄罗斯联邦, 中华人民共和国]
- {FR=法兰西共和国, RU=俄罗斯联邦, CN=中华人民共和国}
-
- 已清空 Map 中数据!
Map 接口存储一组成对的 键-值 对象
提供 key/键 和 value/值 的映射!
Map 中的 key 不要求有序 不允许重复
values 同样不要求有序 但允许重复
最常用的 Map 实现类是 HashMap
她的存储方式是哈希表
哈希表也被称为 散列表
是根据 关键码值/key value 而直接进行访问的数据结构
也就是说 她通过把关键码映射到表中的一个位置来访问记录 以加快查找速度
存放记录的数组称为散列表 使用这种方式存储数据的优点是查询指定元素效率较高!
%%%%%
总结一下上面用到的 Map 接口中定义的各种常用方法 也是 HashMap 的各种常用方法:
- Object put (Object key, Object value); 以 键-值对 的方式进行存储 注意,键必须是唯一的 值可以重复 如果试图添加重复的键 那么最后加入的 键-值对 将替换掉原先的 键-值对!
-
- Object get (Object key) 根据键返回相关联的值 若不存在指定的键 则返回 null
-
- Object remove (Object key) 删除指定的键映射的 键-值对
-
- int size () 返回元素个数
-
- set ketSet () 返回键的集合
-
- Collection values () 返回值的集合
-
- boolean containsKey (Object key) 若存在指定的键映射的 键-值对 则返回 true
- boolean isEmpty () 若不存在 键-值 映射关系 则返回 true
-
- void clear () 从此映射中移除所有映射关系
%%%%%
HashMap 出现之前 JDK 中存在一个和她同样采用哈希表存储方式、同样实现键值映射的集合类 Hashtable
两者实现原理相同 功能相同 很多情况下可以互用
Hashtable 继承自 Dictionary 类
而 HashMap 实现了 Map 接口
Hashtable 是线程安全的
HashMap 重速度、轻安全 是线程非安全的 所以当运行到多线程环境中需要程序员自己管理线程的同步问题
Hashtable 不允许 null 值 包括 key 和 value 都不允许 null 值
HashMap 允许 null 值 包括 key 和 value 都允许!
所以开发过程中最好使用新版本的 HashMap
##################################################
——————————
所有集合接口和类都没有提供相应的遍历方法
而是把遍历交給迭代器 Iterator 完成
Iterator 为集合而生 专门实现集合的遍历
Irerator 隐藏了各种集合实现类的内部细节 提供了遍历集合的统一编程接口
Collection 接口的 iterate() 返回一个 Iterator
然后通过 Iterator 接口的两个方法即可方便地实现遍历
- boolean hasNext() 判断是否存在另一个可访问的元素
- Object next() 返回要访问的下一个元素
我们之前通过 for 循环和 get() 配合实现了 List 中元素的遍历
下面我们测试通过 Iterator 实现遍历
Ren 类 code:
- package testt;
-
- public class Ren {
- /* 人类 */
-
- private String name = "无名氏"; // 姓名
- private Double gao = 1.89; // 身高
-
- public Ren () {
- /* 无参构造方法设置身高 */
-
- this.gao = 2.50;
- System.out.println ( "执行人类的无参构造方法.." );
- }
-
- public Ren(String name, Double gao) {
- /* 带参构造设置姓名和身高 */
-
- this.name = name;
- this.gao = gao;
- }
-
- public String getName() {
- /* 获取姓名方法 */
-
- return this.name;
- }
-
- public double getGao() {
- /* 获取身高方法 */
-
- return this.gao;
- }
-
- public void print () {
- /* 输出个人信息 */
-
- System.out.println ();
- System.out.printf ( "姓名 >>> [%s]\n", this.getName() );
- System.out.printf ( "身高 >>> [%f]\n", this.getGao() );
- }
- }
测试类 code:
- package testt;
-
- import java.util.Map;
- import java.util.HashMap;
- import java.util.Set; /* 导入 Set */
- import java.util.Iterator; /* 导入 Iterator */
-
- public class Testt {
-
- public static void main (String[] args) {
-
- /* 创建多个人对象 */
- Ren ren_1 = new Ren ( "李哈哈", 3.14 );
- Ren ren_2 = new Ren ( "李二哈", 5.21 );
- Ren ren_3 = new Ren ( "李恰恰", 5.12 );
- Ren ren_4 = new Ren ( "崽了你", (double) 23 ); // 因为要接收 double 所以需要转换一下 等同与直接传参 23.0
-
- /* 创建 Map 集合对象并把多人对象放入其中 */
- Map rensMap = new HashMap ();
- rensMap.put ( ren_1.getName(), ren_1 ); // 键为 人对象的名字 值为人对象 以后就可以根据名字找到人对象的信息
- rensMap.put( ren_2.getName(), ren_2 );
- rensMap.put( ren_3.getName(), ren_3 );
- rensMap.put( ren_4.getName(), ren_4 );
-
- /* 通过迭代器依次输出集合中所有人的信息! */
- System.out.println ( "使用 Iterator 遍历, 所有人的昵称和身高分别为 >>> " );
- Set keys = rensMap.keySet(); // 取出所有 key 键集
- Iterator it = keys.iterator (); // 获取 Iterator 对象
- while ( it.hasNext () ) {
- /* 取出 key */
-
- String key = (String) it.next (); // 取出 key 并强制转换为 String 类型
- Ren ren = (Ren) rensMap.get(key); // 根据 key 键取出对应的 value 值
- System.out.printf ( "\t%s\t%.2f\n", key, ren.getGao() );
- }
- }
- }
IDE demo:
- 使用 Iterator 遍历, 所有人的昵称和身高分别为 >>>
- 崽了你 23.00
- 李二哈 5.21
- 李恰恰 5.12
- 李哈哈 3.14
——————————
JDK 1.5 加入了增强型 for 循环 是 for 语句的特殊简写版本
我们通常称之为 foreach 语句
她在遍历数组、集合等方面提供了极大的方便
foreach 语句的格式如下:
- for ( 元素类型 t 元素变量 x : 数组或集合对象 ) {
-
- 引用了 x 的 java 语句
- }
其中 t 的类型必须属于 数组或集合对象 的元素类型
我们刚刚使用了 Iterator 遍历 Map 集合 现在使用 foreach 语句遍历已经存储数据的 Map 对象
测试类 code:
- package testt;
-
- import java.util.Map;
- import java.util.HashMap;
- import java.util.Set;
- import java.util.Iterator;
-
- public class Testt {
-
- public static void main (String[] args) {
-
- Ren ren_1 = new Ren ( "李哈哈", 3.14 );
- Ren ren_2 = new Ren ( "李二哈", 5.21 );
- Ren ren_3 = new Ren ( "李恰恰", 5.12 );
- Ren ren_4 = new Ren ( "崽了你", (double) 23 );
-
- Map rensMap = new HashMap ();
- rensMap.put ( ren_1.getName(), ren_1 );
- rensMap.put( ren_2.getName(), ren_2 );
- rensMap.put( ren_3.getName(), ren_3 );
- rensMap.put( ren_4.getName(), ren_4 );
-
- /* 通过迭代器依次输出集合中所有人的信息! */
- System.out.println ( "使用 foreach 遍历, 所有人的昵称和身高分别为 >>> " );
- Set keys = rensMap.keySet (); // 获取所有 key 集合
- for ( Object key : keys ) {
-
- Ren r = (Ren) rensMap.get (key); // 根据 key 取出对应的 value 值
- System.out.println ( "\t" + key + "\t" + r.getGao() );
- }
- }
- }
IDE demo:
- 使用 foreach 遍历, 所有人的昵称和身高分别为 >>>
- 崽了你 23.0
- 李二哈 5.21
- 李恰恰 5.12
- 李哈哈 3.14
——————————
测试类 code:
- package testt;
-
- import java.util.List; /* 导入 List */
- import java.util.ArrayList; /* 导入 ArrayList */
- import java.util.Iterator; /* 导入 Iterator */
-
- public class Testt {
-
- public static void main (String[] args) {
-
- /* 创建多人对象 */
- Ren ren_1 = new Ren ( "李哈哈", 3.14 );
- Ren ren_2 = new Ren ( "李二哈", 5.21 );
- Ren ren_3 = new Ren ( "李恰恰", 5.12 );
- Ren ren_4 = new Ren ( "崽了你", (double) 23 );
-
- /* 创建 ArrList 集合对象并把多个人对象放入其中 */
- List rens = new ArrayList ();
- rens.add ( ren_1 );
- rens.add ( ren_2 );
- rens.add ( ren_3 );
- rens.add ( 1, ren_4 );
-
- /* 通过迭代器依次输出集合中所有人对象的信息 */
- System.out.println ( "使用 Iterator 遍历,所有人的昵称和身高分别是 >>> " );
- Iterator its = rens.iterator ();
- while ( its.hasNext () ) {
-
- Ren r = (Ren) its.next ();
- System.out.println ( "\t" + r.getName() + "\t" + r.getGao() );
- }
- }
- }
IDE demo:
- 使用 Iterator 遍历,所有人的昵称和身高分别是 >>>
- 李哈哈 3.14
- 崽了你 23.0
- 李二哈 5.21
- 李恰恰 5.12
——————————
集合框架是为表示和操作集合而规定的一种同一的标准体系结构
集合框架包含 3 大块内容
对外的接口
接口的实现
对集合运算的算法
通常说 Java 的集合框架中有两大类接口 Collection 和 Map
其中 Collection 有两个子接口 即 List 和 Set 接口
各接口区别如下:
Collection 接口存储一组不唯一 无序的对象
Set 接口继承 Collectio 接口 存储一组不唯一 无序的对象
List 接口继承 Collection 接口 存储一组不唯一 有序的对象
Map 接口存储一组承兑的 键-值 对象 提供 key 到 value 的映射 key 不要求有序 不允许重复 value 同样不要求有序 但允许重复
ArrayList 和数组采用相同的存储方式 她的优点在于遍历元素和随机访问元素的效率比较高
LinkedList 采用链表存储方式 优点在于插入、删除元素时效率比较高
HashMap 是最常见的 Map 实现类 她的存储方式是哈希表 优点是查询指定元素效率高
Iterator 为集合而生 专门实现集合的遍历 她隐藏了各种集合实现类的内部细节 提供了遍历集合的统一编程接口
##################################################
——————————
数组和集合的主要区别包括如下几个方面:
数组可以存储基本数据类型和对象
而集合只能存储对象 可以以包装类形式存储基本数据类型
数组长度固定
而集合长度可以动态改变
定义数组时必须制定数组元素类型
集合默认其中所有元素都是 Object
无法直接获取数组实际存储元素的元素个数 length 用来获取数组的长度
但可以通过 size() 直接获取集合实际存储的元素个数
集合有多种实现方式和不同的使用场合
而不像数组仅仅采用分配连续空间方式
集合以接口和类的形式存在 具有封装、继承和多态等典型的特性
通过简单的方法和属性即可实现各种复杂操作
大大提高了软件的开发效率
JDK 中有一个 Arrays 类 专门用来操作数组
她提供一系列静态方法实现对数据搜索、排序比较和填充等操作
JDK 中有一个 Collections 类 专门用来操作集合
她提供一系列静态方法实现对各种集合的搜索、复制、排序和线程安全化等操作
——————————
前面已经提到 Collection 的 add (Object obj) 方法的参数是 Object 类型
无论把什么对象放入 Collection 及其子接口或实现类中 认为只是 Object 类型
在通过 get (int index) 方法取出集合中元素时必须进行强制类型转换
不仅繁琐而且容易出现 ClassCastException 异常
Map 中使用 put (Object key, Object value) 和 get (Object key) 存储对象时
使用 Iterator 的 next 方法获取元素时也存在同样的问题
JDK 1.5 中通过引入 泛型/Generic 有效解决了这个问题!
在 JDK 1.5 中已经改写了集合框架中的所有接口和类 增加了泛型的支持
使用泛型集合在创建集合对象时指定集合中元素的类型 从集合中取出元素时无须进行类型强制转换
并且如果把非指定类型对象放入集合 会出现编译错误
%%%%%
测试类 code:
- package testt;
-
- import java.util.List; /* 导入 List */
- import java.util.ArrayList; /* 导入 ArrayList */
-
- public class Testt {
-
- public static void main (String[] args) {
-
- /* 创建多人对象 */
- Ren ren_1 = new Ren ( "李哈哈", 3.14 );
- Ren ren_2 = new Ren ( "李二哈", 5.21 );
- Ren ren_3 = new Ren ( "李恰恰", 5.12 );
- Ren ren_4 = new Ren ( "崽了你", 23.0 );
-
- List
rs = new ArrayList (); // 标记元素类型 - rs.add ( ren_1 );
- rs.add ( ren_2 );
- rs.add ( ren_3 );
- rs.add ( 1, ren_4 );
- /* rs.add ( "字符串" ); // 此时出现编译错误 元素类型不是 Ren */
-
- /* 显示第 3 个元素的信息 */
- Ren r_3 = rs.get( 2 ); // 因为下标索引从 0 开始所以第三个位置的下标是 2
- System.out.printf ( "第三个人的信息如下 >>> \n\t%s\t%f\n", r_3.getName (), r_3.getGao() );
-
- /* 使用 foreach 语句遍历 rs 对象 */
- System.out.println ( "\n所有人的信息如下 >>> " );
- for ( Ren r : rs )
- /* 再也无须类型强制转换! */
- System.out.println ( "\t" + r.getName () + "\t" + r.getGao () );
- }
- }
IDE demo:
- 第三个人的信息如下 >>>
- 李二哈 5.210000
-
- 所有人的信息如下 >>>
- 李哈哈 3.14
- 崽了你 23.0
- 李二哈 5.21
- 李恰恰 5.12
%%%%%
使用 Iterator 遍历版本 code:
- package testt;
-
- import java.util.Map; /* 导入 Map */
- import java.util.HashMap; /* 导入 HashMap */
- import java.util.Set; /* 导入 Set */
- import java.util.Iterator; /* 导入 Iterator */
-
- public class Testt {
-
- public static void main (String[] args) {
-
- /* 创建多人对象 */
- Ren ren_1 = new Ren ( "李哈哈", 3.14 );
- Ren ren_2 = new Ren ( "李二哈", 5.21 );
- Ren ren_3 = new Ren ( "李恰恰", 5.12 );
- Ren ren_4 = new Ren ( "崽了你", 23.0 );
-
- /* 创建 Map 集合对象并把多个人对象放入其中 */
- Map
rMap = new HashMap (); - rMap.put (ren_1.getName(), ren_1);
- rMap.put (ren_2.getName(), ren_2);
- rMap.put (ren_3.getName(), ren_3);
- rMap.put (ren_4.getName(), ren_4);
-
- /* 通过迭代器一次输出集合中所有人的信息 */
- System.out.println ( "使用 Iterator 遍历,所有人的昵称和品种分别是 >>> " );
- Set
keys = rMap.keySet(); // 取出所有 key 的集合 - Iterator
it = keys.iterator (); // 获取 Iterator - while ( it.hasNext () ) {
-
- String key = it.next (); // 取出 key 而无需类型强制转换
- Ren r = rMap.get ( key ); // 根据 key 取出对应的值而无需类型强制转换
- System.out.println ( "\t" + key + "\t" + r.getGao() );
- }
- }
- }
IDE demo:
- 使用 Iterator 遍历,所有人的昵称和品种分别是 >>>
- 崽了你 23.0
- 李二哈 5.21
- 李恰恰 5.12
- 李哈哈 3.14
使用 foreach 语句输出集合中所有人的信息 code:
- package testt;
-
- import java.util.Map; /* 导入 Map */
- import java.util.HashMap; /* 导入 HashMap */
- import java.util.Set; /* 导入 Set */
-
- public class Testt {
-
- public static void main (String[] args) {
-
- /* 创建多人对象 */
- Ren ren_1 = new Ren ( "李哈哈", 3.14 );
- Ren ren_2 = new Ren ( "李二哈", 5.21 );
- Ren ren_3 = new Ren ( "李恰恰", 5.12 );
- Ren ren_4 = new Ren ( "崽了你", 23.0 );
-
- /* 创建 Map 集合对象并把多个人对象放入其中 */
- Map
rMap = new HashMap (); - rMap.put (ren_1.getName(), ren_1);
- rMap.put (ren_2.getName(), ren_2);
- rMap.put (ren_3.getName(), ren_3);
- rMap.put (ren_4.getName(), ren_4);
-
- /* 通过迭代器一次输出集合中所有人的信息 */
- System.out.println ( "使用 foreach 遍历,所有人的昵称和品种分别是 >>> " );
- Set
keys = rMap.keySet (); // 取出所有 key 的集合 - for ( String key : keys ) {
-
- Ren r = rMap.get( key ); // 根据 key 取出对应值而无需类型强制转换
- System.out.println ( "\t" + key + "\t" + r.getGao () );
- }
- }
- }
IDE demo:
- 使用 foreach 遍历,所有人的昵称和品种分别是 >>>
- 崽了你 23.0
- 李二哈 5.21
- 李恰恰 5.12
- 李哈哈 3.14
%%%%%
使用泛型集合在创建集合对象时指定集合中元素的类型
在集合中取出元素时无需进行类型强制转换 避免了 ClassCastException 异常
查阅 JDK 帮助文档中集合的接口和类 如下
Java API 中 List 接口:
- java.util
- 接口 List
-
- 所有超级接口。
- Collection
, Iterable
Java API 中 Map 接口:
- java.util
- 接口 Map
-
- 类型参数。
- K - 此映射所维护的键的类型
- V - 映射值的类型
——————————
看看下面的代码 其中定义的 ArrayList 集合中 泛型为 Object
向这个集合中存储的都是基本数据类型 但是程序为什么不报错呢?
- ArrayList
- list.add ( 52 );
- list.add ( 13.14 );
- list.add ( '崽' );
- list.add ( false );
因为代码中使用了包装类!
包装类是指将基本类型封装到一个类中 也就是将基本类型包装成一个类类型
Java 程序设计语言时为每一种基本类型都提供了一个包装类 而这些包装类都在 java.lang 包中
共有 8 个包装类 如下:
- Object
- Boolean
- Number
- Byte
- Short
- Integer
- Long
- Float
- Double
- Character
Boolean 类包装布尔类型
Character 包装的是字符类型
包装所有数字类型的类都继承自 Number 类
Number 是一个抽象类 包装了 byte、short、int、long、float、double 等数字类型
并实现她所定义的方法 这些方法以不同数字格式返回对象的值!
%%%%%
语法格式如下:
public Type (type value)
Type 表示包装类
type 代表基本类型
示例 code 如下:
- Integer intValue = new Integer ( 52 );
- Long longValue = new Long ( 521L );
- Character charValue = new Character ( '仔' );
- Boolean booleanValue = new Boolean ( true );
- Float floatValue = new Float ( 1314F );
注意 一旦创建了某个包装类对象 该对象所代表的值就不能改变了!
——————————
包装类 转换 基本类型 语法格式如下:
public type typeValue ();
示例代码如下:
- Integer integerId = new Integer ( 1314 );
- int intId = integerId.intValue ();
- Boolean bl = Boolean.valueOf ( true );
- boolean bool = bl.booleanValue ();
字符串 转换 基本类型 语法格式如下:
public static type parseType (String type)
示例代码如下:
- int num = Integer.parseInt ( "521" );
- bool = Boolean.parseBoolean ( "false" );