zoukankan      html  css  js  c++  java
  • 7.2 Java 11新增的Collection和Iterator接口

    一、Collection接口操作集合元素的方法

    Collection接口是List、Set、Queue接口的父接口,该接口里定义了即可以用于操作Set集合,也可以用于操作List和Queue集合。Collection接口里定义操作集合元素的方法:
    ★boolean add(Object o):该方法用于向集合中添加一个元素。如果集合对象被添加操作改变了,则返回true。
    ★boolean addAll(Collection c):该方法把集合c里所有元素添加到指定集合里。如果集合对象被添加操作改变了,则返回true。
    ★void clear():清除集合中的所有元素,将集合长度变为0.
    ★boolean contains(Object o):返回集合中是否包含指定元素。
    ★boolean containsAll(Object o):返回集合中是否包含集合c里的所有元素。
    ★boolean isEmpty():返回集合是否为空。当集合长度为0时返回true,否则返回false。
    ★boolean iterator():返回一个Iterator对象,用于遍历集合的元素
    ★boolean remove(Object o):删除集合中指定元素o,当即和中包含一个或多个元素o时,该方法只删除第一个满足符合条件的元素,该方法返回true。
    ★boolean removeAll(Collection c):从集合中删除集合c里的所有元素,如果删除一个或多个元素,该方法返回true。
    ★boolean retainAll(Collection c):从集合删除c里不包含的元素,如果该操作改变了调用该方法的集合,则返回true。
    ★int size():该方法返回集合中元素个数
    ★Object[] toArray():该方法将集合转换为一个数组,所有集合元素变成对于的数组元素。
    更多方法参看java.util.Collection的API文档
    程序实例:

    import java.util.Collection;
    import java.util.ArrayList;
    import java.util.HashSet;
    public class CollectionTest  
    {
    	public static void main(String[] args) 
    	{
    		//List集合测试
    		var c=new ArrayList();
    		//集合是否为空
    		System.out.println(c.isEmpty());
    		//添加元素
    		c.add("孙悟空");
    		//虽然Java集合中保存的是对象,但Java支持自动装箱
    		c.add(6);
    		System.out.println("集合中的元素个数:"+c.size());//2
    		//删除元素
    		c.remove(1);//对于删除整数对象,只能指定它的索引值
    		System.out.println("集合中的元素个数:"+c.size());//1
    		//判断是否包含指定字符串
    		System.out.println(c.contains("孙悟空"));//true
    		c.add("轻量级Java EE企业级实战");
    		System.out.println("c集合中的元素"+c);
    
    		//Set集合测试
    		var books=new HashSet();
    		books.add("轻量级Java EE企业级实战");
    		books.add("疯狂Java讲义");
    		//c集合是否完全包含books集合
    		System.out.println("c集合是否完全包含books集合:"+c.containsAll(books));
    		//从c集合中减去books集合中的元素
    		c.removeAll(books);
    		System.out.println("集合c的元素:"+c);//集合c的元素:[孙悟空]
    		//删除集合c中的所有元素
    		c.clear();
    		System.out.println("集合c的元素:"+c);
    
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    true
    集合中的元素个数:2
    集合中的元素个数:1
    true
    c集合中的元素[孙悟空, 轻量级Java EE企业级实战]
    c集合是否完全包含books集合:false
    集合c的元素:[孙悟空]
    集合c的元素:[]
    
    输出完成 (耗时 0 秒) - 正常终止
    

    上面创建了两个Collection对象,一个是集合books集合(HashCode集合),一个是c集合(ArrayList)。虽然他们的实现类不同,当它们都是Collection接口的实现类,因此继承了Collection接口中操作数组的方法。
    当使用System.out.println()打印集合时,将输出[ele1,ele2,ele3...]的形式,这是因为所有Collection实现类都重写toString()方法,带方法可以一次性地输出集合中的所有元素。

    二、遍历集合元素的方法:

    注意:在传统模式下,把一个对象“丢进”集合中后,集合会忘记这个对象的类型——也就是说系统把所有集合元素都当作Object类型。从Java 1.5以后,这种状态得到了改进:可以使用泛型来限制集合里的元素类型,并让集合记住所有元素的类型。
    Java 11为Collecton新增了一个toArray(IntFunction)方法,该方法的主要目的就是为了使用泛型。对于传统的toArray()方法而言,不管Collection本身是否使用泛型,toArray()总是返回Object[];但改进后toArray(IntFunction)方法不同,当Collection使用泛型时,toArray(IntFunction)可以返回特定类型的数组。
    例如:

    //该Collection使用泛型,指定它的集合元素都是String
    var strColl=List.of("Java","Kotlin","Swift","Python");
    //toArray()方法参数就是一个Lambda表达式,代表IntFunction对象
    //此时toArray()方法返回值类型是String[], 二不是Object[]
    String[] sa=strColl.toArray(String::new);//调用String类的构造器
    System.out.println(Arrays.toString(sa));
    

    1、使用Lambda表达式遍历集合

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

    程序实例:

    import java.util.Collection;
    import java.util.HashSet;
    
    public class CollectionEach 
    {
    	public static void main(String[] args) 
    	{
    		//创建一个集合Set
    		var books=new HashSet();
    		books.add("三国演义");
    		books.add("西游记");
    		books.add("红楼梦");
    		books.add("水浒传");
    		books.forEach(obj->System.out.println("迭代集合元素:"+obj));
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    迭代集合元素:水浒传
    迭代集合元素:三国演义
    迭代集合元素:红楼梦
    迭代集合元素:西游记
    
    输出完成 (耗时 0 秒) - 正常终止
    

    二、使用Iterator遍历集合元素

    Iterator接口也是Java集合框架的成员,但它与Collection系列、Map系列集合不一样:Collection和Map系列集合主要用于盛装其他对象,而Iterable则主要用于遍历(即迭代访问)Collection集合中的元素,Iterator对象也被称为迭代器。
    Iterator向程序遍历Collection系列集合元素提供了统一的编程接口。Iterator接口里定义了4个方法:
    ★boolean hasNext():被迭代的集合元素还没有被遍历完,则返回true。
    ★Object next():返回集合里的下一个元素。
    ★void remove():删除集合里上一次next方法返回的元素。
    ★void forEachRemaining(Consumer action):这是Java 8为Iterator新增的方法,该方法可以使用Lambda表达式来遍历集合中的元素。
    下面程序示范了如何通过Iterator接口来遍历集合中的元素:

    import java.util.HashSet;
    public class IteratorTest
    {
    	public static void main(String[] args)
    	{
    		//创建集合元素
    		var books=new HashSet();
    		books.add("三国演义");
    		books.add("西游记");
    		books.add("红楼梦");
    		books.add("水浒传");
    		System.out.println(books);//Colletion接口改写toString()方法
    		//获取books集合对应的迭代器
    		var it=books.iterator();
    		while(it.hasNext())
    		{
    			//it.next()方法返回的数据类型是Object类型,因此需要强转
    			var book=(String)it.next();
    			System.out.println(book);
    			if(book.equals("红楼梦"))
    			{
    				//从集合中删除上一次next()方法返回的元素
    				it.remove();
    			}
    			//对book遍历赋值不会改变集合元素本身
    			book="测试字符串";
    		}
    		System.out.println(books);
    	}
    }
    [水浒传, 三国演义, 红楼梦, 西游记]
    水浒传
    三国演义
    红楼梦
    西游记
    [水浒传, 三国演义, 西游记]
    请按任意键继续. . .
    

    Iterator仅用于遍历集合本身,Iterator本身并不提供盛装对象的能力。如果需要创建Iterator对象,则必须有一个被迭代的集合。没有集合的Iterator仿佛一个无本之木,没有存在价值。
    book="测试字符串";对集合元素没有影响,说明当使用Iterator对集合元素进行迭代时,Iterator并不把集合元素本身传给迭代变量,而是把集合元素的值传给了迭代变量,所以修改迭代遍历的值对集合元素本身没有影响。当使用Iterator迭代访问Collection集合元素时,Collection集合里的元素不能被改变,只有通过Iterator的remove()方法删除上一次的next()方法返回的集合元素才可以;否则将会引发java.util.ConcurrentModificationException异常。

    
    import java.util.*;
    public class IteratorErrorTest
    {
    	public static void main(String[] args)
    	{
    		// 创建集合、添加元素的代码与前一个程序相同
    		var books = new HashSet();
    		books.add("轻量级Java EE企业应用实战");
    		books.add("疯狂Java讲义");
    		books.add("疯狂Android讲义");
    		// 获取books集合对应的迭代器
    		var it = books.iterator();
    		while (it.hasNext())
    		{
    			var book = (String) it.next();
    			System.out.println(book);
    			if (book.equals("疯狂Android讲义"))
    			{
    				// 使用Iterator迭代过程中,不可修改集合元素,下面代码引发异常
    				books.remove(book);
    			}
    		}
    	}
    }
    

    3、使用Lambda表达式遍历Iterator

    ★void forEachRemaining(Consumer action):这是Java 8为Iterator新增的方法,该方法可以使用Lambda表达式来遍历集合中的元素。与Collection接口中的forEach()方法相似。
    Java 8 为Iterator新增了一个forEachRemaining(Consumer action)方法,该方法所需的Consumer参数同样也是函数式接口。当程序员调用Iterator的forEachRemianing(Consumer action)遍历集合元素时,程序将会依次将集合元素传给Comsumer的accept(T t)方法(该接口中的唯一抽象方法):

    import java.util.HashSet;
    public class IteratorEach
    {
    	public static void main(String[] args)
    	{
    		//创建集合元素
    		var books=new HashSet();
    		books.add("三国演义");
    		books.add("西游记");
    		books.add("红楼梦");
    		books.add("水浒传");
    		System.out.println(books);//Colletion接口改写toString()方法	
    		
    		//获取集合的迭代器
    		var it=books.iterator();
    		//迭代器的forEachRemaining()方法调用Consumer接口中的accept()方法
    		it.forEachRemaining(obj->System.out.println(obj));
    	}
    }
    [水浒传, 三国演义, 红楼梦, 西游记]
    水浒传
    三国演义
    红楼梦
    西游记
    请按任意键继续. . .
    

    4、使用foreach循环来遍历集合元素

    Java 5提供的foreach循环迭代访问集合的元素更加便捷。

    import java.util.ArrayList;
    public class ForeachTest
    {
    	public static void main(String[] args)
    	{
    		//创建集合list
    		var l=new ArrayList();
    		System.out.println(l.isEmpty());
    		l.add("三国演义");
    		l.add("西游记");
    		l.add("水浒传");
    		l.add("红楼梦");
    		System.out.println(l);
    		for(var book:l)
    		{
    			System.out.println(book);
    		}
    	}
    }
    true
    [三国演义, 西游记, 水浒传, 红楼梦]
    三国演义
    西游记
    水浒传
    红楼梦
    请按任意键继续. . .
    

    5、使用Predicate操作集合

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

    boolean test​(T t)
    Evaluates this predicate on the given argument.
    Parameters:
    t - the input argument
    Returns:
    true if the input argument matches the predicate, otherwise false
    

    下面程序示范了使用Predicate来过滤集合:

    import java.util.HashSet;
    class PredictInit 
    {
    	public static void main(String[] args) 
    	{
    		var books=new HashSet();
    		books.add("轻量级Java EE企业级应用实战");
    		books.add("疯狂Java讲义");
    		books.add("疯狂IOS讲义");
    		//使用Lambda表达式(目标类型是Predicate)过滤集合
    		books.removeIf(ele->((String) ele).length()<10);
    		System.out.println(books);
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    [轻量级Java EE企业级应用实战]
    
    输出完成 (耗时 0 秒) - 正常终止
    

    假设依然有上面的程序的books集合,如果程序有以下三个统计需求:
    1、统计书名中出现“疯狂”字符串的图书数量
    2、统计书名中出现“Java”字符串的图书数量
    3、统计书名长度大于10的图书数量
    如果采用传统的编程(使用正则表达式),则需要执行三次循环,但Predicate只需要一个方法就可以。例如:

    import java.util.HashSet;
    import java.util.Collection;
    import java.util.function.Predicate;
    public class PredicateTest
    {
    	public static void main(String[] args)
    	{
    		var books=new HashSet();
    		books.add("轻量级Java EE企业级应用实战");
    		books.add("疯狂Java讲义");
    		books.add("疯狂IOS讲义");
    		//1、统计书名中出现“疯狂”字符串的图书数量
    		System.out.println(callAll(books,ele->((String) ele).contains("疯狂")));
    		//2、统计书名中出现“Java”字符串的图书数量
    		System.out.println(callAll(books,ele->((String) ele).contains("Java")));
    		//3、统计书名长度大于10的图书数量
    		System.out.println(callAll(books,ele->((String) ele).length()>10));
    	}
    		public static int callAll(Collection books,Predicate fliter)
    	{
    		int total=0;
    		for(var obj:books)
    		{
    			//使用Predicate的test()方法判断对象是否满足Predicate指定的条件
    			if(fliter.test(obj))
    			{
    				total++;
    			}
    		}
    		return total;
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    2
    2
    1
    
    输出完成 (耗时 0 秒) - 正常终止
    

    7、使用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()方法获取对应的流
    4、调用Stream的聚集方法。

    
    import java.util.stream.*;
    public class IntStreamTest
    {
    	public static void main(String[] args)
    	{
    		var 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倍+1
    		var newIs = is.map(ele -> ele * 2 + 1);
    		// 使用方法引用的方式来遍历集合元素
    		newIs.forEach(System.out::println); // 输出41 27 -3 37
    	}
    }
    

    上面创建的IntStream,接下来分别多次调用了IntStream的聚集方法执行操作,这样即可获取该流的相关信息。注意上面的方法只能执行一次,因此需要把其他方法注释掉。
    Stream提供了大量的方法进行聚集操作,这些方法既可以是中间的(intermediate)、也可是“末端的”(terminal).
    1、中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法。上面的Map()方法就是一个中间方法,中间方法返回值是另外一个流。
    2、末端方法:末端方法是对流的最终操作。当对某个流执行末端方法后,该流将会被“消耗”且不可再用。上面的sun()、count()、average()等方法是末端方法。
    流的方法具有两个特征
    1、有状态的方法:这种方法会增加流的一些属性,比如元素的唯一性、元素的最大数量、保证元素以排序方式被处理等。有状态方法往往需要更大的性能开销。
    2、短路方法:短路方法可以尽早结束对流的操作,不必检查所有元素。
    简单介绍一下Stream中间方法:
    ★fliter(Predicate predicate):过滤掉Stream流中所有不符合的predicate元素。
    ★mapToXxx(ToXxxFunction mapper):使用ToXxxFunction对流中的元素执行一对一的转换,该方法返回对流中包含了ToXxxFunction转换生成的所有元素。
    ★peek(Consumer action):依次对每个元素执行一些操作,该方法返回的流与原有流包含相同的元素。该方法主要用于测试。
    ★distinct():该方法用于排序流中所有重复的元素(判断元素重复的标准是利用eaquls()比较返回true)。这是一个有状态的方法。
    ★sorted():该方法用于保证流中的元素在后续访问中处于有序状态。这是一个有状态的方法.
    ★limit(long maxSize):该方法用于保证对该流的后续访问中最大允许访问元素个数。这是一个有状态的、短路的方法。
    下面简单介绍一下Stream流中的末端方法:
    ★forEach(Consumer action):遍历流中的所有元素,对每个元素执行action。
    ★toArray():将流中所有元素转换为一个数组。
    ★reduce():该方法有三个重载版本,都用于通过某种操作来合并流中的元素。
    ★min():返回流中的所有元素的最小值。
    ★max():返回流中元素的最大值。
    ★count():返回流中元素的数量。
    ★anyMatch(Predicate predicate):判断流中是否至少含有一个元素符合Predicate条件。
    ★noneMatch(Predicate predicate):判断流中是否所有元素都不符合Predicate条件。
    ★allMatch(Predicate predicate):判断流中是否所有的元素都满足Predicate条件。
    ★findFirst():返回流中的第一个元素。
    ★findAny():返回流中任意一个元素。
    Java 8允许使用流式API来操作集合,Collection接口提供了一个stream()默认方法,该方法可返回该集合对应的流,接下来可通过流式API来操作集合元素。

    import java.util.HashSet;
    public class CollectionStream
    {
    	public static void main(String[] args)
    	{
    		//创建一个集合
    		var books=new HashSet();
    		books.add("轻量级Java EE企业级应用实战");
    		books.add("疯狂Java讲义");
    		books.add("疯狂IOS讲义");
    		books.add("疯狂Aja讲义");
    		books.add("疯狂Android讲义");
    		//统计书名中包含“疯狂”子串的图书数量
    		System.out.println(books.stream()
    			.filter(ele->((String) ele).contains("疯狂")).count());
    		//统计书名中包含“Java”子串的图书数量
    		System.out.println(books.stream()
    			.filter(ele->((String) ele).contains("Java")).count());
    		// 统计书名字符串长度大于10的图书数量
    		System.out.println(books.stream()
    			.filter(ele->((String) ele).length() > 10)
    			.count()); // 输出2
    		// 先调用Collection对象的stream()方法将集合转换为Stream,
    		// 再调用Stream的mapToInt()方法获取原有的Stream对应的IntStream
    		books.stream().mapToInt(ele -> ((String) ele).length())//返回一个新流
    			// 调用forEach()方法遍历IntStream中每个元素
    			.forEach(System.out::println);// 输出7 11 17 7 8.Set时无序的
    	}
    }
    

    上面程序最后一段代码先调用Collection对象的stream()方法将集合转换成Stream对象,然后调用Stream对象的mapToInt()方法将其转换为IntStream--这个mapToInt()就是一个中间方法,因此程序可继续调用IntStream的forEach()方法来遍历流中的元素。

  • 相关阅读:
    oracle之bitmap索引
    oracle命令集
    Spring入门注解版
    spring入门一
    bootstrap栅格系统
    Struts2之基于配置的字段校验
    Struts2之文件上传下载
    Struts2之类范围拦截器和方法拦截器
    struts2之输入验证
    C++虚函数和纯虚函数的区别
  • 原文地址:https://www.cnblogs.com/weststar/p/12491531.html
Copyright © 2011-2022 走看看