Java系列学习笔记 --- 集合(1) 概述及Collection接口

目录

前言

一、集合概述

二、Collection接口

2.1 Java8新增的操作方法

       (1)、Predicate操作

       (2)、Steam操作

三、遍历集合

3.1 使用Lambda表达式遍历集合

3.2 使用Iterator遍历集合元素

       (1)、使用while循环遍历Collection集合

       (2)、使用Lambda表达式遍历Iterator

3.3 使用forEach循环遍历集合元素

四、总结


前言

       Java集合类可用于存储数量不等的对象或具有映射关系的关联数组,并实现常用的数据结构,如栈、队列等。Java集合大致可分为Set、List、Queue和Map四种体系,其中Set代表无序、不可重复的集合List代表有序、重复的集合Map代表具有映射关系的集合,而Query体系集合代表队列集合

       Java集合能够存储多个对象(实际都是对象的引用),在Java 5之前,集合中的所有对象都是Object类型,但是泛型的出现能够使集合记住集合中所有对象的数据类型。

一、集合概述

       数组也能够存放多个数据,但是数组的长度在初始化的时候就已经固定了,也就是说他的长度以及固定的了,不会随着数组元素的多少而改变长度,如果需要存放未知数量的数据(可能是大量也可能是少量)或者具有映射关系的数据,数组便无能为力了。

       为了保存数量不确定的数据,以及保存具有映射关系的集合,Java提供了集合类。所有集合类都位于java.util包下,后来为了处理多线程环境下的并发安全问题,Java还在util.concurrent包下提供了一些支持多线程的集合类。

       数组元素既可以保存基本类型的值,也可以是对象;而集合里只能保存对象(例如基本类型int它对应的是Integer对象)。

       Java集合类主要由两个接口派生而出:Collection和Map,如下图所示

       如下图所示就是Map体系的继承树,所有Map存储具有映射关系的数据。MAP接口的显示类都有一个功能特征:Map保存的数据都是key-value对(键值对),并且Map中的key是不可重复的,只会替换掉,如果想要查阅指定的数据,只能通过Map中的key来获取。

       总的来说,Set集合无法记住添加元素的顺序,只能根据元素本身来访问,所以里面的元素不能重复,否则无法准确识别元素;List集合和数组很像,能够记住每次添加元素的顺序,通过指定下标来访问元素,相对数组,List集合的长度更有弹性,能够根据集合数量的改变而发生变化;Map集合存储的是键值对,能够通过键名去访问对象,这个键名不但是字符串而且还不能重复。

二、Collection接口

       该接口是List、Set和Queue接口的父接口,它定义了操作集合元素的方法,例如添加对象、删除对象、清空集合、判断集合是否为空或者是否包含某个元素。

  •        boolean add(Object o):该方法用于向集合添加一个元素
  •        boolean addAll(Collection c):该方法把集合c里所有的元素添加到指定集合里。
  •        removeAll():删除集合中第一个指定元素
  •        int size():返回集合里元素的个数,即长度
  •        boolean isEmpty():返回集合是否为空
  •        boolean contains(Object o):返回集合是否包含指定元素
  •        iterator():返回Iterator对象,用于遍历集合里的元素
  •        void clear():清除集合里的所有元素,集合长度变为0

       上面只是列出了常用的方法,还有很多方法可查阅官方手册。下面的程序示范了如何通过上面的方法来操作Collection集合里的元素。

Collection c = new ArrayList();
c.add("张三");
c.add("男");
c.add(18);
System.out.println(c);
System.out.println("元素个数:" + c.size());
System.out.println("元素‘男’是否存在?" + c.contains("男"));
c.remove("男");
System.out.println("删除对象男之后数组为:"+c);
System.out.println("元素‘男’是否存在?" + c.contains("男"));
System.out.println("元素个数:" + c.size());
		
Collection c1 = new HashSet();
c1.add("李四");
c1.add(21);
System.out.println("c集合是否包含c1集合?" + c.containsAll(c1));
c.addAll(c1);
System.out.println("c集合是否包含c1集合?" + c.containsAll(c1));
		
c.removeAll(c1);
System.out.println("---删除集合c1里的所有元素---");
System.out.println("c1集合是否为空?"+c1.isEmpty());
System.out.println("c集合是否包含c1集合?" + c.containsAll(c1));
c.clear();
System.out.println("c集合是否包含c1集合?" + c.containsAll(c1));
System.out.println("c集合的元素:"+c);

       最终的输出结果如下所示:

2.1 Java8新增的操作方法

(1)、Predicate操作

       Java 8为Collection集合新增了一个removeIf(Predicate filter)方法,该方法将会批量删除符合filter条件的所有元素。Predicate也是函数式接口,因此可使用Lambda表达式作为参数。例如下面实例所示:

Collection num = new ArrayList();
num.add("124234");
num.add("1247896");
num.add("823467");
num.add("3478");
num.add("234789");
System.out.println("删除之前的集合对象包含:"+num);
num.removeIf(elem -> ((String)elem).length() < 5);
num.removeIf(elem -> ((String)elem).contains("823467"));
System.out.println("删除之后的集合对象包含:"+num);

(2)、Steam操作

       Java 8还新增了Stream、IntStream、LongStream、DoubleStream等流式API,这些API代表多个支持串行和并行聚集操作的元素。上面4个接口中,Stream是通用的流接口,而后面三个则代表元素类型为int、long、double的流。

       独立使用Stream的步骤如下

  •        使用Stream或XxxStream的builder()类方法创建该Stream对应的Builder。
  •        重复调用Builder的add()方法向该流中添加多个元素。
  •        调用Builder的build()方法获取对应的Stream
  •        调用Stream的聚集方法

       聚集方法可以根据需求来调用不同的方法,但是需要注意的是,每个Stream只能执行一次聚集方法。例如下面代码范例所示

//创建int流并为其添加数据流,最后创建
IntStream is = IntStream.builder().add(10)
                                  .add(20)
                                  .add(30)
                                  .add(40)
                                  .build();
//下面调用聚集方法的代码,每次只能只需一行
System.out.println("IS所有元素的最大值:"+is.max().getAsInt());
System.out.println("IS所有元素的最小值:"+is.min().getAsInt());
System.out.println("IS所有元素的总和:"+is.sum());
System.out.println("IS所有元素的总数:"+is.count());
System.out.println("IS所有元素的平均数:"+is.average());
System.out.println("元素的是否都大于20:"+is.allMatch(ele -> ele>20));
IntStream newIs = is.map(ele -> ele*2);
newIs.forEach(System.out::println);

       最终的输出结果如下图所示


       Stream提供了大量的方法进行聚集操作,这方法可以是“中间的”,也可以是“末端的”。

  •        中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法。上面程序中的map()方法就是中间方法,它的返回值是另外一个流。
  •        末端方法:末端方法是对流的最终操作。当对某个和Stream执行末端方法之后,该流将会被“消耗”且不可再用。

       除此之外,关于流的方法还有如下两个特征。

  •        有状态方法:这种方法会给流增加一些新的属性,例如元素唯一性、元素最大数量、保证元素以排序的方式被处理等,这往往需要更大的性能开销。
  •        短路方法:短路方法可以尽早结束对流的操作,不必检擦所有元素。

三、遍历集合

       上面的代码中,可以通过System.out.println()来输出集合对象,这显然是因为所有Collection实现类都重写了toStrong()方法,该方法可以一次性地输出集合中的所有元素。

       如果想要依次访问集合里的每一个元素,则需要使用以下两种遍历集合的常用方法。

3.1 使用Lambda表达式遍历集合

       Java 8为Interable接口新增forEach(consumer action)默认方法,该方法所需参数的类型是一个函数式接口,而Ierable是Collection接口的父接口,因此Collection集合也可以直接调用该方法。

       当程序调用Iterable的forEach方法遍历集合元素时,程序会依次将集合元素传给Consumer的accept(T t)方法,正因为Consumer是函数式接口,因此可以直接使用Lambda表达式来遍历集合元素。

//创建集合并插入对象
Collection persons = new HashSet();
persons.add("张三");
persons.add("李四");
persons.add("王五");
//调用forEach()方法
persons.forEach(obj -> System.out.println("迭代集合元素:" + obj));

3.2 使用Iterator遍历集合元素

       Iterator接口也是Java集合框架的成员,但与它们不一样的是,Iterator接口主要用来遍历(即迭代访问)Collection集合中的元素,而非存储。所以,Iterator对象也被称为迭代器。它定义了如下4个方法:

       - boolean hasNext():如果被迭代的集合还没有被遍历完,则返回true

       - Object next():返回集合里的下一个元素

       - void remove():删除集合里上一次next方法返回的元素

       - void forEachRemaining(Consumer action):使用Lambda表达式遍历集合

       下面的程序范例演示了如何通过Iterator接口对集合进行操作

(1)、使用while循环遍历Collection集合

//创建集合并插入对象
Collection persons = new ArrayList();
persons.add("李一");
persons.add("陈二");
persons.add("张三");
persons.add("李四");
persons.add("王五");
persons.add("周六");
System.out.println("迭代前的元素为:" + persons);

//获取persons对象的迭代器
Iterator iter = persons.iterator();
//通过while循环遍历集合对象(判断下一个指标是否存在集合对象)
while(iter.hasNext()){
	//通过next()移动指标到下一个指标的位置,并返回该指标下的值
	String person = (String) iter.next();
	System.out.println("当前迭代对象的值为:" + person);
	if(person.equals("张三") || person.equals("王五")){
		iter.remove();	//删除当前迭代元素
	}
}
System.out.println("迭代后的元素为:" + persons);

       输出结果如下所示

       需要注意的是,想要创建Iterator对象就必须依附一个被迭代的集合对象,并且在遍历的时候不能改变(删除、替换等)Collection集合元素,否则会引发异常。实际上,在迭代的时候删除集合元素是一种非常危险的行为,程序员也不应该这么做。

(2)、使用Lambda表达式遍历Iterator

//创建集合并插入对象
Collection persons = new ArrayList();
persons.add("李一");
persons.add("陈二");
persons.add("张三");
persons.add("李四");
persons.add("王五");
persons.add("周六");
System.out.println("迭代前的元素为:" + persons);
//获取persons对象的迭代器
Iterator iter = persons.iterator();
iter.forEachRemaining(obj -> System.out.println("对象值为:"+obj));

       输出结果如下所示

       需要注意的是,不能存在两种迭代,因为它的迭代是通过移动指针的形式进行迭代的,所以在上一次迭代完成之后,当前指针已经在最末尾了,所以再次迭代时的结果将为空。

3.3 使用forEach循环遍历集合元素

       除了使用Iterator接口迭代访问Collection集合里的元素外,还可以使用foreach循环来迭代访问集合元素。

//创建集合并插入对象
Collection persons = new ArrayList();
persons.add("李一");
persons.add("陈二");
persons.add("张三");
persons.add("李四");
persons.add("王五");
persons.add("周六");
//使用forEach循环迭代元素
for(Object obj:persons){
    System.out.println("当前迭代的元素:"+obj);
}

       输出结果如下所示

四、总结

       1、集合能够存储未知数量的元素或具有映射关系的关联数组,需要注意的是,数组能够存储普通元素和对象,而集合只能够存储对象或键值对。

       2、Set集合存储无序且不能重复的集合;List集合存储有序可以重复的集合;Map集合存储具有映射关系的集合;Query体系存储队列集合。

       3、Iterator接口节Collection接口的迭代器,Collection接口派生出Set、List和Query接口。

       4、Java 8为Collection添加了一些操作方法,这些方法可以用来获取元素的最大值、最小值、是否包含某个关键字等等

       5、想要创建对应类型流,需要通过XxxStream流的builder()方法创建该流对应的Builder,并通过add()方法为其填充数据,之后再调用build()创建流获取对应的Stream。

       6、流的聚集函数如果是末端方法只能执行一次,之后就会对流进行关闭,所以这个末端方法之后的所有方法都无法执行。

       7、遍历集合的方式常用的有三种,通过Collection自带的forEach()方法利用Lambda表达式来遍历集合;使用Iterator接口来迭代元素有两种方式,一种是通过while循环来遍历,另外一种是通过forEachRemaining()方法;或者使用Java 8提供的forEach循环迭代元素。

       需要注意的是:Collection的forEach()方法是通过循环来遍历的,而Iterator接口的遍历是通过指针来访问数据的,所以它的遍历会对后续的遍历产生影响。

猜你喜欢

转载自blog.csdn.net/Rao_Limon/article/details/89242095