旅游网站技术方案,河南省建设工会网站,镇江网站设计,长安仿做网站Java基础教程之集合体系 上 #x1f539;本章学习目标1️⃣ 类集框架介绍2️⃣ 单列集合顶层接口#xff1a;Collection3️⃣ List 子接口3.1 ArrayList 类#x1f50d; 数组#xff08;Array#xff09;与列表#xff08;ArrayList#xff09;有什么区别?3.2 LinkedL… Java基础教程之集合体系 · 上 本章学习目标1️⃣ 类集框架介绍2️⃣ 单列集合顶层接口Collection3️⃣ List 子接口3.1 ArrayList 类 数组Array与列表ArrayList有什么区别?3.2 LinkedList 类3.3 Vector 类3.4 Stack 类3.5 List 各子类间的区别及联系 4️⃣ Set 子接口4.1 关于数据排序的说明4.2 关于重复元素的说明 5️⃣ 取出集合元素5.1 迭代输出Iterator5.2 双向迭代Listlterator5.3 foreach 输出5.4 Enumeration 输出 总结 送书活动 本章学习目标
掌握 Java 设置类集的主要目的以及核心接口的使用掌握 Collection 接口的作用及主要操作方法掌握 Collection 子接口 List、Set 的区别及常用子类的使用掌握 Map 接口的定义及使用掌握集合的4种输出操作语法结构掌握 Properties类的使用了解类集工具类 Collections 的作用
1️⃣ 类集框架介绍
在实际的开发中几乎所有项目都会使用到数据结构核心意义在于解决数组的固定长度限制问题所以为了开发者的使用方便 JDK1.0 提供了 Vector、Hashtable、Enumeration 等常见的数据结构实现类。而从 JDK1.2 开始 Java 又进一步完善了自己的可用数据结构操作提出了完整的类集框架的概念。下面将为大家讲解在类集框架之中较为常见的接口与类的使用。
在实际的项目开发中 一定会出现保存多个对象的操作根据之前学习的知识来讲此时会使用对象数组的概念。但是传统的对象数组有一个问题长度是固定的。因为此缺陷所以数组一般不会使用而为了可以动态地实现多个对象的保存可以利用链表来实现一个动态的对象数组但是对于链表的数据结构编写会存在以下一些问题。
由于需要处理大量的引用关系如果要开发链表工具类对初学者而言难度较高为了保证链表在实际的开发中可用在编写链表实现时必须更多地考虑到性能问题链表为了可以保存任意对象类型统一使用了 Object 类型进行保存。那么所有要保存的对象必会发生向上转型而在进行对象信息取出时又必须强制性地向下转型操作。如果在一个链表中所保存的数据不是某种类型这样的操作会带来安全隐患。
综上可以得出一个结论如果在开发项目里面由开发者自己去实现一个链表那么不仅加大了项目的开发难度并且没必要。因为在所有的项目里面都会存在数据结构的应用在 Java 设计之初就考虑到了此类问题所以提供了一个与链表类似的工具类——Vector (向量类)。但是随着时间的推移这个类并不能很好地描述出所需要的数据结构所以 Java 2(JDK1.2 之后) 提供了一个专门实现数据结构的开发框架——类集框架 (所有的程序接口与类都保存在 java.util 包中)。在JDK 1.5之后泛型技术的引入又解决了类集框架中所有的操作类型都使用 Object 所带来的安全隐患。而 JDK1.8又针对类集的大数据操作环境推出了数据流的分析操作功能 (MapReduce 操作)。
类集在整个Java 中最为核心的用处就在于其实现了动态对象数组的操作并且定义了大量的操作标准。在整个类集框架中其核心接口为 Collection、List、Set、Map、Iterator、Enumeration等。
2️⃣ 单列集合顶层接口Collection
java.util.Collection 是进行单对象保存的最大父接口即每次利用 Collection 接口都只能保存一个对象信息。单对象保存顶层接口定义如下。
public interface CollectionE extends IterableE通过定义可以发现 Collection 接口中使用了泛型这样可以保证集合中操作数据类型的统一同时 Collection 接口属于 Iterable 的子接口。
随着Java 版本的不断提升Collection接口经历从无到有的使用以及定义结构的不断加强需要对 Collection 接口的发展进行以下几点说明。
Collection 接口是从JDK 1.2开始定义的最初所有的操作数据都会使用 Object 进行接收这样就会存在向下转型的安全隐患JDK 1.5之后为了解决 Object 所带来的安全隐患使用泛型重新定义了 Collection 接口同时为了进一步定义迭代操作的标准增加了 Iterable接口(JDK 1.5时增加)使得Collection 接口又多了一个父接口JDK 1.8之后引入了 static与 default 定义接口方法的定义所以在 Collection 接口中的方法又得到了进一步扩充(主要是为了进行数据流操作)。
在 Collection接口里面定义了9个常用操作方法如下表所示。
方法名称类型描述public boolean add(E e)普通向集合里面保存数据public boolean addAll(Collection? extends E c)普通追加一个集合public void clear()普通清空集合根元素为nullpublic boolean contains(Object o)普通判断是否包含指定的内容需要 equals()支持public boolean isEmpty()普通判断是否是空集合(不是null)public boolean remove(Object o)普通删除对象需要equals()支持public int size()普通取得集合中保存的元素个数public Object[] toArray()普通将集合变为对象数组保存public IteratorE iterator()普通为Iterator接口实例化(Iterable接口定义)
对于上表所列出的方法大家应该要记住 add()与 iterator() 两个方法因为这两个方法几乎所有的项目都会使用到同时在进行 contains() 与 remove() 两个方法的操作时必须保证类中已经成功地覆写了 Object 类中的equals() 方法否则将无法正常完成操作。
虽然 Collection 是单对象集合操作的顶层父接口但是 Collection 接口本身却存在一个问题无法区分保存的数据是否重复。所以在实际的开发中往往会使用 Collection 的两个子接口 List 子接口 (数据允许重复)、 Set 子接口(数据不允许重复)继承关系如下所示。 图1 Collection 及其子接口继承关系 3️⃣ List 子接口
List 子接口最大的功能是里面所保存的数据可以存在重复内容并且在 Collection 的子接口中List 子接口是最为常用的一个子接口在 List 接口中对 Collection 接口的功能进行了扩充。 List子接口扩充的方法如下所示。
方法名称类型描述public E get(int index)普通取得索引编号的内容public E set(int index, E element)普通修改指定索引编号的内容public ListIteratorE listIterator()普通为ListIterator接口实例化
在使用 List 接口时主要使用 ArrayList 或 LinkedList 两个子类来进行接口对象的实例化操作。List 接口的继承体系如下所示接下来我们会一一介绍ArrayList 类、 LinkedList 类、Vector类、Stack类。 图2 List 及其子接口继承关系 3.1 ArrayList 类
ArrayList 子类是 List子接口中最为常用的一个子类下面通过 ArrayList 类来实现 List 接口的操作。
// 范例 1: List 基本操作
package com.xiaoshan.demo;
import java.util.ArrayList;
import java.util.List;public class TestDemo {public static void main(String[] args){//从JDK1.5 开始应用了泛型从而保证集合中所有的数据类型都一致ListString all new ArrayListString(); // 实例化 List 集合System.out.println(长度 all.size() ,是否为空 all.isEmpty());all.add(Hello); //保存数据all.add(Hello); //保存重复元素all.add(World); //保存数据System.out.println(长度 alL.size() ,是否为空 all.isEmpty());// Collection 接口定义size()方法取得了集合长度 List子接口扩充get()方法根据索引取得了数据 for (int x0; xall.size();x){String str all.get(x); //取得索引数据System.out.println(str); //直接输出内容}}
}程序执行结果
长度0,是否为空true
长度3,是否为空false
Hello
Hello
World此程序通过 ArrayList 子类实例化了 List 接口对象这样就可以使用 List 接口中定义的方法 (包括 Collection 接口定义的方法)由于List 接口相对于 Collection 接口中扩充了 get()方法所以可以利用循环的方式依次取出集合中的每一个保存数据。 数组Array与列表ArrayList有什么区别? 数组(Array)中保存的内容是固定的而列表ArrayList中保存的内容是可变的。在很多时候列表(ArrayList) 进行数据保存与取得时需要一系列的判断而如果是数组(Array) 只需要操作索引即可。 如果在已经确定好长度的前提下完全可以使用数组Array来替代数组列表 (ArrayList)但是如果集合保存数据的内容长度是不固定的那么就使用 ArrayList。 另外在许多开发框架中会将数组与List 集合作为同一种形式。例如在 Mybatis 框架中的参数及结果映射中如果是数组或者是List集合其最终的结果是完全相同的。关于这一点大家可以随着技术的深入而有更深的体会。 ArrayList 是List接口的子类所以也就是 Collection接口的子类这样就可以利用 ArrayList 为 Collection 接口实例化(大部分情况下这样的操作不会出现)但是如果直接使用 Collection接口对象将不具备 get()方法只能将全部集合转化为对象数组后才可以使用循环进行输出。
// 范例 2: Collection接口实例化操作
package com.xiaoshan.demo;
import java.util.ArrayList;
import java.util.Collection;public class TestDemo {public static void main(String[] args){CollectionString all new ArrayListString();all.add(Hello); //保存数据all.add(Hello); //重复元素all.add(World); //保存数据//Collection不具备List接口的get()方法所以必须将其转化为对象数组Object obj[] all.toArray(): //变为对象数组for (int x0; xobj.length; x){ //采用循环输出String str (String) obj[x]; //强制向下转型System.out.println(str); //输出数据}}
} 程序执行结果
Hello
Hello
World可以发现Collection与 List接口的最大区别在于List 提供了get() 方 法这样就可以根据索引取得内容在实际的开发中此方法使用较多。但是需要提醒大家的是范例2的输出操作并不是集合的标准输出操作具体的输出操作在下文讲解。
范例2的操作是在集合中保存了String 类的对象然而对于集合的操作也可以保存自定义对象。而如果要正确地操作集合中的 remove() 或 contains() 方法则必须保证自定义类中明确地覆写了 equals() 方法。
// 范例 3: 在集合里面保存对象
package com.xiaoshan.demo;
import java util.ArrayList;
import java.util.List;class Book { //创建一个自定义类private String title;private double price;public Book(String title, double price){this.title title;this.price price;}Overridepublic boolean equals(Object obj){ //必须覆写此方法否则remove()、contains()无法使用if (this obj){return true;}if (obj null){return false;}if (!(obj instanceof Book)){return false;}Book book (Book) obj;if (this.title.equals(book.title) this.price book.price){return true;}return false;}Overridepublic String toString(){return 书名this.title,价格this.price\n;}
}public class TestDemo {public static void main(String[] args){ListBook all new ArrayListBook();all.add(new Book(Java开发入门教程, 129.9)); //保存自定义类对象all.add(new Book(Java开发实战经典, 69.8)); all.add(new Book(Oracle开发实战经典, 89.8));all.remove(new Book(Oracle开发实战经典, 89.8)); //需要使用equals()方法System.out.println(all);}
}程序执行结果
[书名Java开发入门教程,价格129.9,
书名Java开发实战经典,价格69.8]此程序实现了自定义类对象的保存由于设置的泛型限制所以在集合保存数据操作中只允许保存 Book 类对象同时为了可以使用集合中的 remove() 方法 在 Book 类中必须明确覆写 equals() 方法。
3.2 LinkedList 类
在List子接口中还存在一个LinkedList子类而使用时大部分情况下都是利用子类为父接口实例化。ArrayList和LinkedList的主要区别如下
ArrayList 中采用顺序式的结果进行数据的保存并且可以自动生成相应的索引信息LinkedList 集合保存的是前后元素也就是说它每一个节点中保存的是两个元素对象 一个它对应的下一个节点以及另外一个它对应的上一个节点所以 LinkedList 要占用比 ArrayList 更多的内存空间。同时 LinkedList比ArrayList 多实现 了一个Queue队列数据接口。
// 范例 4: 观察 LinkedList 的使用
public class TestDemo {public static void main(String[] args) {// 创建一个LinkedList对象LinkedListBook books new LinkedList();// 添加元素到列表的末尾books.add(new Book(Java开发入门教程, 129.9)); //保存自定义类对象books.add(new Book(Java开发实战经典, 69.8));books.add(new Book(Oracle开发实战经典, 89.8));// 在列表指定位置插入元素books.add(1, new Book(Oracle从入门到放弃, 328.8));// 获取列表中的第一个元素Book firstBook books.getFirst();// 移除列表中的最后一个元素Book lastBook books.removeLast();// 遍历LinkedList并打印所有元素System.out.println(LinkedList元素: books);// 打印首个和最后一个元素System.out.println(首个元素: firstBook);System.out.println(最后一个元素: lastBook);}
}程序执行结果
LinkedList元素:[书名Java开发入门教程,价格129.9
, 书名Oracle从入门到放弃,价格328.8
, 书名Java开发实战经典,价格69.8
]
首个元素: 书名Java开发入门教程,价格129.9最后一个元素: 书名Oracle开发实战经典,价格89.8
在上边代码中我们首先创建了一个LinkedListBook类型的对象然后使用add()方法向列表添加元素。还通过指定索引位置使用add()方法将元素插入列表中。接下来使用getFirst()方法获取列表中的第一个元素使用removeLast()方法从列表中移除最后一个元素。
最后打印出每个元素首个及最后一个元素。从输出结果中可以看出我们成功地创建了一个含有几个元素的LinkedList并且能够插入、移除和遍历它们。
3.3 Vector 类
在 JDK 1.0 时就已经提供了Vector 类(当时称为向量类)同时由于其提供的较早这个类被大量使用。但是到了 JDK 1.2时由于类集框架的引入对于整个集合的操作就有了新的标准为了可以继续保留 Vector 类就让这个类多实现了一 个List 接口 。
// 范例 5: 使用Vector
package com.xiaoshan.demo;
import java.util.List;
import java.util.Vector;public class TestDemo{public static void main(String[] args){//由于都是利用子类为父类实例化所以不管使用哪个子类 List接口功能不变ListString all new VectorString(); //实例化 List集合System.out.println(长度 all.size(), 是否为空 all.isEmpty());all.add(Hello); //保存数据all.add(Hello); //保存重复元素all.add(World);System.out.println(长度 all.size(), 是否为空 all.isEmpty()); // Collection接口定义了size()方法取得集合长度 List子接口扩充了get()方法根据索引取得数据for (int x0; xall.size(); x){String str all.get(x); //取得索引数据 System.out.println(str); //直接输出内容}}
}程序执行结果
长度0,是否为空true
长度3,是否为空false
Hello
Hello
World此程序只是将 ArrayList 子类替换为 Vector 子类由于最终都是利用子类实例化 List 接口对象 所以最终的操作结果并没有区别而两个操作子类最大的区别在于 Vector 类中的部分方法使用 synchronized 关键字声明也就是说类中操作都是同步操作。
3.4 Stack 类
Stack 类表示栈栈也是一种动态对象数组采用的是一种先进后出的数据结构形式即在栈中最早保存的数据最后才会取出而最后保存的数据可以最先取出如下所示。 图3 入栈与出栈 在 java.util 包中可以利用 Stack 类实现栈的功能此类定义如下。
public class StackE extends VectorE通过定义可以发现 Stack 类属于 Vector 子类但是需要注意的是在进行 Stack 类操作时不会使用 Vector 类定义的方法主要使用 Stack 自己定义的方法。 Stack 类的常用方法如下所示。
方法类型描述public E push(E item)普通数据入栈public E pop()普通数据出栈如果栈中没有数据则调用此方法会抛出空栈异常(EmptyStackException)
// 范例 6: 观察栈的操作
package com.xiaoshan.demo;
import java.util.Stack;public class TestDemo {public static void main(String[] args){StackString all new StackString();all.push(www.baidu.com);all.push(www.xiaoshan.com);all.push(www.ccc.cn);System.out.println(all.pop());System.out.println(all.pop());System.out.println(all.pop());System.out println(all.pop()); // EmptyStackException}
}程序执行结果
www.ccc.cn
www.xiaoshan.com
www.baidu.com
Exception in threadmainjava.util.EmptyStackExceptionat java.util.Stack.peek(Unknown Source)at java.util.Stack.pop(Unknown Source)at com.xiaoshan.demo.TestDemo.main(TestDemo.java:14)程序利用 Stack 类的 push() 方法向栈中保存数据而取得数据时只需要利用 pop()方法即可实现出栈操作如果栈中没有任何数据进行出栈操作时则将抛出 “EmptyStackException” 异常。
3.5 List 各子类间的区别及联系
ArrayListArrayList是基于数组实现的动态数组它可以根据需要自动调整大小。它提供了快速的随机访问和高效的插入/删除操作。ArrayList允许存储重复元素并且继承了AbstractList抽象类LinkedListLinkedList是基于双向链表实现的列表。它仅保留到前后节点的引用在插入/删除操作时具有更好的性能。与ArrayList相比LinkedList适用于频繁的插入/删除操作但对于随机访问较慢它也允许存储重复元素VectorVector是一个线程安全的列表实现与ArrayList类似但它的所有方法都是同步的。它提供了与ArrayList相似的功能不过由于同步的开销通常比ArrayList的性能稍差。由于Java 2的引入推荐使用ArrayList而不是VectorStackStack是一个基于后进先出LIFO原则的列表它继承自Vector类。它提供了常规的栈操作如push将元素推入栈顶、pop将元素弹出栈顶和peek查看栈顶元素。
这些类都位于Java集合框架Java Collections Framework中通过它们可以方便地对列表进行操作。每个类都有其自己的特性和适用场景根据实际需求选择适当的类是很重要的。
4️⃣ Set 子接口
在 Collection 接口下又有另外一个比较常用的子接口为 Set 子接口但是 Set 子接口并不像 List 子接口那样对 Collection 接口进行了大量的扩充而是简单地继承了接口。也就是说在Set 子接口里面无法使用 get()方法根据索引取得保存数据的操作。在Set 子接口下有两个常用的子类 HashSet、TreeSet。
HashSet是散列存放数据而TreeSet是有序存放的子类。在实际的开发中如果要使用TreeSet子类则必须同时使用比较器的概念而 HashSet子类相对于TreeSet子类更加容易一些所以如果没有排序要求应优先考虑 HashSet子类。
// 范例 7: 观察 HashSet 子类的特点
package com.xiaoshan.demo;
import java.util.HashSet;
import java.util.Set;public class TestDemo{public static void main(String[] args){SetString all new HashSetString(); //实例化Set接口all.add(abcdefg); //保存数据all.add(小山)all.add(HELLO)all.add(小山); //重复数据System.out.println(all); //直接输出集合}
}程序执行结果
[abcdefg, 小山, HELLO]此程序利用 HashSet 子类实例化了Set 接口对象并且在Set 集合中不允许保存重复数据。
程序使用的是HashSet子类并且根据名称可以发现在这个子类上采用了 Hash算法也称为散列、无序。这种算法就是利用二进制的计算结果来设置保存的空间根据数值的不同最终保存空间的位置也不同所以利用Hash算法保存的集合都是无序的但是其查找速度较快。
而如果希望保存的数据有序那么可以使用 Set 接口的另外一个子类 TreeSet 子类。
// 范例 8: 使用TreeSet 子类
package com.xiaoshan.demo;
import java.util.Set;
import java.util.TreeSet;public class TestDemo {public static void main(String[] args){SetString all new TreeSetString(); // 实例化Set接口all.add(abcdefg); //保存数据all.add(小山);all.add(HELLO);all.add(小山); //重复数据System.out.println(all); //直接输出集合}
}程序执行结果
[HELLO, abcdefg, 小山]TreeSet 子类属于排序的类集结构所以当使用 TreeSet 子类实例化 Set 接口后所保存的数据将会变为有序数据默认情况下按照字母的升序排列。
4.1 关于数据排序的说明
TreeSet 子类保存的内容可以进行排序但是其排序是依靠比较器接口(Comparable) 实现的即如果要利用TreeSet 子类保存任意类的对象并且按照自定义顺序对对象排序那么该对象所在的类必须要实现 java.lang.Comparable接口。
在前面的讲解常用类库时的文章中曾经讲解过比较器的使用现在在TreeSet子类中由于其不允许保存重复元素 (compareTo() 方法的比较结果返回0)如果说此时类中存在5个属性但是只比较了3个属性并且这3个属性的内容完全相同 (其余两个属性不同)那么TreeSet 也会认为是相同内容从而不会保存该数据因此会出现数据丢失的情况。
// 范例 9: 利用TreeSet 保存自定义类对象
package com.xiaoshan.demo;
import java.util.Set;
import java.util.TreeSet;class Book implements ComparableBook{ //需要实现Comparable 接口private String title;private double price;public Book(String title, double price){this.title title;this.price price;}Overridepublic String toString(){return 书名this.title,价格this.price\n;}Overridepublic int compareTo(Book o){ //排序方法比较所有属性if (this.price o.price){return 1;}else if(this.price o.price){return -1;}else{return this.title.compareTo(o.title); //调用String类的比较大小}}
}public class TestDemo{public static void main(String[] args){SetBook all new TreeSetBook(); // 实例化Set接口all.add(new Book(Java开发实战宝典,79.8)); //保存数据all.add(new Book(Java开发实战宝典,79.8)); //全部信息重复all.add(new Book(JSP开发实战宝典,79.8)); //价格信息重复all.add(new Book(Android开发实战宝典,89.8)); //都不重复System.out.println(all); }
}程序执行结果
[书名JSP开发实战宝典价格79.8,
书名Java开发实战宝典价格79.8,
书名Android开发实战宝典价格89.8]此程序首先利用 TreeSet 子类保存了几个 Book 类对象由于 Book 类实现了 Comaprable 接口所以会自动将所有保存的 Book 类对象强制转换为 Comparable 接口对象然后调用 compareTo() 方法进行排序如果发现比较结果为0则认为是重复元素将不再进行保存。因此 TreeSet 数据的排序以及重复元素的消除依靠的都是 Comparable 接口。
4.2 关于重复元素的说明
TreeSet 利用 Comparable 接口实现重复元素的判断但是这样的操作只适合支持排序类集操作环境下而其他子类(例如 HashSet) 如果要消除重复元素则必须依靠 Object 类中提供的两个方法。
取得哈希码 public int hashCode();对象比较 public boolean equals(Object obj)。
依靠着两个方法先通过第一个方法判断对象的哈希码是否相同若不同则说明一定不是同一个对象则相同则继续比较通过第二个方法再将对象的属性进行依次的比较。
对于 hashCode()与 equals()两个方法的使用可以换个角度来看。例如如果要核查一个人的信息肯定先要通过身份证编号查找到这个编号的信息(hashCode()方法负责编号), 再利用此身份证信息与个人信息进行比较(equals()进行属性的比较) 后才可以确定结果。
// 范例 10: 利用HashSet子类保存自定义类对象
package com.xiaoshan.demo;
import java.util.Set;
import java.util.HashSet;class Book{private String title;private double price;public Book(String title, double price){this.title title;this.price price;}Overridepublic int hashCode(){final int prime 31;int result 1;long temp;temp Double.doubleToLongBits(price);result prime* result (int)(temp^(temp32));result prime* result ((title null) ? 0 : title.hashCode());return result;}Overridepublic boolean equals(Object obj){if (this obj){return true;}if (obj null){return false;}if (getClass() ! obj.getClass()){return false;}Book other (Book) obj;if (Double.doubleToLongBits(price) ! Double.doubleToLongBits(other.price)){return false;}if (title null){if (other.title ! null){return false;}else if(!title.equals(other.title)){return false;}return true;}}Overridepublic String toString(){return 书名this.title,价格 this.price\n;}
}public class TestDemo{public static void main(String[] args){SetBook all new HashSetBook(); //实例化Set接口all.add(new Book(Java开发实战经典,79.8)); //保存数据all.add(new Book(Java开发实战经典,79.8)); //全部信息重复all.add(new Book(JSP开发实战经典,79.8)); //价格信息重复all.add(new Book(Android开发实战经典,89.8)); //都不重复System.out.println(all);}
}程序执行结果
[书名Android开发实战经典价格89.8,
书名JSP开发实战经典价格79.8,
书名Java开发实战经典价格79.8]此程序实现了集合中重复元素的清除利用的就是 hashCode() 与 equals() 两个方法所以在进行非排序集合操作时只要是判断重复元素依靠的永远都是 hashCode() 与 equals()。
5️⃣ 取出集合元素
由于集合中往往会保存多个对象数据所以一般进行集合输出时都会采用循环的方式完成。而在 Java 中集合的输出操作有4种形式 Iterator 输出、 ListIterator 输出、 foreach (加强for 循环 ) 输出、 Enumeration 输出。
5.1 迭代输出Iterator
Iterator (迭代器)是集合输出操作中最为常用的接口而在 Collection 接口中也提供了直接为 Iterator 接口实例化的方法 iterator(), 所以任何集合类型都可以转换为 Iterator接口输出。
在JDK1.5之前Collection 接口会直接提供 iterator() 方法但是到了JDK1.5之后为了可以让更多的操作支持Iterator 迭代输出单独建立了 Iterable 接口同时在这个接口里只定义了一个 iterator() 的抽象方法。所谓迭代器就好比排队点名一样从前向后开始一边判断是否有人一边进行操作。
在 Iterator 接口中一共定义了两个抽象方法如下所示。
方法类型描述public boolean hasNext()普通判断是否还有内容public E next()普通取出当前内容
当使用 Iterator接口输出时往往都先利用 hasNext() 改变指针位置同时判断是否有数据如果当前指针所在位置存在数据则利用 next() 取出数据这两个方法的作用如下所示。 图4 Iterator迭代器操作流程 在前面讲解 IO操作的文章中曾经讲解过一个 java.util.Scanner 的类实际上 Scanner 就是 Iterator 接口的子类所以在Scanner使用时才要求先利用 hasNextXxx() 判断是否有数据再利用 nextXxx()取得数据。
// 范例 11: 使用 Iterator 输出集合
package com.xiaoshan.demo;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class TestDemo {public static void main(String[] args){ListString all new ArrayListString(); //实例化List集合all.add(Hello); //保存数据all.add(Hello);all.add(World);IteratorString iterator all.iterator(); //实例化Iterator接口while (iterator.hasNext()){ //判断是否有数据String str iter.next(); //取出当前数据System.out.println(str); //输出数据}}
}程序执行结果
Hello
Hello
World此程序利用 List 接口的 iterator()方法 (Collection 接口继承而来) 将全部集合转变为 Iterator 输出由于不确定循环次数所以使用 while循环进行迭代输出。
在Iterator接口定义了一个删除数据的操作方法但是对不同的版本此方法也存在以下两种定义。
JDK 1.8以前public void remove();JDK 1.8之后default void remove()。
在JDK 1.8之前 remove()属于一个普通的删除方法而JDK 1.8之后将其定义为一个接口的 default方法。而之所以提供这个方法是因为在使用 Iterator输出数据时如果利用集合类 (Collection、List、Set) 提供的 remove()方法会导致程序中断执行的问题而如果非要进行集合元素的删除只能利用 Iterator 接口提供的 remove()方法才可以正常完成。
// 范例 12: 观察删除问题
package com.xiaoshan.demo;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class TestDemo{public static void main(String[] args){ListString allnew ArrayListString(); //实例化List集合all.add(Hello); //保存数据all.add(java);all.add(World);all.add(xiaoshan);IteratorString iterator all.iterator(); //实例化Iterator接口while (iterator.hasNext()){String str iter.next(); //取出当前数据if (java.equals(str)){all.remove(str); //此代码一执行输出将中断}System.out.println(str);//输出数据}}
}程序执行结果
Hello
java
Exception in threadmain java.util.ConcurrentModificationException程序并没有完成正常输出这是因为在迭代输出时进行了集合数据的错误删除操作而要避免此类问题只能利用Iterator接口提供的 remove() 方法。但是需要注意的是从实际的开发来讲集合输出中几乎不会出现删除数据的操作所以对此概念了解即可。
同时也希望大家要记住在集合的操作中增加数据(add())以及迭代输出操作是较为核心的部分对于此操作模式一定要熟练掌握。
5.2 双向迭代Listlterator
虽然利用Iterator 接口可以实现集合的迭代输出操作但是 Iterator 本身却存在一个问题只能进行由前向后的输出。所以为了让输出变得更加灵活在类集框架中就提供了一个 ListIterator 接口利用此接口可以实现双向迭代。 Listlterator 属于Iterator 的子接口此接口常用方法如下所示。
方法类型描述public boolean hasPrevious()普通判断是否有前一个元素public E previous()普通取出前一个元素public void add(E e)普通向集合追加数据public void set(E e)普通修改集合数据
在ListIterator 接口中除了可以继续使用 Iterator 接口的 hasNext()与 next()方法也具备了向前迭代的操作(hasPrevious()、previous()), 同时还提供了向集合追加数据和修改数据的支持。
从实际的开发来讲绝大多数情况如果要进行集合的输出都会使用Iterator接口相较而言ListIterator接口在实际使用中并不常见。同时通过ListIterator接口的定义可以发现该接口除了支持输出之外还可以进行集合更新 (增加、修改、删除), 但是这些操作在实际开发中使用得非常有限。
Listlterator 是专门为 List 子接口定义的输出接口所以 ListIterator 接口对象的实例化可以依靠 List 接口提供的方法
public ListIteratorE listIterator()实际上迭代器本质上就是一个指针的移动操作而 ListIterator与 Iterator的迭代处理原理类似。所以如果要进行由后向前迭代必须先进行由前向后迭代。
// 范例 13: 完成双向迭代
package com.xiaoshan.demo;
import java.util.ArrayList;
import java.util.List;
import java.util.Listlterator;public class TestDemo {public static void main(String[] args){ListString all new ArrayListString(); //实例化List接口对象 all.add(www.xiaoshan.com); //向集合保存数据all.add(www.baidu.com);all.add(www.csdn.cn);System.out.print(由前向后输出); ListIteratorString iterator all.listIterator(); //实例化Listlterator接口while (iterator.hasNext()){ //由前向后迭代String str iter.next(); //取出当前数据System.out.print(str 、);//输出数据}System.out.print(\n由后向前输出);while(iter.hasPrevious()){ //由后向前迭代 String str iter.previous(); //取出当前数据System.out.print(str 、); //输出数据}}
}程序执行结果
由前向后输出www.xiaoshan.com、www.baidu.com、www.csdn.cn、
由后向前输出www.csdn.cn、www.baidu.com、www.xiaoshan.com、程序利用 ListIterator 接口实现了List 集合的双向迭代输出首先利用 hasNext()与 next()实现由前向后的数据迭代然后使用 hasPrevious()与 previous()两个方法实现了数据的由后向前迭代。
5.3 foreach 输出
JDK 1.5之后为了简化数组以及集合的输出操作专门提供了 foreach (增强型 for 循环)输出所以也可以利用 foreach 语法实现所有集合数据的输出操作。
// 范例 14: 利用foreach 输出集合数据
package com.xiaoshan.demo;
import java.util.ArrayList;
import java.util.List;public class TestDemo{public static void main(String[] args){ListString all new ArrayListString();// 实例化List接口对象 all.add(www.baidu.com); //向集合保存数据all.add(www.xiaoshan.com); all.add(www.csdn.cn); //集合中包含的数据都是String型所以需要使用String接收集合中的每一个数据for (String str: all){ //for循环输出System.out.println(str);}}
}程序执行结果
www.baidu.com
www.xiaoshan.com
www.csdn.cn此程序利用 foreach 循环实现了集合输出由于集合中保存的都是 String 型数据所以每次执行 foreach 循环时都会将当前对象内容赋值给 str 对象而后就可以在循环体中利用 str 对象进行操作。
5.4 Enumeration 输出
Enumeration (枚举输出) 是与 Vector 类一起在JDK 1.0 时推出的输出接口即最早的 Vector 如果要输出数据就需要使用 Enumeration 接口完成此接口定义如下。
public interface EnumerationE{public boolean hasMoreElements(); // 判断是否有下一个元素等同于hasNext()public E nextElement(); //取出当前元素等同于next()
} 通过定义可以发现在 Enumeration 接口中一共定义了两个方法 hasMoreElements() 方法用于操作指针并且判断是否有数据而 nextElement() 方法用于取得当前数据。
因为 Enuemration 出现较早所以在 Collection 接口中并没有定义取得 Enumeration 接口对象的方法。 所以Enumeration 接口对象的取得只在 Vector 类中有所定义 public EnumerationE elements()。
// 范例 15: 利用 Enumeration 接口输出数据
package com.xiaoshan.demo;
import java.util.Enumeration;
import java.util.Vector;public class TestDemo{public static void main(String[] args){VectorString all new VectorString(); //实例化Vector子类对象all.add(www.baidu.com); //向集合保存数据all.add(www.xiaoshan.com);all.add(www.csdn.cn);EnumerationString enumeration all.elements(); //取得Enumeration接口对象while(enumeration.hasMoreElements()){ //判断是否有数据String str enumeration.nextElement(); //取出当前数据System.out.println(str); //输出数据}}
}程序执行结果
www.baidu.com
www.xiaoshan.com
www.csdn.cn此程序与 Iterator 接口输出实现的最终效果是完全一致的唯一的区别就是如果要利用集合类为 Enuemration 接口实例化就必须依靠 Vector 子类完成。
如果要进行类集的操作大部分情况下都会使用 List或 Set子接口很少会直接操作 Vector子类所以对于Enumeration接口而言使用情况有限大部分都以 Iterator输出为主。 总结
本文介绍了Java类集框架重点关注了单列集合顶层接口Collection和其子接口List的特点和用法。我们比较了数组和列表ArrayList之间的区别并深入探讨了List子类ArrayList、LinkedList、Vector、Stack之间的联系与区别。
在Set子接口部分我们介绍了HashSet和TreeSet的区别并解释了数据排序和处理重复元素的方法。
最后我们讨论了取出集合元素的几种常见方式包括迭代输出使用Iterator、双向迭代使用ListIterator、使用foreach循环以及使用Enumeration输出。
通过本文的学习我们对类集框架有了全面的了解。了解不同的集合类型和它们的特点可以根据实际需求选择最合适的集合。同时熟练使用各种元素取出方法可以方便地遍历和操作集合中的元素。 送书活动
书籍介绍 ISBN编号9787302511052 书名设计模式第2版高等学校设计模式课程系列教材 作者刘伟、夏莉、于俊洋、黄辛迪 定价79.80元 开本16开 出版社名称清华大学出版社 书籍图示 书籍目录 抽奖方式满足条件的用户中随机抽取两名程序抽取~参与方式关注点赞收藏评论区留言“打工真快乐我爱上班我学编程发自内心” 每人最多评论三次截止到统计时未关注、未收藏、未按格式正确评论的朋友将无法成功被程序统计到截止时间2023/8/1 20:00:00通知方式私信 评论区公布~ 书籍目录 第1章 统一建模语言基础知识 1.1 UML简介 1.1.1 UML的延生 1.1.2 UML的结构 1.1.3 UML的特点 1.2 类图 1.2.1 类与类图 1.2.2 类之间的关系 1.2.3 类图实例 1.3 顺序图 1.3.1 顺序图定义 1.3.2 顺序图组成元素与绘制 1.3.3 顺序图实例 1.4 状态图 1.4.1 状态图定义 1.4.2 状态图组成元素与绘制 1.4.3 状态图实例 1.5 本章小结 思考与练习 第2章 面向对象设计原则 2.1 面向对象设计原则概述 2.1.1 软件的可维护性和可复用性 2.1.2 面向对象设计原则简介 2.2 单一职责原则 2.2.1 单一职责原则定义 2.2.2 单一职责原则分析 2.2.3 单一职责原则实例 2.3 开闭原则 2.3.1 开闭原则定义 2.3.2 开闭原则分析 2.3.3 开闭原则实例 2.4 里氏代换原则 2.4.1 里氏代换原则定义 2.4.2 里氏代换原则分析 2.4.3 里氏代换原则实例 2.5 依赖倒转原则 2.5.1 依赖倒转原则定义 2.5.2 依赖倒转原则分析 2.5.3 依赖倒转原则实例 2.6 接口隔离原则 2.6.1 接口隔离原则定义 2.6.2 接口隔离原则分析 2.6.3 接口隔离原则实例 2.7 合成复用原则 2.7.1 合成复用原则定义 2.7.2 合成复用原则分析 2.7.3 合成复用原则实例 2.8 迪米特法则 2.8.1 迪米特法则定义 2.8.2 迪米特法则分析 2.8.3 迪米特法则实例 2.9 本章小结 思考与练习 第3章 设计模式概述 3.1 设计模式的诞生与发展 3.1.1 模式的诞生与定义 3.1.2 软件模式 3.1.3 设计模式的发展 3.2 设计模式的定义与分类 3.2.1 设计模式的定义 3.2.2 设计模式的基本要素 3.2.3 设计模式的分类 3.3 GoF设计模式简介 3.4 设计模式的优点 3.5 本章小结 思考与练习 第4章 简单工厂模式 4.1 创建型模式 4.1.1 创建型模式概述 4.1.2 创建型模式简介 4.2 简单工厂模式动机与定义 4.2.1 模式动机 4.2.2 模式定义 4.3 简单工厂模式结构与分析 4.3.1 模式结构 4.3.2 模式分析 4.4 简单工厂模式实例与解析 4.4.1 简单工厂模式实例之简单电视机工厂 4.4.2 简单工厂模式实例之权限管理 4.5 简单工厂模式效果与应用 4.5.1 模式优缺点 4.5.2 模式适用环境 4.5.3 模式应用 4.6 简单工厂模式扩展 4.7 本章小结 思考与练习 第5章 工厂方法模式 5.1 工厂方法模式动机与定义 5.1.1 简单工厂模式的不足 5.1.2 模式动机 5.1.3 模式定义 5.2 工厂方法模式结构与分析 5.2.1 模式结构 5.2.2 模式分析 5.3 工厂方法模式实例与解析 5.3.1 工厂方法模式实例之电视机工厂 5.3.2 工厂方法模式实例之日志记录器 5.4 工厂方法模式效果与应用 5.4.1 模式优缺点 5.4.2 模式适用环境 5.4.3 模式应用 5.5 工厂方法模式扩展 5.6 本章小结 思考与练习 第6章 抽象工厂模式 6.1 抽象工厂模式动机与定义 6.1.1 模式动机 6.1.2 模式定义 6.2 抽象工厂模式结构与分析 6.2.1 模式结构 6.2.2 模式分析 6.3 抽象工厂模式实例与解析 6.3.1 抽象工厂模式实例之电器工厂 6.3.2 抽象工厂模式实例之数据库操作工厂 6.4 抽象工厂模式效果与应用 6.4.1 模式优缺点 6.4.2 模式适用环境 6.4.3 模式应用 6.5 抽象工厂模式扩展 6.6 本章小结 思考与练习 第7章 建造者模式 7.1 建造者模式动机与定义 7.1.1 模式动机 7.1.2 模式定义 7.2 建造者模式结构与分析 7.2.1 模式结构 7.2.2 模式分析 7.3 建造者模式实例与解析 7.4 建造者模式效果与应用 7.4.1 模式优缺点 7.4.2 模式适用环境 7.4.3 模式应用 7.5 建造者模式扩展 7.6 本章小结 思考与练习 第8章 原型模式 8.1 原型模式动机与定义 8.1.1 模式动机 8.1.2 模式定义 8.2 原型模式结构与分析 8.2.1 模式结构 8.2.2 模式分析 8.3 原型模式实例与解析 8.3.1 原型模式实例之邮件复制浅克隆 8.3.2 原型模式实例之邮件复制深克隆 8.4 原型模式效果与应用 8.4.1 模式优缺点 8.4.2 模式适用环境 8.4.3 模式应用 8.5 原型模式扩展 8.6 本章小结 思考与练习 第9章 单例模式 … 第27章 访问者模式 27.1 访问者模式动机与定义 27.1.1 模式动机 27.1.2 模式定义 27.2 访问者模式结构与分析 27.2.1 模式结构 27.2.2 模式分析 27.3 访问者模式实例与解析 27.3.1 访问者模式实例之购物车 27.3.2 访问者模式实例之奖励审批系统 27.4 访问者模式效果与应用 27.4.1 模式优缺点 27.4.2 模式适用环境 27.4.3 模式应用 27.5 访问者模式扩展 27.6 本章小结 思考与练习 参考文献 文摘Abstract ⏪ 温习回顾上一篇点击跳转 《【Java基础教程】四十七网络编程篇网络通讯概念TCP、UDP协议Socket与ServerSocket类使用实践与应用场景~》 ⏩ 继续阅读下一篇点击跳转 《【Java基础教程】四十九集合体系篇 · 下双列集合解析——HashMap、Hashtable、LinkedHashMap、TreeMap、Properties ~》