Java学习笔记一Java集合概述

Java集合就像一种容器,可以把多个对象(实际上是对象的引用)丢进该容器中。在Java 6之前,Java集合会丢失容器中所有对象的数据类型,把所有对象都当成Object类型处理;从Java 5增加了泛型以后,Java集合可以记住容器中对象的数据类型。所有的集合类都位于java.util包下,为了处理多线程环境下的并发安全问题,Java 5还在java.util.concurrent包下提供了一些多线程支持的集合类。数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量);而集合里只能保存对象(实际上只是保存对象的引用变量,但通常习惯上认为集合里保存的是对象)。
Java的集合类主要由两个接口派生的:CollectionMap,Collection和Map是Java集合框架的根接口,这两个接口又包含一些子接口或实现类。

Collection和Iterator接口

Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法既可用于操作Set集合,也可用于操作List和Queue集合。在传统模式下,把一个对象丢进集合中后,集合会忘记这个对象的类型-也就是说,系统把所有的集合都当成Object类型。从JDK 1.5以后,这种状态得到了改进:可以使用泛型来限制集合里元素的类型,并让集合记住所有集合元素的类型。

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

public class CollectionTest {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        // 添加元素
        c.add("悟空");
        // 虽然集合不能放基本类型的值,但Java支持自动装箱
        c.add(6);
        System.out.println("c集合的元素个数为:"+c.size());
        // 删除指定元素
        c.remove(6);
        System.out.println("c集合的元素个数为:"+c.size());
        // 判断是否包含指定字符串
        System.out.println("c集合是否包含\"悟空\"字符串:"+c.contains("悟空"));
        c.add("轻量级Java EE 企业应用实战");
        System.out.println("c集合的元素:"+c);

        Collection books = new HashSet();
        books.add("轻量级Java EE 企业应用实战");
        books.add("疯狂Java讲义");
        System.out.println("c集合是否完全包含books集合?"+c.containsAll(books));
        // 用c集合减去books集合里的元素
        c.removeAll(books);
        System.out.println("c集合的元素:"+c);
        // 删除c集合里的所有元素
        c.clear();
        System.out.println("c集合的元素:"+c);
        // 控制books集合里只剩下c集合里也包含的元素
        books.retainAll(c);
        System.out.println("books集合的元素:"+books);
    }
}

在这里插入图片描述

使用Lambda表达式遍历集合

Java 8为Iterable接口新增了一个forEach(Consumer action)默认方法,该方法所需参数的类型是一个函数式接口,而Iterable接口是Collection接口的父接口,因此Collection集合也可直接调用该方法。当程序调用Iterable的forEach(Consumer action)遍历集合元素时,程序会依次将集合元素传给Consumer的accept(T t)方法(该接口中唯一的抽象方法)。正因为Consumer是函数式接口,因此可以使用Lambda表达式来遍历集合元素。

// 创建一个集合
Collection bookall = new HashSet();
bookall.add("轻量级Java EE 企业应用实战");
bookall.add("疯狂Java讲义");
bookall.add("疯狂Android讲义");
// 调用forEach方法遍历集合
bookall.forEach(obj -> System.out.println("迭代集合元素:"+obj));

在这里插入图片描述

使用Java 8增强的Iterator遍历集合元素

Iterator接口也是Java集合框架的成员,但它与Collection系列、Map系列的集合不一样:Collection系列集合、Map系列集合主要用于盛装其他对象,而Iterator则主要用于遍历(即迭代访问)Collection集合中的元素,Iterator对象也被称为迭代器。Iterator接口隐藏了各种Collection实现类的底层细节,向应用程序提供了遍历Collection集合元素的统一编程接口。Iterator接口里定义了如下方法:boolean hasNext()如果被迭代的集合元素还没有被遍历完,则返回true。Object next()返回集合里的下一个元素。void remove()删除集合里上一次next方法返回的元素。void forEachRemaining(Consumer action)这是Java 8为Iterator新增的默认方法,该方法可使用Lambda表达式来遍历集合元素。

 // 获取bookall集合对应的迭代器
Iterator it = bookall.iterator();
while(it.hasNext())
{
	// it.next()方法返回的数据类型是Object类型,因此需要强制类型转换
    String book = (String)it.next();
    System.out.println(book);
    if(book.equals("疯狂Java讲义")){
    // 从集合中删除上一次next()方法返回的元素
    it.remove();
    //bookall.remove(book); //使用Iterator迭代过程中,不可修改集合元素,将引发异常
	}
    // 对book变量赋值,不会改变集合元素本身
    book = "测试字符串";
}
System.out.println(bookall);

在这里插入图片描述
当使用Iterator对集合元素进行迭代时,Iterator并不是把集合元素本身传给了迭代变量,而是把集合元素的值传给了迭代变量,所以修改迭代变量的值对集合元素本身没有任何影响。
当使用Iterator迭代访问Collection集合元素时,Collection集合里的元素不能被改变,只有通过Iterator的romove()方法删除上一次next()方法返回的集合元素才可以;否则将会引发java.util.ConcurrentModificationException异常。Iterator迭代器采用的是快速失败(fail-fast)机制,一旦在迭代过程中检测到该集合已经被修改(通常是程序中的其他线程修改),程序立即引发ConcurrentModificationException异常,而不是显示修改后的结果,这样可以避免共享资源而引发的潜在问题。

使用Lambda表达式遍历Iterator

Java 8为Iterator新增了一个forEachRemaining(Consumer action)方法,该方法所需的Consumer参数同样也是函数式接口。当程序调用Iterator的forEachRemaining(Consumer action)遍历集合元素时,程序会依次将集合元素传给Consumer的accept(T t)方法(该接口中唯一的抽象方法)。

// 获取bookall集合对应的迭代器
Iterator it1 = bookall.iterator();
// 使用Lambda表达式(目标类型是Comsumer)来遍历集合元素
it1.forEachRemaining(obj -> System.out.println("迭代集合元素:"+obj));

在这里插入图片描述

使用foreach循环遍历集合元素

使用Java 5提供的foreach循环迭代访问集合元素,系统只是依次把集合元素的值赋给迭代变量,因此在foreach循环中修改迭代变量的值也没有任何实际意义。当使用foreach循环迭代访问集合元素时,该集合也不能被改变,否则将引发ConcurrentModificationException异常。

for(Object obj : bookall)
{
	// 此处book变量不是集合元素本身
    String book = (String)obj;
    System.out.println(book);
    if(book.equals("疯狂Android讲义")) {
    	//bookall.remove(book); // 引发ConcurrentModificationException异常
    }
}
System.out.println(bookall);

在这里插入图片描述

使用Java 8新增的Predicate操作集合

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

// 创建一个集合
Collection books = new HashSet();
books.add(new String("轻量级Java EE 企业应用实战"));
books.add(new String("疯狂Java讲义"));
books.add(new String("疯狂iOS讲义"));
books.add(new String("疯狂Ajax讲义"));
books.add(new String("疯狂Android讲义"));
// 使用Lambda表达式(目标类型是Predicate)过滤集合
books.removeIf(ele -> ((String)ele).length() < 10);
System.out.println(books);

使用Java 8新增的Stream操作集合

Java 8还新增了Stream、IntStream、LongStream、DoubleStream等流式API,这些API代表多个支持串行和并行聚集操作的元素。Stream是一个通用的流接口,而IntStream、LongStream、DoubleStream则代表类型为int、long、double的流。Java 8为每个流式API提供了对应的Builder,例如Stream.Builder、IntStream.Builder、LongStream.Builder、DoubleStream.Builder,开发者可以通过这些Builder来创建对应的流。
独立使用Stream的步骤如下:

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

Stream提供了大量的方法进行聚集操作,这些方法既可以是中间的(intermediate),也可以是末端的(terminal)。中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法,其返回值是另外一个流。末端方法是对流的最终操作,当对某个Stream执行末端方法后,该流将会被消耗且不再可用。

IntStream is = IntStream.builder().add(20).add(13).add(-2).add(18).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("is所有元素的平方是否都大于20:"+is.allMatch(ele -> ele * ele > 20));
//System.out.println("is是否包含任何元素的平方大于20:"+is.anyMatch(ele -> ele * ele > 20));
// 将is映射成一个新Stream,新stream的每个元素是原Stream元素的2倍加一
IntStream newIs = is.map(ele -> ele * 2 + 1);
// 使用方法引用的方式来遍历集合元素
newIs.forEach(System.out::println);

关于流的方法还有如下两个特征

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

除此之外,Java 8允许使用流式API来操作集合,Collection接口提供了一个stream(0默认方法,该方法可返回该集合对应的流,接下来可通过流式API来操作集合元素。由于Stream可以对集合元素进行整体的聚集操作,因此Stream极大地丰富集合功能。

// 创建一个集合
Collection bookall = new HashSet();
bookall.add("轻量级Java EE 企业应用实战");
bookall.add("疯狂Java讲义");
bookall.add("疯狂Android讲义");

// 统计图书数量
System.out.println(bookall.stream().filter(ele -> ((String)ele).contains("疯狂")).count());
System.out.println(bookall.stream().filter(ele -> ((String)ele).contains("Java")).count());
System.out.println(bookall.stream().filter(ele -> ((String)ele).length() > 10).count());
// 先调用Collection对象的stream()方法将集合转换为Stream
// 再调用Stream的mapToInt()方法获取原有的Stream对应的IntStream
bookall.stream().mapToInt(ele -> ((String)ele).length()).forEach(System.out::println);

在这里插入图片描述

发布了134 篇原创文章 · 获赞 141 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/asmartkiller/article/details/105009819