Java集合框架简述
定义
- 在Java语言中,Java语言的设计者对常用的数据结构和算法做了一些规范(接口)和实现(具体实现接口的类)。所有抽象出来的数据结构和操作(算法)统称为Java集合框架(JavaCollectionFramework)。
- Java程序员在具体应用时,不必考虑数据结构和算法实现细节,只需要用这些类创建出来一些对象,然后直接应用就可以了,这样就大大提高了编程效率
集合框架包含内容

-
Collection:高度抽象出来的集合,它包含了集合的基本操作和属性。包含了List和Set两大接口分支。
-
注意:Map不是Collection的子接口。

1、List接口
1.1 特点:
- 有序:集合中每一个元素都有它的索引,类似于数组下标,第一个元素的索引值是0。
- 不唯一:集合中允许有相同的元素。
- 实现List接口的实现类有LinkedList,ArrayList,Vector和Stack。
1.2 常用方法:
// 添加元素:
.add(Object类型值)
// 获取元素
.get(下标)
// 获取集合大小(存放元素个数),如果size()返回值为0,代表集合为空
.size()
// 判断集合是否包含某个元素
.contains(元素)
// 判断集合是否非空
.isEmpty()
// 集合一步转换为数组对象
.toArray()
// 数组一步转换为集合
.Arrays.asList(数组)
1.3 List接口常用实现类
1.3.1 ArrayList类
-
特点
- 存放元素是按插入顺序来的,元素不唯一
- 由于存在下标值,遍历元素和随机访问元素的效率高。但是插入、删除效率低。
- 可以存null
- 元素下标不能超出已有元素数量-1
- ArrayList没有同步,及不安全。
- 存放元素是按插入顺序来的,元素不唯一
-
底层实现:Object类型的空数组。
- 如果初始化不传初始元素个数,或给的初始元素个数<10时,第一次赋值时扩容容量为10。
- 以后每次内存不足时,动态扩容1.5倍。扩容就是使用Array.copyof拷贝成一个新数组返回
- 存放数组的最大长度就是int的最大值
-
实现
public class TestArrayList { public static void main(String[] args) { List<String> list = new ArrayList<>(); // 添加元素 list.add("tom"); list.add("jack"); list.add("jason"); String obj1 = (String) list.get(0); System.out.println("0下标对应的集合元素为:" + obj1); System.out.println("list集合中,元素个数为:" + list.size()); // 判断list集合是否为空 System.out.println("strList集合是否为空?" + list.isEmpty()); // 判断list集合判断是否包含某个元素 System.out.println("strList集合中,是否包含Benz?" + list.contains("Benz")); // list集合指定下标添加元素:指定的下标值,必须在已存在元素个数范围内,由于效率不高,不推荐使用 list.add(3, "Masha"); System.out.println(list); // list集合删除元素:remove(可以是下标也可以是元素对象-删除首次出现的) list.remove("jack"); list.remove(3); // 1、遍历集合元素 for (int i = 0; i < list.size(); i++) { String str = list.get(i); System.out.println(str); } // 2、遍历集合元素 // 获取list集合的迭代器(只能通过集合获取,用完就结束,如果要再次使用,必须重新获取),可以进行迭代元素 Iterator<String> iterator = list.iterator(); // 使用while遍历迭代器,建议:list集合不推荐 while (iterator.hasNext()) { System.out.println("汽车品牌为:" + iterator.next()); } // 集合变数组-toArray(), Object[] objs = list.toArray(); System.out.println(Arrays.toString(objs)); // 数组变集合-Arrays工具类的方法 String[] name = { "小王", "小马", "小红" }; // 注意:此方法只能进行遍历操作,不可以增加元素(长度固定) List<String> nameList = Arrays.asList(name); for (int i = 0; i < nameList.size(); i++) { System.out.println("同学姓名为:" + nameList.get(i)); } // list集合清空:clear()方法 list.clear(); } }
1.3.2 LinkedList类
-
特点
- 能存null
- 存放元素有序,元素不唯一
- 存取元素效率高。但是遍历、随机访问元素效率低
- 遍历使用二分查找法。越靠中间的元素效率越低。只要想取得的元素的下标小于总元素个数的一半,就从前往后找,否则从后往前找
- 存放元素个数无限制
- 如果单看添加末尾元素,理论上说ArrayList效率更高
- 能存null
-
底层实现:实现类Deque接口(双向链表)
-
实现
public class TestLinkedList { public static void main(String[] args) { LinkedList<String> linkedList = new LinkedList<>(); linkedList.add("Audi"); linkedList.add("Bmw"); linkedList.add("Benz"); linkedList.add("Volvo"); linkedList.add("Audi"); linkedList.add(null); // 特有方法1:支持首尾快速添加元素 linkedList.addFirst("Masha"); linkedList.addLast("Falali"); // 特有方法2:支持首尾快速获取元素 System.out.println("首个汽车品牌:" + linkedList.getFirst()); System.out.println("末尾汽车品牌:" + linkedList.getLast()); // 特有方法3:支持首尾快速删除元素 linkedList.removeFirst(); linkedList.removeLast(); // 获取集合元素个数:size()方法 System.out.println("linkedList包含元素个数:" + linkedList.size()); /* * LinkedList也支持get(下标-从0开始)获取元素,但是实现原理跟ArrayList不同 * 1)下标也必须是在元素个数的范围内,否则异常:IndexOutOfBoundsException * 2)如果下标值在范围内,自动进行二分查找,首先看下标值是在元素个数一半的前半段,还是后半段 * 如果在前半段,从第一个节点开始,依次向后查找,直到找到对应下标节点,如果在后半段,从最后一个节点,依次往前查找,直到找到指定下标节点 * 遍历和随机访问,必须要依次移动指针。下标越接近中心值,效率越低 */ System.out.println(linkedList.get(3)); } }
1.3.3 Vector类
- 继承关系

-
Vector 继承了AbstractList,实现了List接口。
-
Vector实现了RandmoAccess接口,即提供了随机访问功能。
-
Vector 实现了Cloneable接口,即实现克隆功能。
-
Vector 实现Serializable接口,表示支持序列化。
-
没有实现java.io.Serializable接口,不支持序列化。
-
特点
- Vector实现了AbstractList抽象类和List接口,和ArrayList一样是基于Array存储的
- Vector 是线程安全的,在大多数方法上存在synchronized关键字
- synchronized关键字:Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
2、Set接口
2.1 特点
- 存储一组唯一、无序的对象
- 无序:集合中的元素顺序是固定的,只是不按照插入顺序排列,而是根据元素HashCode值确定
2.2 Set接口常用实现类
2.2.1 HashSet类
-
底层实现
- 底层使用HashMap实现
- 无参数的构造函数,此构造函数创建一个大小为16的容器,加载因子为0.75(即容器的大小始终是2的冥,默认为16)
-
特点:无序、唯一
- 无序:不按照存储顺序排列,但相应的元素的位置是固定的
- 唯一:根据重写的equals方法,判断是否重复。add()时,如果添加重复元素不会报错,只是添不进去,add返回boolean值。
- add():先判断元素hashcode是否相等,相等再使用equals()判断。不相等则存入元素。
- 它不允许出现重复元素;不保证集合中元素的顺序
- 允许包含值为null的元素,但最多只能有一个null元素。
- HashSet按Hash算法来存储集合的元素,因此具有很好的存取和查找性能。
- 如果泛型是类,在存入类对象时,如果要判断是否重复,必须要在这个类里重写hashCode()和equals()方法
-
实现
public class TestHashSet { public static void main(String[] args) { Set<String> strHashSet = new HashSet<String>(); strHashSet.add("Java"); strHashSet.add("JAVA"); strHashSet.add(null); System.out.println("strHashSet中存放的元素个数:" + strHashSet.size()); System.out.println("strHashSet中存放的元素:" + strHashSet); // 判断是否包含相同的元素 System.out.println(strHashSet.contains("Java")); // Set集合的两种遍历方法 Set<String> carSet = new HashSet<String>(); carSet.add("Audi"); carSet.add("Benz"); carSet.add("Bmw"); carSet.add("Volvo"); // 1、使用迭代器 Iterator<String> iterator = carSet.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } System.out.println("--------------"); // 2、使用增强for循环 for(String carStr : carSet){ System.out.println(carStr); } } }
2.2.2 TreeSet类
- 特点
- 就是有序的set集合,支持add、remove、get等方法.其底层是基于TreeMap实现的,非线程安全。
- 可以实现排序等功能的集合,它在将对象元素添加到集合中时会自动按照某种比较规则将其插入到有序的对象序列中,并保证该集合元素组成按照“升序”排列。
- TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。当我们构造TreeSet时,若使用不带参数的构造函数,则TreeSet的使用自然比较器;若用户需要使用自定义的比较器,则需要使用带比较器的参数。
- 注意:TreeSet集合不是通过hashcode和equals函数来比较元素的,而是通过实现Comparable接口并重写compareTo()方法或者Comparator接口的compare()来判断元素是否相等
2.2.3 总结
- 在对大量信息进行检索(不知道下标,比如contains)的时候,TreeSet比ArrayList更有效率。
- TreeSet是用树形结构来存储信息的,每个节点都会保存一下指针对象,分别指向父节点,左分支,右分支,相比较而言,ArrayList就是一个含有元素的简单数组了,正因为如此,它占的内存也要比ArrayList多一些。
- 想TreeSet插入元素也比ArrayList要快一些,因为当元素插入到ArrayList的任意位置时,平均每次要移动一半的列表(普遍的都是,set查询慢,插入快,list查询快,插入慢)。
3、Map接口
3.1、特点
- 存放的内容为:key-value键值对。根据键得到值,因此不允许键重复,但允许值重复。
3.2、Map常用实现类
3.2.1 HashMap类
-
特点
- 存放键值对
- 键可以允许为null,但只能有一个键为null
- 键值对存放是无序的
- 一个空的HashMap集合,初始容量是16,后续扩容都是2的整数次幂,最大容量是2的30次方
- Get()键不存在时,返回null
- 存放重复键值对时,会把值替换,键不变
- 不支持线程的同步,即任一时刻可以有多个线程同时写HashMap——可能会导致数据的不一致。
- HashMap集合重写了toString方法
-
底层实现:Node数组
-
实现
public class TestHashMap { public static void main(String[] args) { // 添加键值对方法:put(key, value) Map<String, String> carMap = new HashMap<>(); carMap.put("Audi", "奥迪"); carMap.put("Bmw", "宝马"); carMap.put("Benz", "奔驰"); System.out.println(carMap); // 获取键值对:只能根据key获取值,如果key不存在返回值为null System.out.println("Bmw对应的品牌名称为:" + carMap.get("Audi")); System.out.println("VW对应的品牌名称为:" + carMap.get("VW")); // 键是唯一的,但是如果存放重复的键,键值不变,会将值用新的值替换(区别于set) carMap.put("Audi", "奥迪A4"); System.out.println(carMap); // HashMap中,键可以为null,只能有一个,值可以有多个为null carMap.put(null, "未知1"); carMap.put(null, "未知2"); carMap.put("AA", null); carMap.put("BB", null); System.out.println(carMap); // 获取键集 keySet() Set<String> keySet = carMap.keySet(); System.out.println(keySet); // 获取值集 values() Collection<String> valueColl = carMap.values(); System.out.println(valueColl); // 判断是否包含某个键值对 : 只需要借助key唯一特性(是否包含key) System.out.println("是否包含Bmw的键值关系?" + carMap.containsKey("Bmw")); carMap.put("BmwX3", "宝马"); // 判断是否包含某个值(不能作为唯一的判断条件,值可以重复) System.out.println("是否包含宝马的值关系?" + carMap.containsValue("宝马")); // 删除元素:remove(key) carMap.remove(null); System.out.println(carMap); // HashMap集合遍历的三种方式 // 1) 迭代器-map集合没有直接获取迭代器的方法,只能通过Collection集合获取 Set<String> carKeySet = carMap.keySet(); Iterator<String> iterator = carKeySet.iterator(); while(iterator.hasNext()){ String keyStr = iterator.next(); String valueStr = carMap.get(keyStr); System.out.print(keyStr + "--" + valueStr + " "); } System.out.println("\n-------------------------"); // 2)使用增强for循环,原理跟1方法相同 for (String keyStr1 : carMap.keySet()) { System.out.print(keyStr1 + "--" + carMap.get(keyStr1) + " "); } System.out.println("\n-------------------------"); // 3)推荐使用,尤其是针对大容量的集合,效率最高 for (Map.Entry<String, String> entry : carMap.entrySet()) { System.out.print(entry.getKey() + "--" + entry.getValue() + " "); } System.out.println("\n-------------------------"); } }
3.2.2 TreeMap类
- 底层
- TreeMap的实现是红黑树算法的实现
3.3.3 HaspTable类
特点
- 与HashMap一样,Hashtable也是一个散列表,是以key-value存储形式存在,即主要用来存放键值对
- 与HashMap不同,Hashtable的函数都是同步的,这意味着它是线程安全的
- Hashtable的key、value都不可以为null,并且,Hashtable中的映射不是有序的
- 实现结构是数组+单向链表。
Hashtable的put、get和remove方法在多线程下可以保证数据安全,实现方式都是使用Synchronized同步锁方法的方式实现的。- key\value不可为Null
4、迭代器Iterator
4.1 简介
- 它是遍历集合的工具,即我们通常通过Iterator迭代器来遍历集合。我们说Collection依赖于Iterator,是因为Collection的实现类都要实现iterator()函数,返回一个Iterator对象。
- 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
4.2 实现迭代器
// (1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。
// (2) 使用next()获得序列中的下一个元素。
// (3) 使用hasNext()检查序列中是否还有元素
// (4) 使用remove()将迭代器新返回的元素删除。
Set<String> carKeySet = carMap.keySet();
Iterator<String> iterator = carKeySet.iterator();
while (iterator.hasNext()) {
String keyStr = iterator.next();
String valueStr = carMap.get(keyStr);
System.out.print(keyStr + "--" + valueStr + " ");
}
5、 泛型集合
6.1 简介
- 泛型,即“参数化类型”。将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
6.2 特点
- 特点: 1)适用于多种数据类型执行相同的代码 2)泛型中的类型在使用时指定 3)泛型归根到底就是“模版”
- 优点:使用泛型时,在实际使用之前类型就已经确定了,不需要强制类型转换。
6.3 实现
// 不加泛型,任何类型的元素都可以添加进来
List list = new ArrayList();
// 加了泛型,即指定添加元素类型
List<String> list = new ArrayList<>();
6、Collections和Collection不同
-
前者是集合的操作类,是一个提供对结合之间操作的工具类,后者是集合接口。
-
实现
public class TestCollections { public static void main(String[] args) { // 排序方法:sort(list集合) List<String> nameList = new ArrayList<>(); nameList.add("Tom"); nameList.add("Mark"); nameList.add("Marry"); nameList.add("Jack"); System.out.println("排序前:" + nameList); System.out.println("排序后:"); Collections.sort(nameList); System.out.println(nameList); // 反转方法:reverse(list结合) Collections.reverse(nameList); System.out.println(nameList); // 最大max(Collection集合)或者最小值min(Collection集合) Set<String> nameSet = new HashSet<String>(); nameSet.addAll(Arrays.asList("d", "f", "a", "g")); System.out.println("集合中最大元素:" + Collections.max(nameSet)); System.out.println("集合中最小元素:" + Collections.min(nameSet)); List<String> list = new ArrayList<String>(); list.add("c"); list.add("b"); list.add("a"); // 查找前要先进行排序 Collections.sort(list); // 集合元素查找,返回下标。 System.out.println(Collections.binarySearch(list, "c")); // 对象集合的自定义排序 List<Car> cars = new ArrayList<Car>(); cars.add(new Car("Audi", "A8", 400000)); cars.add(new Car("Audi", "A1", 150000)); cars.add(new Car("Audi", "A4", 300000)); cars.add(new Car("Audi", "A3", 200000)); cars.add(new Car("Audi", "A6", 350000)); cars.add(new Car("Audi", "A5", 300000)); System.out.println("-----------未排序前:-----------"); System.out.println(cars); System.out.println("-----------排序后:-----------"); // 如果排序的是对象,该对象必须实现Comparator接口 Collections.sort(cars); System.out.println(cars); } } // 对象类 public class Car implements Comparable<Car> { private String brand; private String type; private double price; public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public String getType() { return type; } public void setType(String type) { this.type = type; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public Car() { } public Car(String brand, String type, double price) { this.brand = brand; this.type = type; this.price = price; } @Override public String toString() { return "Car [brand=" + brand + ", type=" + type + ", price=" + price + "]"; } @Override public int compareTo(Car car) { return (int) (this.price - car.getPrice()); } }
8、补充内容
8.1 Vector、ArrayList、LinkedList:
1、Vector:
Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
2、ArrayList:
a. 当操作是在一列数据的后面添加数据而不是在前面或者中间,并需要随机地访问其中的元素时,使用ArrayList性能比较好。
b. ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
3、LinkedList:
a. 当对一列数据的前面或者中间执行添加或者删除操作时,并且按照顺序访问其中的元素时,要使用LinkedList。
b. LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。
Vector和ArrayList在使用上非常相似,都可以用来表示一组数量可变的对象应用的集合,并且可以随机的访问其中的元素。
8.2 Vector与ArrayList比较:
- 性能上 ArrayList底层数据结构是数组,适合随机查找和遍历,不适合插入和删除,线程不安全,效率高。LinkedList底层数据结构是链表, 适合数据的动态插入和删除,随机访问和遍历速度比较慢,线程不安全,效率高。
- 同步性
Vectors是可同步的,是线程安全的。ArrayList是不可同步的,不是线程安全的。所以,一般单线程推荐用ArrayList,多线程中则用Vector - 数据增长
往一个ArrayList或者Vector里插入一个元素时,如果内部数组空间不够,ArrayList或Vector会扩展它的大小。Vector在默认情况下增长一倍的大小,而ArrayList增加50%的大小。
8.3 HashTable、HashMap、HashSet:
HashTable和HashMap采用的存储机制是一样的,不同的是:
1、HashMap:
a. 采用数组方式存储key-value构成的Entry对象,无容量限制;
b. 基于key hash查找Entry对象存放到数组的位置,对于hash冲突采用链表的方式去解决;
c. 在插入元素时,可能会扩大数组的容量,在扩大容量时须要重新计算hash,并复制对象到新的数组中;
d. 是非线程安全的;
e. 遍历使用的是Iterator迭代器;
2、HashTable:
a. 是线程安全的;
b. 无论是key还是value都不允许有null值的存在;在HashTable中调用Put方法时,如果key为null,直接抛出NullPointerException异常;
c. 遍历使用的是Enumeration列举;
3、内部实现使用的数组初始化和扩容方式不同
HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。
HashTable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
4、HashSet:
a. 基于HashMap实现,无容量限制;
b. 是非线程安全的;
c. 不保证数据的有序;
总结
练习
/*课后作业:
已知数据手机号数组:
String[] phones = {"13804010040","13804010041","13804010042",
"13804010043","13804010044","13804010045",
"13804010046","13804010047","13804010044",
"13804010054","13804010055","13804010056",
"13804010058","13804010059","13804010060",
"13804010061","13804010062","13804010063",
"13804010064","13804010065","13804010066",
"13804010067","13804010068","13804010069",
"13804010070","13804010071","13804010072",
"13804010073","13804010074","13804010075",
"13804010045","13804010057","13804010063",
"13804010058","13804010069","13804010066"}
1、 使用集合,快速去除重复的手机号,将去重后手机号存放到List集合中,输出去重后有多少个手机号,13804010099这个手机号是否存在
2、 已知上面手机号数组,将数组转为集合,然后使用map集合存储每个手机号出现的次数,并遍历输出
*/
public static void main(String[] args) {
String[] phones = { "13804010040", "13804010041", "13804010042", "13804010043", "13804010044", "13804010045",
"13804010046", "13804010047", "13804010044", "13804010054", "13804010055", "13804010056", "13804010058",
"13804010059", "13804010060", "13804010061", "13804010062", "13804010063", "13804010064", "13804010065",
"13804010066", "13804010067", "13804010068", "13804010069", "13804010070", "13804010071", "13804010072",
"13804010073", "13804010074", "13804010075", "13804010045", "13804010057", "13804010063", "13804010058",
"13804010069", "13804010066" };
System.out.println("--------------------第一题开始--------------------");
Set<String> set = new HashSet<String>();
for (int i = 0; i < phones.length; i++) {
set.add(phones[i]);
}
System.out.println("去重后共有:" + set.size() + "个手机号");
System.out.println("13804010099手机号" + (set.contains("13804010099") ? "存在" : "不存在"));
System.out.println("--------------------第一题结束--------------------");
System.out.println("--------------------第二题开始--------------------");
Map<String, String> map = new HashMap<String, String>();
for (int i = 0; i < phones.length; i++) {
if (map.containsKey(phones[i])) {
String num1 = map.get(phones[i]);
int num = Integer.valueOf(num1);
num++;
map.put(phones[i], String.valueOf(num));
continue;
}
map.put(phones[i], "1");
}
System.out.println(map);
System.out.println("--------------------第二题结束--------------------");
}
/*
3、 系统循环输入5个学生信息(学号,姓名和分数),将五个学生信息存放到Map集合中,可以根据学号获得学生对象
a) 使用两种方式遍历map集合,输出学生的完整信息如下格式
[学号:xxx,姓名:xxx,分数:xxx]
b) 系统输入学号,可以快速获取学生对象,如果没有该学号,提示学生不存在,有就输出学生信息
*/
public class TestStu {
public static void main(String[] args) {
System.out.println("--------------------第三题开始--------------------");
Scanner scanner = new Scanner(System.in);
Map<String, Student> map = new HashMap<>();
for (int i = 0; i < 1; i++) {
System.out.print("请输入" + (i + 1) + "个学生的姓名:");
String name = scanner.next();
System.out.print("请输入" + (i + 1) + "个学生的性别:");
String sex = scanner.next();
System.out.print("请输入" + (i + 1) + "个学生的学号:");
String stu_num = scanner.next();
Student student = new Student(name, sex, stu_num);
map.put(stu_num, student);
}
for (String string : map.keySet()) {
System.out.println(map.get(string));
}
for (Map.Entry<String, Student> entry : map.entrySet()) {
System.out.println(entry.getValue());
}
System.out.println("请输入要查找的学生学号");
String stu_num = scanner.next();
if (map.get(stu_num) != null) {
System.out.println(map.get(stu_num));
} else {
System.out.println("学生不存在");
}
scanner.close();
System.out.println("--------------------第三题结束--------------------");
}
}
/*4、 设计简单程序,创建省份和地市的关系集合,通过省份可以获取所有地市,通过输入地市,可以获取对应的省份
a) 江苏省: 无锡市 常州市 扬州市 徐州市 苏州市 连云港 盐城市 淮安市 宿迁市 镇江市 南通市 泰州市
b) 浙江省: 绍兴市 温州市 湖州市 嘉兴市 台州市 金华市 舟山市 衢州市 丽水市
c) 安徽省:合肥市 芜湖市 亳州市 马鞍山 池州市 淮南市 淮北市 蚌埠市 巢湖市 安庆市 宿州市 宣城市 滁州市 黄山市 六安市 阜阳市 铜陵市
*/
public class TestCity {
public static void main(String[] args) {
System.out.println("--------------------第四题开始--------------------");
String[] pro1 = { "无锡市", "常州市", "扬州市", "徐州市", "苏州市", "连云港", "盐城市", "淮安市", "宿迁市", "镇江市", "南通市", "泰州市" };
String[] pro2 = { "绍兴市", "温州市", "湖州市", "嘉兴市", "台州市", "金华市", "舟山市", "衢州市", "丽水市" };
String[] pro3 = { "合肥市", "芜湖市", "亳州市", "马鞍山", "池州市", "淮南市", "淮北市", "蚌埠市", "巢湖市", "安庆市", "宿州市", "宣城市", "滁州市",
"黄山市", "六安市", "阜阳市", "铜陵市" };
List<String> list1 = new ArrayList<>();
for (int i = 0; i < pro1.length; i++) {
list1.add(pro1[i]);
}
List<String> list2 = new ArrayList<>();
for (int i = 0; i < pro2.length; i++) {
list2.add(pro2[i]);
}
List<String> list3 = new ArrayList<>();
for (int i = 0; i < pro3.length; i++) {
list3.add(pro3[i]);
}
Map<String, List<String>> map = new HashMap<String, List<String>>();
map.put("江苏省", list1);
map.put("浙江省", list2);
map.put("安徽省", list3);
Scanner scanner = new Scanner(System.in);
System.out.println("请输入省份");
String nameString = scanner.next();
System.out.println(map.get(nameString));
System.out.println("请输入地市");
nameString = scanner.next();
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
if (entry.getValue().contains(nameString)) {
System.out.println(entry.getKey());
}
}
scanner.close();
System.out.println("--------------------第四题结束--------------------");
}
}

浙公网安备 33010602011771号