Java集合详解
可以使用数组来保存多个对象,但数组长度不可变化;为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),Java 提供了集合类。
集合类和数组不一样,数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量),而集合里只能保存对象
Java 集合类型分为 Collection 和 Map,它们是 Java 集合的根接口,这两个接口又包含了一些子接口或实现类。
Collection接口详解
Collection 接口是 List、Set 和 Queue 接口的父接口,通常情况下不被直接使用。Collection 接口定义了一些通用的方法,通过这些方法可以实现对集合的基本操作。定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
- public static void main(String[] args) {
- ArrayList list1 = new ArrayList(); // 创建集合 list1
- ArrayList list2 = new ArrayList(); // 创建集合 list2
- list1.add("one"); // 向 list1 添加一个元素
- list1.add("two"); // 向 list1 添加一个元素
- list2.addAll(list1); // 将 list1 的所有元素添加到 list2
- list2.add("three"); // 向 list2 添加一个元素
- System.out.println("list2 集合中的元素如下:");
- Iterator it1 = list2.iterator();
- while (it1.hasNext()) {
- System.out.print(it1.next() + "、");
- }
- }
- public static void main(String[] args) {
- ArrayList list1 = new ArrayList(); // 创建集合 list1
- ArrayList list2 = new ArrayList(); // 创建集合 list2
- list1.add("one");
- list1.add("two");
- list1.add("three");
- System.out.println("list1 集合中的元素数量:" + list1.size()); // 输出list1中的元素数量
- list2.add("two");
- list2.add("four");
- list2.add("six");
- System.out.println("list2 集合中的元素数量:" + list2.size()); // 输出list2中的元素数量
- list2.remove(2); // 删除第 3 个元素
- System.out.println(" removeAll() 方法之后 list2 集合中的元素数量:" + list2.size());
- System.out.println("list2 集合中的元素如下:");
- Iterator it1 = list2.iterator();
- while (it1.hasNext()) {
- System.out.print(it1.next() + "、");
- }
- list1.removeAll(list2);
- System.out.println(" removeAll() 方法之后 list1 集合中的元素数量:" + list1.size());
- System.out.println("list1 集合中的元素如下:");
- Iterator it2 = list1.iterator();
- while (it2.hasNext()) {
- System.out.print(it2.next() + "、");
- }
- }
注意:retainAll( ) 方法的作用与 removeAll( ) 方法相反,即保留两个集合中相同的元素,其他全部删除。
Java List集合:ArrayList和LinkedList类的用法及区别
List 是一个有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List 集合默认按元素的添加顺序设置元素的索引,第一个添加到 List 集合中的元素的索引为 0,第二个为 1,依此类推。
ArrayList 类
ArrayList 类实现了可变数组的大小,存储在内的数据称为元素。它还提供了快速基于索引访问元素的方式,对尾部成员的增加和删除支持较好。使用 ArrayList 创建的集合,允许对集合中的元素进行快速的随机访问,不过,向 ArrayList 中插入与删除元素的速度相对较慢。
ArrayList 类的常用构造方法有如下两种重载形式:
- ArrayList():构造一个初始容量为 10 的空列表。
- ArrayList(Collection<?extends E>c):构造一个包含指定 Collection 元素的列表,这些元素是按照该 Collection 的迭代器返回它们的顺序排列的。
ArrayList 类除了包含 Collection 接口中的所有方法之外,还包括 List 接口中提供的如表 1 所示的方法。
注意:当调用 List 的 set(int index, Object element) 方法来改变 List 集合指定索引处的元素时,指定的索引必须是 List 集合的有效索引。例如集合长度为 4,就不能指定替换索引为 4 处的元素,也就是说这个方法不会改变 List 集合的长度。
例 1
使用 ArrayList 类向集合中添加三个商品信息,包括商品编号、名称和价格,然后遍历集合输出这些商品信息。
- public class Product {
- // 商品类
- private int id; // 商品编号
- private String name; // 名称
- private float price; // 价格
- public Product(int id, String name, float price) {
- this.name = name;
- this.id = id;
- this.price = price;
- }
- // 这里是上面3个属性的setter/getter方法,这里省略
- public String toString() {
- return "商品编号:" + id + ",名称:" + name + ",价格:" + price;
- }
- }
- public class Test {
- public static void main(String[] args) {
- Product pd1 = new Product(4, "木糖醇", 10);
- Product pd2 = new Product(5, "洗发水", 12);
- Product pd3 = new Product(3, "热水壶", 49);
- List list = new ArrayList(); // 创建集合
- list.add(pd1);
- list.add(pd2);
- list.add(pd3);
- System.out.println("*************** 商品信息 ***************");
- for (int i = 0; i < list.size(); i++) {
- // 循环遍历集合,输出集合元素
- Product product = (Product) list.get(i);
- System.out.println(product);
- }
- }
- }
例 2
- public static void main(String[] args) {
- List list = new ArrayList();
- list.add("One");
- list.add("|");
- list.add("Two");
- list.add("|");
- list.add("Three");
- list.add("|");
- list.add("Four");
- System.out.println("list 集合中的元素数量:" + list.size());
- System.out.println("list 集合中的元素如下:");
- Iterator it = list.iterator();
- while (it.hasNext()) {
- System.out.print(it.next() + "、");
- }
- System.out.println(" 在 list 集合中'丨'第一次出现的位置是:" + list.indexOf("|"));
- System.out.println("在 list 集合中'丨'最后一次出现的位置是:" + list.lastIndexOf("|"));
- }
例 3
- public static void main(String[] args) {
- List list = new ArrayList();
- list.add("One");
- list.add("Two");
- list.add("Three");
- list.add("Four");
- list.add("Five");
- list.add("Six");
- list.add("Seven");
- System.out.println("list 集合中的元素数量:" + list.size());
- System.out.println("list 集合中的元素如下:");
- Iterator it = list.iterator();
- while (it.hasNext()) {
- System.out.print(it.next() + "、");
- }
- List sublist = new ArrayList();
- sublist = list.subList(2, 5); // 从list集合中截取索引2~5的元素,保存到sublist集合中
- System.out.println(" sublist 集合中元素数量:" + sublist.size());
- System.out.println("sublist 集合中的元素如下:");
- it = sublist.iterator();
- while (it.hasNext()) {
- System.out.print(it.next() + "、");
- }
- }
LinkedList类
LinkedList 类采用链表结构保存对象,这种结构的优点是便于向集合中插入或者删除元素。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高,但是 LinkedList 类随机访问元素的速度则相对较慢。这里的随机访问是指检索集合中特定索引位置的元素。
LinkedList 类除了包含 Collection 接口和 List 接口中的所有方法之外,还特别提供了表 2 所示的方法。
例 4
- public class Test {
- public static void main(String[] args) {
- LinkedList<String> products = new LinkedList<String>(); // 创建集合对象
- String p1 = new String("六角螺母");
- String p2 = new String("10A 电缆线");
- String p3 = new String("5M 卷尺");
- String p4 = new String("4CM 原木方板");
- products.add(p1); // 将 p1 对象添加到 LinkedList 集合中
- products.add(p2); // 将 p2 对象添加到 LinkedList 集合中
- products.add(p3); // 将 p3 对象添加到 LinkedList 集合中
- products.add(p4); // 将 p4 对象添加到 LinkedList 集合中
- String p5 = new String("标准文件夹小柜");
- products.addLast(p5); // 向集合的末尾添加p5对象
- System.out.print("*************** 商品信息 ***************");
- System.out.println(" 目前商品有:");
- for (int i = 0; i < products.size(); i++) {
- System.out.print(products.get(i) + " ");
- }
- System.out.println(" 第一个商品的名称为:" + products.getFirst());
- System.out.println("最后一个商品的名称为:" + products.getLast());
- products.removeLast(); // 删除最后一个元素
- System.out.println("删除最后的元素,目前商品有:");
- for (int i = 0; i < products.size(); i++) {
- System.out.print(products.get(i) + " ");
- }
- }
- }
ArrayList 类和 LinkedList 类的区别
ArrayList 是基于动态数组数据结构的实现,访问元素速度优于 LinkedList。LinkedList 是基于链表数据结构的实现,占用的内存空间比较大,但在批量插入或删除数据时优于 ArrayList。
Set集合:HashSet和TreeSet类
Set 集合中不能包含重复的对象,并且最多只允许包含一个 null 元素。
HashSet 类
HashSet 是按照 Hash 算法来存储集合中的元素。因此具有很好的存取和查找性能。
HashSet 具有以下特点:
- 不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
- HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步。
- 集合元素值可以是 null。
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals() 方法比较返回的结果为 true,但它们的 hashCode 不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。
也就是说,两个对象的 hashCode 值相等且通过 equals() 方法比较返回结果为 true,则 HashSet 集合认为两个元素相等。
- HashSet hs = new HashSet(); // 调用无参的构造函数创建HashSet对象
- HashSet<String> hss = new HashSet<String>(); // 创建泛型的 HashSet 集合对象
- public static void main(String[] args) {
- HashSet<String> courseSet = new HashSet<String>(); // 创建一个空的 Set 集合
- String course1 = new String("Java入门教程");
- String course2 = new String("Python基础教程");
- String course3 = new String("C语言学习教程");
- String course4 = new String("Golang入门教程");
- courseSet.add(course1); // 将 course1 存储到 Set 集合中
- courseSet.add(course2); // 将 course2 存储到 Set 集合中
- courseSet.add(course3); // 将 course3 存储到 Set 集合中
- courseSet.add(course4); // 将 course4 存储到 Set 集合中
- System.out.println("C语言中文网教程有:");
- Iterator<String> it = courseSet.iterator();
- while (it.hasNext()) {
- System.out.println("《" + (String) it.next() + "》"); // 输出 Set 集合中的元素
- }
- System.out.println("有" + courseSet.size() + "套精彩教程!");
- }
TreeSet 类
TreeSet 类同时实现了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口,可以实现对集合进行自然排序,这里的自然排序指的是升序排序。
TreeSet 只能对实现了 Comparable 接口的类对象进行排序,因为 Comparable 接口中有一个 compareTo(Object o) 方法用于比较两个对象的大小。
如果 a 和 b 相等,则该方法返回 0;如果 a 大于 b,则该方法返回大于 0 的值;如果 a 小于 b,则该方法返回小于 0 的值。
- public class Test08 {
- public static void main(String[] args) {
- TreeSet<Double> scores = new TreeSet<Double>(); // 创建 TreeSet 集合
- Scanner input = new Scanner(System.in);
- System.out.println("------------学生成绩管理系统-------------");
- for (int i = 0; i < 5; i++) {
- System.out.println("第" + (i + 1) + "个学生成绩:");
- double score = input.nextDouble();
- // 将学生成绩转换为Double类型,添加到TreeSet集合中
- scores.add(Double.valueOf(score));
- }
- Iterator<Double> it = scores.iterator(); // 创建 Iterator 对象
- System.out.println("学生成绩从低到高的排序为:");
- while (it.hasNext()) {
- System.out.print(it.next() + " ");
- }
- System.out.println(" 请输入要查询的成绩:");
- double searchScore = input.nextDouble();
- if (scores.contains(searchScore)) {
- System.out.println("成绩为: " + searchScore + " 的学生存在!");
- } else {
- System.out.println("成绩为: " + searchScore + " 的学生不存在!");
- }
- // 查询不及格的学生成绩
- SortedSet<Double> score1 = scores.headSet(60.0);
- System.out.println(" 不及格的成绩有:");
- for (int i = 0; i < score1.toArray().length; i++) {
- System.out.print(score1.toArray()[i] + " ");
- }
- // 查询90分以上的学生成绩
- SortedSet<Double> score2 = scores.tailSet(90.0);
- System.out.println(" 90 分以上的成绩有:");
- for (int i = 0; i < score2.toArray().length; i++) {
- System.out.print(score2.toArray()[i] + " ");
- }
- }
- }
注意:在使用自然排序时只能向 TreeSet 集合中添加相同数据类型的对象,否则会抛出 ClassCastException 异常
Map集合详解
Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含一个键(key)对象和一个值(value)对象。用于保存具有映射关系的数据。
Map 的 key 不允许重复,value 可以重复
Map 接口主要有两个实现类:HashMap 类和 TreeMap 类。其中,HashMap 类按哈希算法来存取键对象,而 TreeMap 类可以对键对象进行排序。
- public class Test09 {
- public static void main(String[] args) {
- HashMap users = new HashMap();
- users.put("11", "张浩太"); // 将学生信息键值对存储到Map中
- users.put("22", "刘思诚");
- users.put("33", "王强文");
- users.put("44", "李国量");
- users.put("55", "王路路");
- System.out.println("******** 学生列表 ********");
- Iterator it = users.keySet().iterator();
- while (it.hasNext()) {
- // 遍历 Map
- Object key = it.next();
- Object val = users.get(key);
- System.out.println("学号:" + key + ",姓名:" + val);
- }
- Scanner input = new Scanner(System.in);
- System.out.println("请输入要删除的学号:");
- int num = input.nextInt();
- if (users.containsKey(String.valueOf(num))) { // 判断是否包含指定键
- users.remove(String.valueOf(num)); // 如果包含就删除
- } else {
- System.out.println("该学生不存在!");
- }
- System.out.println("******** 学生列表 ********");
- it = users.keySet().iterator();
- while (it.hasNext()) {
- Object key = it.next();
- Object val = users.get(key);
- System.out.println("学号:" + key + ",姓名:" + val);
- }
- }