- 知识目标
- 1. 理解Java的集合框架体系。
- 2. 掌握List、Set、Map接口的具体实现、内部结构、特殊的方法和适用场景等。
- 能力目标
- 1. 熟练使用List、Set、Map接口的具体实现类完成对集合的“增删改查”操作。
- 2. 根据各个不同的场景选择适合的框架来分析问题和解决问题。
- 素质目标
- 1. 能够阅读科技文档和撰写分析文档。
- 2. 能够查阅jdk API。
目录
前言:
数组虽然可以存放多个数据,但数组长度是固定不变的,也无法保存具有映射关系的数据
这时集合类的出现解决了这两个问题
集合:
数学含义:通常情况下,我们把具有相同性质的一类东西,汇聚成一个整体,就可以称为集合,集合论的奠基人康托尔在创建集合理论时给出了许多公理和性质,这都成为后来集合在其他领域应用的基础。
集合框架
集合是存放数据的容器;框架是类库的集合。集合框架就是为表示和操作集合而规定的一种统一的、标准的体系结构。
任何集合框架都包含三大块内容:对外的接口、接口的实现类和对集合运算的算法。我们可以把一个集合看成一个微型数据库,操作包括“增、删、改、查”4种。
java集合框架:
Java对常用的数据结构和算法做了一些规范(接口)和实现(具体实现接口的类)。所有抽象出来的数据结构和操作(算法)统称为Java集合框架(JavaCollectionFramework),如图2-1所示:
Collection、List、Set、Queue和Map都是接口(Interface),不是具体的类实现
Java集合框架支持3种类型的集合:规则集(Set),线性表(List)和图(Map)
表2-1 集合类和接口比较表:
图2-2 集合类接口关系图:
java集合框架的使用:
1.(1)LIst接口
List继承自Collection接口,List是有序集合。元素可以根据索引进行查询、删除、插入操作
Set不能有重复的元素,List可以有,即e1.equals(e2)的两个对象元素可以同时存在于List集合中
(2)List实现类
List接口的实现类主要有ArrayList、LinkedList、Vector(向量)、Stack(栈)
(3)ArrayList常用方法
表2-2 ArrayList主要方法
(4)List一般用法
在使用List集合时,通常情况下声明为List类型,实例化时根据实际情况的需要,实例化为ArrayList或LinkedList。
① 声明一个list的示例如下:
List<String> l1 = new ArrayList<String>();//利用ArrayList类实例化List集合
List<String> l2 = new LinkedList<String>();//利用LinkedList类实例化List集合
② 向list中存值的示例如下:
l1.add("张三");
l2.set(1,"李四");
③ 遍历list的示例如下:
Iterator it = l2.iterator();
while(it.hasNext())
{
System,out.println(it.next());
}
Iterator迭代器 API![](https://img-blog.csdnimg.cn/3ac6787b97b94082af539a239576b6e3.png)
ListIterator迭代器 API
<1>任务需求
使用List存取用户信息,并做增删改查操作。
任务分析:一个班级之中存在若干个学生,通过一个实体类定义一个学生相对应的基本信息,然后通过一个List集合进行存储,实现对学生基本信息的CURD(增删改查)((CURD):创建、读取、更新和删除)。类图如图下:
任务实现
User类
/**
* Title:描述用户类
* Description:
*
* @Copyright:Copyright(c)2017
* @author:wangzhikang
* @version:1.0
*/
package List;
import java.util.EmptyStackException;
/**
* 封装员工的信息和操作
* @author:wangzhiknag
*
*
*/
public class User {
private String name;
private String email;
/**
* Constructor
*/
public User(String name,String email){
this.name = name;
this.email = email;
}
/**
* 获取员工姓名
* @return:员工姓名
*
*/
public String getName(){
return name;
}
/**
* 修改员工姓名
* @param:name员工姓名
*
*/
public void setName(String name){
this.name = name;
}
/**
* 扩取员工email
* @return:员工email
*
*/
public String getEmail(){
return email;
}
/**
* 修改员工email
* @param:员工email
*
*/
public void setEmail(String email){
this.email = email;
}
}
TestList类
package List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class TestList {
public static void main(String[] args) {
List<User> l1 = new ArrayList<User>();
l1.add(new User("zhangsan","zhangsan@qq.com"));
User lisi = new User("lisi","lisi@qq.com");
User wangwu = new User("wangwu","wangwu@qq.com");
l1.add(lisi);
l1.add(wangwu);
///用迭代器遍历
// ListIterator integer = l1.listIterator();
// System.out.println("用Iterator迭代");
// while(integer.hasNext()){
// User user = integer.next();
// System.out.println(user.getName()+user.getEmail());
//foreach遍历
for(User user:l1){
System.out.println(user.getName()+":"+user.getEmail());
}
}
}
① 使用add()方法向列表的尾部或指定位置添加指定的元素。
② 使用set()方法替换列表中指定位置的元素;使用get()方法返回列表中指定位置的元素。
③ 使用remove(int index)方法移除列表中指定位置的元素。
④ 使用listIterator()方法返回此列表元素的列表迭代器。利用迭代最大的好处就程序员不用再去为了索引越界等等异常所苦恼了,只需在迭代过程中对列表元素进行操作即可。
ListIterstor<Integer> iterator = list.listInerstor();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
......................................................................................................................................................................................................................................................................................................................
2.Set接口
(1)Set接口
使用Set存取数据,并做增删改查操作
Set是继承于Collection的接口。
Java中的set接口有如下特点。
• 不允许出现重复元素;
• 集合中的元素位置无顺序;
• 有且只有一个值为null的元素
(2)Set接口的实现类
主要由:HashSet、TreeSet、LinkedHashSet.
HashSet依赖于HashMap,它实际上是通过HashMap实现的。HashSet中的元素是无序的。
TreeSet依赖于TreeMap,它实际上是通过TreeMap实现的。TreeSet中的元素是有序的。
(3)HashSet常用方法
HashSet在java.util包,同时也被称为集合
(3)HashSet常用方法
(4)Set接口的一般用法。
在使用Set集合时,通常情况下声明为Set类型,实例化时根据实际情况的需要,实例化为HashSet或TreeSet。
① 创建HashSet对象的示例如下:
HashSet hashset = new HashSet();
② 添加元素的示例如下:
hashset.add("abc");
③ 删除元素的示例如下:
hashset.clear();//清除所有元素
hashset.remove(Object o);//删除指定元素
④遍历HashSet:
Iterator it = hashset.iterator();//拿到hashset的迭代器
while(it.hasNext()){
HashSet h = it.next();
}
2. 任务需求使用Set存储学生信息,并进行CRUD操作。
3. 任务分析一个班级之中存在若干个学生,通过一个实体类定义一个学生相对应的基本信息,然后通过一个Set集合进行存储,实现对学生基本信息的CURD。
类图关系如图2-5所示。
代码实现:
Student.java
/**
* Title:描述学生类
* Description:
* @Copyright:Copyright(c)2022
* @date:06。11
* @author:wangzhikang
* @version:1.0
*/
package Set;
/**
* 封装学生的信息和操作
* @author:wangzhiknag
*/
public class Student {
private String name;
private int age;
/**
* Constructor
*/
public Student(String name,int age){
this.name = name;
this.age = age;
}
/**
* 获取学生姓名
* @return:学生姓名
*/
public String getName(){
return name;
}
/**
* 修改学生姓名
* @param:name学生姓名
*/
public void setName(String name){
this.name = name;
}
/**
* 获取学生年龄
* @return:学生年龄
*/
public int getAge(){
return age;
}
/**
* 修改学生年龄
* @param:学生年龄
*/
public void setAge(int age){
this.age = age;
}
//复写hashCode方法
@Override
public int hashCode(){
return 60;
}
//复写equals方法
public boolean equals(Object arg0){
if(!(arg0 instanceof Student)) return false;
Student student = (Student) arg0;
return this.name.equals(student.name)&&this.age==student.age;
}
}
TestHashSet.java
package Set;
import javax.swing.plaf.synth.SynthOptionPaneUI;
import javax.swing.text.html.HTMLDocument;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
public class TestHashSet {
public static void hashSet1() {
HashSet<String> hashset1 = new HashSet<String>();
hashset1.add("java001");
hashset1.add("java01");
hashset1.add("java011");
hashset1.add("java002");
hashset1.add("java004");
//用Iterator迭代器输出内容
Iterator<String> iterator = hashset1.iterator();
while (iterator.hasNext()) {
System.out.println((String) iterator.next());
}
}
public static void hashSet2(){
HashSet<Student> hashset2 = new HashSet<Student>();
hashset2.add(new Student("zhangsan",20));
hashset2.add(new Student("lisi",21));
hashset2.add(new Student("wangwu",22));
hashset2.add(new Student("zhaojun",23));
hashset2.add(new Student("mimi",24));
//用Iterator迭代器输出内容
Iterator<Student> iterator2 = hashset2.iterator();
while(iterator2.hasNext()){
Student next = (Student) iterator2.next();
System.out.println(next.getName()+":"+next.getAge());
}
}
public static void main(String[] args){
hashSet1();
hashSet2();
}
}
3.Map接口
(1)Map接口
Map提供了一种映射关系,其中的元素是以键值对(key-value)的形式存储的,能够实现根据key快速查找value。Map中的键值对以Entry类型的对象实例形式存在。键值(key值)不可重复,value值可以重复,一个value值可以和很多key值形成对应关系,每个键最多只能映射到一个值。Map支持泛型,形式如:Map<K,V>。Map中使用put(K key, V value)方法添加
(2)已知实现类
在Java.util包中接口Map<K,V>存储键值对,作为一个元组存入。元组以键作为标记,键相同时,值覆盖。类型参数有:• K——此映射所维护的键的类型;• V——映射值的类型。其已知实现类为HashMap、TreeMap。
(3)HashMap常用操作说明
HashMap是一个散列表,它存储的内容是键值对(key-value)映射。HashMap继承于AbstractMap,实现了Map、Cloneable、Java.io.Serializable接口。HashMap的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。
HashMap接口主要方法
(4)Map的一般用法
① 声明一个Map的示例如下:
Map map = new HashMap();
② 向map中存值的示例如下(注意map是以key-value的形式存放的):
map.put(1,"zhangsan");
③ 从map中取值的示例如下:
String str = map.get(1).toString;
//str = "zhangsan"
④ 遍历一个map,从中取得key和value的示例如下:
Map map = new HashMap();
for(Object obj:map.keySet()){
Object value = map.get(obj);
}
2. 任务需求使用Map存储学生信息,并进行CRUD操作。
3. 任务分析一个班级之中存在若干个学生,通过一个实体类定义一个学生相对应的基本信息,然后通过一个Map集合进行key-value键值存储,实现对学生基本信息的CURD。类图如图2-7所示。
Entry:Entry: 键值对 对象。
在Map类设计是,提供了一个嵌套接口(static修饰的接口):Entry。Entry将键值对的对应关系封装成了对象,即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键与对应的值。
Entry为什么是静态的?
Entry是Map接口中提供的一个静态内部嵌套接口,修饰为静态可以通过类名调用。
Map集合遍历键值对的方式:
Set<Map.Entry<K,V>> entrySet();
//返回此映射中包含的映射关系的Set视图
该方法返回值是Set集合,里面装的是Entry接口类型,即将映射关系装入Set集合。
实现步骤:
1,调用Map集合中的entrySet()方法,将集合中的映射关系对象存储到Set集合中
2,迭代Set集合
3,获取Set集合的元素,是映射关系的对象
4,通过映射关系对象的方法,getKey()和getValue(),获取键值对
代码实现
Student.java
/**
* Title:描述学生类
* Description:
* @Copyright:Copyright(c)2022
* @date:06。11
* @author:wangzhikang
* @version:1.0
*/
package Map;
/**
* 封装学生的信息和操作
* @author:wangzhiknag
*/
public class Student {
private String name;
private String id;
/**
* Constructor
*/
public Student(String name,String id){
this.name = name;
this.id = id;
}
/**
* 获取学生姓名
* @return:学生姓名
*/
public String getName(){
return name;
}
/**
* 修改学生姓名
* @param:name学生姓名
*/
public void setName(String name){
this.name = name;
}
/**
* 获取学生id
* @return:学生id
*/
public String getId(){
return id;
}
/**
* 修改学生id
* @param:学生id
*/
public void setId(String id){
this.id = id;
}
//复写hashCode方法
@Override
public int hashCode(){
return 60;
}
//复写equals方法
public boolean equals(Object arg0){
if(!(arg0 instanceof Student)) return false;
Student student = (Student) arg0;
return this.name.equals(student.name)&&this.id==student.id;
}
}
TestMap.java
package Map;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
/**
* 测试Map应用
* @author:wangzhikang
* @date:6.11
*
*/
public class TestMap {
//创建一个Map属性来承接学生对象
public Map<String,Student> student;
/**
* constructor
*
*/
public TestMap(){
this.student = new HashMap<String, Student>();
}
/**
* 添加方法:输入学生ID,判断是否被占用,
* 如果未被占用,则输入姓名,创建新学生对象,添加到student中
*/
public void testPut(){
//创建一个Scanner对象,输入学生ID
Scanner scan = new Scanner(System.in);
for(int i=0;i<3;i++){
System.out.println("请输入学生ID");
String nextID = scan.next();
Student stu = student.get(nextID);
if(stu==null){
System.out.println("请输入姓名:");
String nextName = scan.next();
student.put(nextID,new Student(nextID,nextName));
System.out.println("成功添加学生:"+student.get(nextID).getName());
}
else {System.out.println("该学生已被占用!");
}
}
}
/**
* 测试Map的keySet方法
*/
public void testKeySet(){
//通过keySet方法,返回Map中所有“键”的集合
Set<String> keySet = student.keySet();
//取得student的容量
System.out.println("总共有"+student.size()+"个学生");
//遍历keySet,取得每一个键,再调用get方法取得每一个键对应的value
for(String key : keySet){
Student stu = student.get(key);
if(stu!=null){
System.out.println("学生:"+stu.getName());
}
}
}
/**
*通过entrySet遍历Map
*/
public void testEntrySet(){
//通过entrySet返回Map中所有的键值对
Set<Map.Entry<String,Student>> entrySet = student.entrySet();
for(Map.Entry<String,Student> entry : entrySet){
System.out.println("取得键:"+entry.getKey());
System.out.println("对应的值为:"+entry.getValue().getName());
}
}
/**
* 删除Map中的映射
*/
public void testRemove(){
Scanner scan = new Scanner(System.in);
while (true){
System.out.println("请输入要删除学生的ID");
String stuID = scan.next();
//判断输入的ID是否存在对应的学生对象
Student stu = student.get(stuID);
if(stu==null){
System.out.println("输入的学生ID不存在");
continue;
}
student.remove(stuID);
System.out.println("成功删除学生"+stu.getName());
break;
}
testEntrySet();
}
/**
* 使用put方法修改Map中已有的映射
*/
public void testModify(){
System.out.println("请输入要修改的学生ID:");
Scanner scan = new Scanner(System.in);
while (true){
String id = scan.next();
Student stu = student.get(id);
if(stu==null){
System.out.println("ID不存在");
continue;
}
System.out.println("当前学生是:"+stu.getName());
System.out.println("请输入新的学生:");
String name = scan.next();
Student newStu = new Student(id,name);
student.put(id,newStu);
System.out.println("修改成功");
break;
}
}
/**
* 测试Map中是否存在某个key值或value值
*/
public void testContainKey() {
System.out.println("请输入学生ID:");
Scanner scan = new Scanner(System.in);
String stuID = scan.next();
//用containsKey()方法来判断是否存在某个key值
System.out.println("输入的ID为" + stuID + ",在学生列表中是否存在:" + student.containsKey(stuID));
if (student.containsKey(stuID)) {
System.out.println("学生的姓名为:" + student.get(stuID).getName());
}
System.out.println("请输入学生姓名:");
String stuName = scan.next();
//用containsValue()方法来判断是否存在某个value值
if (student.containsValue(new Student(null, stuName))) {
System.out.println("存在学生" + stuName);
} else {
System.out.println("学生不存在");
}
}
public static void main(String[] args){
TestMap mt = new TestMap();
mt.testPut();
mt.testKeySet();
}
}
拓展知识
上面主要对Java集合框架做了详细的介绍,包括Collection和Map两个接口及它们的抽象类和常用的具体实现类。下面主要介绍一下其他几个特殊的集合类,Vector、Stack、HashTable、ConcurrentHashMap以及CopyOnWriteArrayList。
1. Vector前面我们已经提到,Java设计者们在对之前的容器类进行重新设计时保留了一些数据结构,其中就有Vector。用法上,Vector与ArrayList基本一致,不同之处在于Vector使用了关键字synchronized,将访问和修改向量的方法都变成同步的了,所以对于不需要同步的应用程序来说,类ArrayList比类Vector更高效。
2. StackStack,栈类,是Java2之前引入的,继承自类Vector。
3. HashTableHashTable和前面介绍的HashMap很类似,它也是一个散列表,存储的内容是键值对映射,不同之处在于,HashTable是继承自Dictionary的,HashTable中的函数都是同步的,这意味着它也是线程安全的,另外,HashTable中key和value都不可以为null。上面的3个集合类都是在Java2之前推出的容器类,可以看到,尽管在使用中效率比较低,但是它们都是线程安全的。下面介绍两个特殊的集合类。
4. ConcurrentHashMapConcurrent,并发,从名字就可以看出来ConcurrentHashMap是HashMap的线程安全版。同HashMap相比,ConcurrentHashMap不仅保证了访问的线程安全性,而且在效率上与HashTable相比,也有较大的提高。
5. CopyOnWriteArrayListCopyOnWriteArrayList是一个线程安全的List接口的实现,它使用了ReentrantLock锁来保证在并发情况下提供高性能的并发读取。