Java集合

一、为什么需要集合?

在Java中可以使用数组来保存多个对象,但数组长度不可变化,一旦在初始化数组时指定了数组长度,这个数组长度就是不可变的。如果需要保存数量变化的数据,数组就有点无能为力了。而且数组无法保存具有映射关系的数据,如成绩表为语文——79,数学——80,这种数据看上去像两个数组,但这两个数组的元素之间有一定的关联关系。

为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),Java 提供了集合类。特点如下:

  • 集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。
  • Java 所有的集合类都位于 java.util 包下,提供了一个表示和操作对象集合的统一构架,包含大量集合接口,以及这些接口的实现类和操作它们的算法。
  • 集合类和数组不一样,数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量),而集合里只能保存对象(实际上只是保存对象的引用变量,但通常习惯上认为集合里保存的是对象)。

二、Java中集合类型说明

Java 集合类型分为 CollectionMap,它们是 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 介绍了集合中这些常用的实现类。

表 2 Java集合实现类的作用
类名称作用
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 集合。

表 1 Collection接口的常用方法
方法名称说明
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);
    }
}
posted @ 2023-06-02 11:09  酒剑仙*  阅读(26)  评论(0)    收藏  举报