7.2 Java 11新增的Collection和Iterator接口

时间:2020-03-14 22:06:50   收藏:0   阅读:50

一、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 retiaiNALll(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()方法来遍历流中的元素。

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!