Java集合
一、为什么需要集合?
在Java中可以使用数组来保存多个对象,但数组长度不可变化,一旦在初始化数组时指定了数组长度,这个数组长度就是不可变的。如果需要保存数量变化的数据,数组就有点无能为力了。而且数组无法保存具有映射关系的数据,如成绩表为语文——79,数学——80,这种数据看上去像两个数组,但这两个数组的元素之间有一定的关联关系。
为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),Java 提供了集合类。特点如下:
- 集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。
- Java 所有的集合类都位于 java.util 包下,提供了一个表示和操作对象集合的统一构架,包含大量集合接口,以及这些接口的实现类和操作它们的算法。
- 集合类和数组不一样,数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量),而集合里只能保存对象(实际上只是保存对象的引用变量,但通常习惯上认为集合里保存的是对象)。
二、Java中集合类型说明
Java 集合类型分为 Collection 和 Map,它们是 Java 集合的根接口,这两个接口又包含了一些子接口或实现类。图 1 和图 2 分别为 Collection 和 Map 的子接口及其实现类。


在 图 1 和图 2 中,黄色块为集合的接口,蓝色块为集合的实现类。表 1 介绍了这些接口的作用。
| 接口名称 | 作 用 |
|---|---|
| Iterator 接口 | 集合的输出接口,主要用于遍历输出(即迭代访问)Collection 集合中的元素,Iterator 对象被称之为迭代器。迭代器接口是集合接口的父接口,实现类实现 Collection 时就必须实现 Iterator 接口。 |
| Collection 接口 | 是 List、Set 和 Queue 的父接口,是存放一组单值的最大接口。所谓的单值是指集合中的每个元素都是一个对象。一般很少直接使用此接口直接操作。 |
| Queue 接口 | Queue 是 Java 提供的队列实现,有点类似于 List。 |
| Dueue 接口 | 是 Queue 的一个子接口,为双向队列。 |
| List 接口 | 是最常用的接口。是有序集合,允许有相同的元素。使用 List 能够精确地控制每个元素插入的位置,用户能够使用索引(元素在 List 中的位置,类似于数组下标)来访问 List 中的元素,与数组类似。 |
| Set 接口 | 不能包含重复的元素。 |
| Map 接口 | 是存放一对值的最大接口,即接口中的每个元素都是一对,以 key➡value 的形式保存。 |
对于 Set、List、Queue 和 Map 这 4 种集合,Java 最常用的实现类分别是 HashSet、TreeSet、ArrayList、ArrayDueue、LinkedList 和 HashMap、TreeMap 等。表 2 介绍了集合中这些常用的实现类。
| 类名称 | 作用 |
|---|---|
| HashSet | 为优化査询速度而设计的 Set。它是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,实现比较简单 |
| TreeSet | 实现了 Set 接口,是一个有序的 Set,这样就能从 Set 里面提取一个有序序列 |
| ArrayList | 一个用数组实现的 List,能进行快速的随机访问,效率高而且实现了可变大小的数组 |
| ArrayDueue | 是一个基于数组实现的双端队列,按“先进先出”的方式操作集合元素 |
| LinkedList | 对顺序访问进行了优化,但随机访问的速度相对较慢。此外它还有 addFirst()、addLast()、getFirst()、getLast()、removeFirst() 和 removeLast() 等方法,能把它当成栈(Stack)或队列(Queue)来用 |
| HsahMap | 按哈希算法来存取键对象 |
| TreeMap | 可以对键对象进行排序 |
三、Java Collection接口
Collection 接口是 List、Set 和 Queue 接口的父接口,通常情况下不被直接使用。Collection 接口定义了一些通用的方法,通过这些方法可以实现对集合的基本操作。定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
| 方法名称 | 说明 |
|---|---|
| boolean add(E e) | 向集合中添加一个元素,如果集合对象被添加操作改变了,则返回 true。E 是元素的数据类型 |
| boolean addAll(Collection c) | 向集合中添加集合 c 中的所有元素,如果集合对象被添加操作改变了,则返回 true。 |
| void clear() | 清除集合中的所有元素,将集合长度变为 0。 |
| boolean contains(Object o) | 判断集合中是否存在指定元素 |
| boolean containsAll(Collection c) | 判断集合中是否包含集合 c 中的所有元素 |
| boolean isEmpty() | 判断集合是否为空 |
| Iterator<E>iterator() | 返回一个 Iterator 对象,用于遍历集合中的元素 |
| boolean remove(Object o) | 从集合中删除一个指定元素,当集合中包含了一个或多个元素 o 时,该方法只删除第一个符合条件的元素,该方法将返回 true。 |
| boolean removeAll(Collection c) | 从集合中删除所有在集合 c 中出现的元素(相当于把调用该方法的集合减去集合 c)。如果该操作改变了调用该方法的集合,则该方法返回 true。 |
| boolean retainAll(Collection c) | 从集合中删除集合 c 里不包含的元素(相当于把调用该方法的集合变成该集合和集合 c 的交集),如果该操作改变了调用该方法的集合,则该方法返回 true。 |
| int size() | 返回集合中元素的个数 |
| Object[] toArray() | 把集合转换为一个数组,所有的集合元素变成对应的数组元素。 |
案例如下:
public class Test11 { public static void main(String[] args) { ArrayList list1 = new ArrayList();//创建集合 ArrayList list2 = new ArrayList();//创建集合 //向集合添加元素 list1.add("李媛");//向list1添加一个元素 list1.add("向红");//向list1添加一个元素 list2.addAll(list1);//将list1的所有元素添加到list2 list2.add("王源");//向list2添加一个元素 System.out.println("输出list2中的元素如下:"); for (Object o : list2) { System.out.println(o); } } }
由于 Collection 是接口,不能对其实例化,所以上述代码中使用了 Collection 接口的 ArrayList 实现类来调用 Collection 的方法。add() 方法可以向 Collection 中添加一个元素,而调用 addAll() 方法可以将指定 Collection 中的所有元素添加到另一个 Collection 中。
代码创建了两个集合 list1 和 list2,然后调用 add() 方法向 list1 中添加了两个元素,再调用 addAll() 方法将这两个元素添加到 list2 中。接下来又向 list2 中添加了一个元素,最后输出 list2 集合中的所有元素,结果如下:
输出list2中的元素如下:
李媛
向红
王源
Collection 集合中 size()、remove() 和 removeAll() 方法的应用。具体代码如下:
public class Test12 { public static void main(String[] args) { ArrayList list1 = new ArrayList(); list1.add("接化发"); list1.add("混元十三刀"); list1.add("虎扑崩拳"); System.out.println("集合list1中元素个数:"+list1.size());//输出元素个数 //删除第三个元素 list1.remove(2);//根据索引删除第3个元素 System.out.println("执行remove后集合list1中元素个数:"+list1.size());//输出元素个数 System.out.println("list1的集合元素如下:"); for (Object o : list1) { System.out.println(o); } ArrayList list2 = new ArrayList(); list2.add("接化发"); list2.add("混元十三刀"); list2.add("虎扑崩拳"); //从 list2 中将 list2 和 list1 中相同的元素删除 list2.removeAll(list1); System.out.println("执行removeAll后集合list2中元素个数:"+list2.size());//输出元素个数 } }
执行后结果如下:
list1 集合在调用 remove(2) 方法删除第 3 个元素之后剩下了 接化发 和 混元十三刀 。list2.removeAll(list1) 语句会从 list2 中将 list2 和 list1 中相同的元素删除,即删除 接化发 和 混元十三刀 元素。最后输出结果如下:
集合list1中元素个数:3 执行remove后集合list1中元素个数:2 list1的集合元素如下: 接化发 混元十三刀 执行removeAll后集合list2中元素个数:1
注意:
- retainAll( ) 方法的作用与 removeAll( ) 方法相反,即保留两个集合中相同的元素,其他全部删除。
- 编译上面程序时,系统可能输出一些警告提示,这些警告是提示用户没有使用泛型来限制集合里的元素类型,读者暂时不要理会这些警告,后面我们会详细介绍泛型编程。
- 在传统模式下,把一个对象“丢进”集合中后,集合会忘记这个对象的类型。也就是说,系统把所有的集合元素都当成 Object 类型。从 Java 5 以后,可以使用泛型来限制集合里元素的类型,并让集合记住所有集合元素的类型。
四、ArrayList和LinkedList类的用法及区别
List 是一个有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List 集合默认按元素的添加顺序设置元素的索引,第一个添加到 List 集合中的元素的索引为 0,第二个为 1,依此类推。
List 实现了 Collection 接口,它主要有两个常用的实现类:ArrayList 类和 LinkedList 类。
4.1.ArrayList 类
ArrayList 类实现了可变数组的大小,存储在内的数据称为元素。它还提供了快速基于索引访问元素的方式,对尾部成员的增加和删除支持较好。使用 ArrayList 创建的集合,允许对集合中的元素进行快速的随机访问,不过,向 ArrayList 中插入与删除元素的速度相对较慢。ArrayList 类的常用构造方法有如下两种重载形式:
- ArrayList():构造一个初始容量为 10 的空列表。
- ArrayList(Collection<?extends E>c):构造一个包含指定 Collection 元素的列表,这些元素是按照该 Collection 的迭代器返回它们的顺序排列的。
ArrayList 类除了包含 Collection 接口中的所有方法之外,还包括 List 接口中提供的如下:方法。
| 方法名称 | 说明 |
|---|---|
| E get(int index) | 获取此集合中指定索引位置的元素,E 为集合中元素的数据类型 |
| int index(Object o) | 返回此集合中第一次出现指定元素的索引,如果此集合不包含该元 素,则返回 -1 |
| int lastIndexOf(Object o) | 返回此集合中最后一次出现指定元素的索引,如果此集合不包含该 元素,则返回 -1 |
| E set(int index, Eelement) | 将此集合中指定索引位置的元素修改为 element 参数指定的对象。 此方法返回此集合中指定索引位置的原元素 |
| List<E> subList(int fromlndex, int tolndex) | 返回一个新的集合,新集合中包含 fromlndex 和 tolndex 索引之间 的所有元素。包含 fromlndex 处的元素,不包含 tolndex 索引处的 元素 |
注意:当调用 List 的 set(int index, Object element) 方法来改变 List 集合指定索引处的元素时,指定的索引必须是 List 集合的有效索引。例如集合长度为 4,就不能指定替换索引为 4 处的元素,也就是说这个方法不会改变 List 集合的长度。
例 1:使用 ArrayList 类向集合中添加三个商品信息,包括商品编号、名称和价格,然后遍历集合输出这些商品信息。
1)创建一个商品类 Product,在该类中定义 3 个属性和 toString() 方法,分别实现 setter/getter 方法。代码的实现如下:
public class Product { private int id;//商品编号 private String name;//商品名称 private float price; //价格 public Product(int id, String name, float price) { this.id = id; this.name = name; this.price = price; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } @Override public String toString() { return "Product{" + "id=" + id + ", name='" + name + '\'' + ", price=" + price + '}'; } }
2)创建一个测试类,调用 Product 类的构造函数实例化三个对象,并将 Product 对象保存至 ArrayList 集合中。最后遍历该集合,输出商品信息。测试类的代码实现如下
public class Test13 { public static void main(String[] args) { //创建对象 Product p1 = new Product(1, "口香糖", 8); Product p2 = new Product(2, "矿泉水", 5); Product p3 = new Product(3, "方便面", 4); //创建 集合 ArrayList<Product> products = new ArrayList<>(); products.add(p1); products.add(p2); products.add(p3); System.out.println("*************** 商品信息 ***************"); for (int i = 0; i < products.size(); i++) { //遍历集合,输出 Product product = products.get(i); System.out.println(product); } } }
上面 ArrayList 集合中存放的是自定义类 Product 的对象,这与存储的 String 类的对象是相同的。与 Set 不同的是,List 集合中存在 get() 方法,该方法可以通过索引来获取所对应的值,从而获取商品信息。
*************** 商品信息 *************** Product{id=1, name='口香糖', price=8.0} Product{id=2, name='矿泉水', price=5.0} Product{id=3, name='方便面', price=4.0}
常见方法如下:
1)List 集合时需要注意区分 indexOf() 方法和 lastIndexOf() 方法。前者是获得指定对象的最小索引位置,而后者是获得指定对象的最大索引位置。前提条件是指定的对象在 List 集合中有重复的对象,否则这两个方法获取的索引值相同
2)使用 subList() 方法截取 List 集合中部分元素时要注意,新的集合中包含起始索引位置的元素,但是不包含结束索引位置的元素。例如,subList(1,4) 方法实际截取的是索引 1 到索引 3 的元素,并组成新的 List 集合。
3)Java迭代器(Iterator)是 Java 集合框架中的一种机制,它提供了一种在不暴露集合内部实现的情况下遍历集合元素的方法。Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayList 和 HashSet 等集合。Iterator 是 Java 迭代器最简单的实现,ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口。迭代器 it 的三个基本操作是 next 、hasNext 和 remove。
- 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
- 调用 it.hasNext() 用于检测集合中是否还有元素。
- 调用 it.remove() 将迭代器返回的元素删除。
案例如下:
public class Test11 { public static void main(String[] args) { ArrayList<String> lists = new ArrayList<>(); lists.add("张三"); lists.add("|"); lists.add("李四"); lists.add("|"); lists.add("王五"); lists.add("|"); lists.add("赵六"); System.out.println("list集合中的元素数量:"+lists.size()); System.out.println("lists集合中的元素如下:"); /** * ava迭代器(Iterator)是 Java 集合框架中的一种机制,它提供了一种在不暴露集合内部实现的情况下遍历集合元素的方法。 * Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayList 和 HashSet 等集合。 * Iterator 是 Java 迭代器最简单的实现,ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口。 * 迭代器 it 的三个基本操作是 next 、hasNext 和 remove。 * 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。 * 调用 it.hasNext() 用于检测集合中是否还有元素。 * 调用 it.remove() 将迭代器返回的元素删除。 */ Iterator<String> iterator = lists.iterator(); //用于检测集合中是否还有元素。 while (iterator.hasNext()){ System.out.println(iterator.next()); } System.out.println("\n在 list 集合中'丨'第一次出现的位置是:" + lists.indexOf("|")); System.out.println("在 list 集合中'丨'最后一次出现的位置是:" + lists.lastIndexOf("|")); List<String> strings = new ArrayList<>(); //subList(2,5)从lists集合截取索引2-5的元素,保存到strings strings = lists.subList(2,5); System.out.println("strings集合中的元素数量:"+strings.size()); System.out.println("输出集合的元素如下:"); //通过迭代器取值 Iterator<String> stringIterator = strings.iterator(); while (stringIterator.hasNext()){ System.out.println(stringIterator.next()); } } }
4.2.LinkedList类
LinkedList 类采用链表结构保存对象,这种结构的优点是便于向集合中插入或者删除元素。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高,但是 LinkedList 类随机访问元素的速度则相对较慢。这里的随机访问是指检索集合中特定索引位置的元素。
LinkedList 类除了包含 Collection 接口和 List 接口中的所有方法之外,还有如下方法。
| 方法名称 | 说明 |
|---|---|
| void addFirst(E e) | 将指定元素添加到此集合的开头 |
| void addLast(E e) | 将指定元素添加到此集合的末尾 |
| E getFirst() | 返回此集合的第一个元素 |
| E getLast() | 返回此集合的最后一个元素 |
| E removeFirst() | 删除此集合中的第一个元素 |
| E removeLast() | 删除此集合中的最后一个元素 |
实现代码如下:
public class Test12 { public static void main(String[] args) { LinkedList<String> strings = new LinkedList<>();//创建集合对象 //添加元素 strings.add("面包"); strings.add("牛奶"); strings.add("橙子"); strings.add("香蕉"); //向集合的末尾添加元素 strings.addLast("西瓜"); //指定位置添加元素 strings.add(3,"菠萝"); //输出 System.out.println("strings集合中有哪些元素:"); for (int i = 0; i < strings.size(); i++) { System.out.println(strings.get(i)); } System.out.println("第一个商品的名称为:"+strings.getFirst()); System.out.println("最后一个商品的名称为:"+strings.getLast()); strings.removeLast();//删除最后一个元素 System.out.println("删除最后一个元素后,元素剩余:"); System.out.println("strings集合中有哪些元素:"); for (int i = 0; i < strings.size(); i++) { System.out.println(strings.get(i)); } } }
4.3.ArrayList 类和 LinkedList 类的区别
- ArrayList 与 LinkedList 都是 List 接口的实现类,因此都实现了 List 的所有未实现的方法,只是实现的方式有所不同。
- ArrayList 是基于动态数组数据结构的实现,访问元素速度优于 LinkedList。LinkedList 是基于链表数据结构的实现,占用的内存空间比较大,但在批量插入或删除数据时优于 ArrayList。
- 对于快速访问对象的需求,使用 ArrayList 实现执行效率上会比较好。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高。
- 不同的结构对应于不同的算法,有的考虑节省占用空间,有的考虑提高运行效率,对于程序员而言,它们就像是“熊掌”和“鱼肉”,不可兼得。高运行速度往往是以牺牲空间为代价的,而节省占用空间往往是以牺牲运行速度为代价的。
五、Java Set集合:HashSet和TreeSet类
Set 集合类似于一个罐子,程序可以依次把多个对象“丢进”Set 集合,而 Set 集合通常不能记住元素的添加顺序。也就是说 Set 集合中的对象不按特定的方式排序,只是简单地把对象加入集合。Set 集合中不能包含重复的对象,并且最多只允许包含一个 null 元素。
Set 实现了 Collection 接口,它主要有两个常用的实现类:HashSet 类和 TreeSet类。
5.1.HashSet 类
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时就是使用这个实现类。HashSet 是按照 Hash 算法来存储集合中的元素。因此具有很好的存取和查找性能。
HashSet 具有以下特点:
- 不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
- HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步。
- 集合元素值可以是 null。
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals() 方法比较返回的结果为 true,但它们的 hashCode 不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。
也就是说,两个对象的 hashCode 值相等且通过 equals() 方法比较返回结果为 true,则 HashSet 集合认为两个元素相等。
在 HashSet 类中实现了 Collection 接口中的所有方法。HashSet 类的常用构造方法重载形式如下。
- HashSet():构造一个新的空的 Set 集合。
- HashSet(Collection<? extends E>c):构造一个包含指定 Collection 集合元素的新 Set 集合。其中,“< >”中的 extends 表示 HashSet 的父类,即指明该 Set 集合中存放的集合元素类型。c 表示其中的元素将被存放在此 Set 集合中。
public class Test13 { public static void main(String[] args) { //创建一个空的set集合 HashSet<String> strings = new HashSet<>(); String s1 = new String("Java自动化"); String s2 = new String("java测试平台开发"); String s3 = new String("全链路性能测试"); String s4 = new String("微服务项目实战"); String s5 = new String("云原生"); //将上面的的对象存储到集合中 strings.add(s1); strings.add(s2); strings.add(s3); strings.add(s4); strings.add(s5); System.out.println("测试架构师需要学习的课程:"); Iterator<String> iterator = strings.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } strings.add(new String("云原生"));//会自动去重。所以云原生只会有一个 System.out.println("测试架构师需要学习:"+strings.size()+"门课程"); } }
上面的代码,首先使用 HashSet 类的构造方法创建了一个 Set 集合,接着创建了 5 个 String 类型的对象,并将这些对象存储到 Set 集合中。使用 HashSet 类中的 iterator() 方法获取一个 Iterator 对象,并调用其 hasNext() 方法遍历集合元素,再将使用 next() 方法读取的元素强制转换为 String 类型。最后调用 HashSet 类中的 size() 方法获取集合元素个数。执行结果如下:
测试架构师需要学习的课程:
云原生
Java自动化
全链路性能测试
微服务项目实战
java测试平台开发
测试架构师需要学习:5门课程
注意:上面的案例中,如果再向 strings 集合中再添加一个名称为“云原生”的 String 对象,则输出的结果与上述执行结果相同。也就是说,如果向 Set 集合中添加两个相同的元素,则后添加的会覆盖前面添加的元素,即在 Set 集合中不会出现相同的元素。
5.2.TreeSet 类
TreeSet 类同时实现了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口,可以实现对集合进行自然排序,因此使用 TreeSet 类实现的 Set 接口默认情况下是自然排序的,这里的自然排序指的是升序排序。
TreeSet 只能对实现了 Comparable 接口的类对象进行排序,因为 Comparable 接口中有一个 compareTo(Object o) 方法用于比较两个对象的大小。例如 a.compareTo(b),如果 a 和 b 相等,则该方法返回 0;如果 a 大于 b,则该方法返回大于 0 的值;如果 a 小于 b,则该方法返回小于 0 的值。
下面列举了 JDK 类库中实现 Comparable 接口的类,以及这些类对象的比较方式。
| 类 | 比较方式 | |
|---|---|---|
| 包装类(BigDecimal、Biglnteger、 Byte、Double、 Float、Integer、Long 及 Short) |
按数字大小比较 | |
| Character | 按字符的 Unicode 值的数字大小比较 | |
| String | 按字符串中字符的 Unicode 值的数字大小比较 | |
TreeSet 类除了实现 Collection 接口的所有方法之外,还提供了如下的方法。
| 方法名称 | 说明 |
|---|---|
| E first() | 返回此集合中的第一个元素。其中,E 表示集合中元素的数据类型 |
| E last() | 返回此集合中的最后一个元素 |
| E poolFirst() | 获取并移除此集合中的第一个元素 |
| E poolLast() | 获取并移除此集合中的最后一个元素 |
| SortedSet<E> subSet(E fromElement,E toElement) | 返回一个新的集合,新集合包含原集合中 fromElement 对象与 toElement 对象之间的所有对象。包含 fromElement 对象,不包含 toElement 对象 |
| SortedSet<E> headSet<E toElement〉 | 返回一个新的集合,新集合包含原集合中 toElement 对象之前的所有对象。 不包含 toElement 对象 |
| SortedSet<E> tailSet(E fromElement) | 返回一个新的集合,新集合包含原集合中 fromElement 对象之后的所有对 象。包含 fromElement 对象 |
注意:表面上看起来这些方法很多,其实很简单。因为 TreeSet 中的元素是有序的,所以增加了访问第一个、前一个、后一个、最后一个元素的方法,并提供了 3 个从 TreeSet 中截取子 TreeSet 的方法。
public class Test14 { public static void main(String[] args) { //创建TreeSet集合 TreeSet<Double> doubles = new TreeSet<>(); Scanner scanner = new Scanner(System.in); System.out.println("-----------------学生成绩管理系统----------------"); for (int i = 0; i < 5; i++) { System.out.print("第"+(i+1)+"学生的成绩为:"); double score = scanner.nextDouble(); //将学生成绩添加到doubles集合中 doubles.add(score); } System.out.println("学生成绩由低到高排序如下:"); //创建iterator对象 Iterator<Double> iterator = doubles.iterator(); //根据迭代器输出值 while (iterator.hasNext()){ //输出值 System.out.println(iterator.next()); } System.out.println("请输入要查询的成绩:"); double searchScore = scanner.nextDouble(); //Java的contains方法是String类的一种方法,它可以检查一个字符串中是否包含指定的字符串。它是一个boolean类型的方法,即如果传入的字符串在给定字符串中,则返回true;如果不在,则返回false。 if(doubles.contains(searchScore)){ System.out.print("成绩为:"+searchScore+"的学生存在"); }else{ System.out.print("成绩为:"+searchScore+"的学生不存在!!!"); } //查看成绩不及格的学生成绩 //headSet返回一个新的集合,新集合包含原集合中 toElement 对象之前的所有对象。不包含 toElement 对象 SortedSet<Double> doubles1 = doubles.headSet(60.0); System.out.println("不及格的成绩有:"); for (int i = 0; i < doubles1.toArray().length; i++) { System.out.println(doubles1.toArray()[i]); } //查询90分以上的学生成绩 //返回一个新的集合,新集合包含原集合中 fromElement 对象之后的所有对象。包含 fromElement 对象 SortedSet<Double> doubles2 = doubles.tailSet(90.0); System.out.println("90分以上的成绩有:"); for (int i = 0; i < doubles2.toArray().length; i++) { System.out.println(doubles2.toArray()[i]); } } }
注意:在使用自然排序时只能向 TreeSet 集合中添加相同数据类型的对象,否则会抛出 ClassCastException 异常。如果向 TreeSet 集合中添加了一个 Double 类型的对象,则后面只能添加 Double 对象,不能再添加其他类型的对象,例如 String 对象等。
六、Java Map集合
Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含一个键(key)对象和一个值(value)对象。用于保存具有映射关系的数据。
Map 集合里保存着两组值,一组值用于保存 Map 里的 key,另外一组值用于保存 Map 里的 value,key 和 value 都可以是任何引用类型的数据。Map 的 key 不允许重复,value 可以重复,即同一个 Map 对象的任何两个 key 通过 equals 方法比较总是返回 false。
Map 中的 key 和 value 之间存在单向一对一关系,即通过指定的 key,总能找到唯一的、确定的 value。从 Map 中取出数据时,只要给出指定的 key,就可以取出对应的 value。
Map 接口主要有两个实现类:HashMap 类和 TreeMap 类。其中,HashMap 类按哈希算法来存取键对象,而 TreeMap 类可以对键对象进行排序。
Map 接口中提供的常用方法如表 1 所示。
| 方法名称 | 说明 |
|---|---|
| void clear() | 删除该 Map 对象中的所有 key-value 对。 |
| boolean containsKey(Object key) | 查询 Map 中是否包含指定的 key,如果包含则返回 true。 |
| boolean containsValue(Object value) | 查询 Map 中是否包含一个或多个 value,如果包含则返回 true。 |
| V get(Object key) | 返回 Map 集合中指定键对象所对应的值。V 表示值的数据类型 |
| V put(K key, V value) | 向 Map 集合中添加键-值对,如果当前 Map 中已有一个与该 key 相等的 key-value 对,则新的 key-value 对会覆盖原来的 key-value 对。 |
| void putAll(Map m) | 将指定 Map 中的 key-value 对复制到本 Map 中。 |
| V remove(Object key) | 从 Map 集合中删除 key 对应的键-值对,返回 key 对应的 value,如果该 key 不存在,则返回 null |
| boolean remove(Object key, Object value) | 这是 Java 8 新增的方法,删除指定 key、value 所对应的 key-value 对。如果从该 Map 中成功地删除该 key-value 对,该方法返回 true,否则返回 false。 |
| Set entrySet() | 返回 Map 集合中所有键-值对的 Set 集合,此 Set 集合中元素的数据类型为 Map.Entry |
| Set keySet() | 返回 Map 集合中所有键对象的 Set 集合 |
| boolean isEmpty() | 查询该 Map 是否为空(即不包含任何 key-value 对),如果为空则返回 true。 |
| int size() | 返回该 Map 里 key-value 对的个数 |
| Collection values() | 返回该 Map 里所有 value 组成的 Collection |
Map 集合最典型的用法就是成对地添加、删除 key-value 对,接下来即可判断该 Map 中是否包含指定 key,也可以通过 Map 提供的 keySet() 方法获取所有 key 组成的集合,进而遍历 Map 中所有的 key-value 对。
public class Test15 { public static void main(String[] args) { HashMap<String, String> users = new HashMap<>(); //将学生信息键值对存储到map中 users.put("1001","曹长卿"); users.put("1002","李淳罡"); users.put("1003","王仙之"); users.put("1004","邓太阿"); users.put("1005","赵宣素"); System.out.println("---------------学生信息------------------"); //通过迭代器,取map集合的key,然后根据key取值 Iterator<String> iterator = users.keySet().iterator(); while (iterator.hasNext()){ //遍历map,取出key String key = iterator.next(); //根据key取对应的值 String val = users.get(key); System.out.println("学号:"+key+", 姓名:"+val); } Scanner scanner = new Scanner(System.in); System.out.print("请输入要删除的学生学号:"); //输入学号 int num = scanner.nextInt(); //将输入的学号,String.valueOf将非字符串,转换为字符串,containsKey判断是在map集合中是否存在某个键值对 if(users.containsKey(String.valueOf(num))){ users.remove(String.valueOf(num));//删除元素 }else { System.out.println("该学生不存在!"); } System.out.println("---------------学生列表------------------"); Iterator<String> iterator1 = users.keySet().iterator(); while (iterator1.hasNext()){ //取出key String key = iterator1.next(); //根据key取值 String val = users.get(key); System.out.println("学号:" + key + ",姓名:" + val); } } }
代码执行后结果如下:
---------------学生信息------------------ 学号:1005, 姓名:赵宣素 学号:1004, 姓名:邓太阿 学号:1003, 姓名:王仙之 学号:1002, 姓名:李淳罡 学号:1001, 姓名:曹长卿 请输入要删除的学生学号:1005 ---------------学生列表------------------ 学号:1004,姓名:邓太阿 学号:1003,姓名:王仙之 学号:1002,姓名:李淳罡 学号:1001,姓名:曹长卿
注意:TreeMap 类的使用方法与 HashMap 类相同,唯一不同的是 TreeMap 类可以对键对象进行排序,这里不再赘述。
七、Java遍历Map集合的四种方式
Map 集合的遍历与 List 和 Set 集合不同。Map 有两组值,因此遍历时可以只遍历值的集合,也可以只遍历键的集合,也可以同时遍历。Map 以及实现 Map 的接口类(如 HashMap、TreeMap、LinkedHashMap、Hashtable 等)都可以用以下几种方式遍
1)在 for 循环中使用 entries 实现 Map 的遍历(最常见和最常用的)。
public class Test16 { public static void main(String[] args) { HashMap<String, String> map = new HashMap<>(); //设置值 map.put("李淳罡","剑道万古如长夜,天不生我李淳罡"); map.put("曹长卿","官子无敌曹长卿"); //entrySet是 java中 键-值 对的集合,Set里面的类型是Map.Entry,一般可以通过map.entrySet()得到。 //entrySet实现了Set接口,里面存放的是键值对。一个K对应一个V。 //entrySet取 for (Map.Entry<String, String> entry : map.entrySet()) { System.out.println("key:"+entry.getKey()+", value:"+entry.getValue()); } } }
2)使用 for-each 循环遍历 key 或者 values,一般适用于只需要 Map 中的 key 或者 value 时使用。性能上比 entrySet 较好。
public class Test17 { public static void main(String[] args) { HashMap<String, String> map = new HashMap<>(); //设置值 map.put("李淳罡","剑道万古如长夜,天不生我李淳罡"); map.put("曹长卿","官子无敌曹长卿"); //输出key的集合 for (String key : map.keySet()) { System.out.println(key); } //输出value的集合 for (String val : map.values()) { System.out.println(val); } } }
3)使用迭代器(Iterator)遍历
public class Test18 { public static void main(String[] args) { HashMap<String, String> map = new HashMap<>(); //设置值 map.put("李淳罡", "剑道万古如长夜,天不生我李淳罡"); map.put("曹长卿", "官子无敌曹长卿"); Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator(); while (iterator.hasNext()){ //返回一个值 Map.Entry<String, String> entry = iterator.next(); //取key String key = entry.getKey(); //取值 String value = entry.getValue(); System.out.println("key:"+key+",value:"+value); } } }
4)通过键找值遍历,这种方式的效率比较低,因为本身从键取值是耗时的操作。
public class Test18 { public static void main(String[] args) { HashMap<String, String> map = new HashMap<>(); //设置值 map.put("李淳罡", "剑道万古如长夜,天不生我李淳罡"); map.put("曹长卿", "官子无敌曹长卿"); Set<String> strings = map.keySet(); for (String key : strings) { //根据key取值 String s = map.get(key); System.out.println("key的值:"+key+", value的值为:"+s); } } }
八、Java Collections类操作集合
Collections 类是 Java 提供的一个操作 Set、List 和 Map 等集合的工具类。Collections 类提供了许多操作集合的静态方法,借助这些静态方法可以实现集合元素的排序、查找替换和复制等操作。下面介绍 Collections 类中操作集合的常用方法。
public class Test19 { public static void main(String[] args) { //Collections不支持创建对象,因为构造器私有化了 //里面的属性和方法都是被static修饰,我们可以直接用类名.去调用即可: //addAll方法,同时给集合中添加多个值 ArrayList<String> list = new ArrayList<>(); list.add("青鸟"); list.add("红薯"); list.add("姜泥"); Collections.addAll(list,"清风","玄鸟","朱奇"); Collections.addAll(list,new String[]{"两袖青蛇","一剑开天门"}); System.out.println(list); Collections.sort(list);//sort提供的升序排列 System.out.println(list); //binarySearch必须在有序的集合中查找:-->排序,输出索引 System.out.println(Collections.binarySearch(list, "姜泥")); //copy替换方法,会将新的集合中的元素拷贝到到之前的集合,替换到之前集合的元素 ArrayList<String> list1 = new ArrayList<>(); Collections.addAll(list1,"aa","bb","cc"); //将list1的内容替换到list上去 Collections.copy(list,list1); System.out.println(list); System.out.println(list1); //fill 填充 Collections.fill(list1,"yyyy"); //将list1集合中的元素使用 yyyy 替换掉 System.out.println(list1); } }

浙公网安备 33010602011771号