集合流和过滤器。使用 Streams 和 List 的 forEach 方法进行迭代。使用 lambda 表达式过滤集合。

迭代Foreach

遍历一个List常用的方法是

  • 使用 for 块
List<String> words = ...
for(int i = 0; i < words.size(); i++) {
    System.out.println(words.get(i));
}
  • 使用迭代器:
List<String> words = ...
for(Iterator<String> it = words.iterator(); it.hasNext();) {
    System.out.println(it.next());
}
  • for-each 循环:
List<String> words = ...
for(String w : words) {
    System.out.println(w);
}

建议的方法是尽可能使用 for-each 循环。前两点可能会发生错误(索引和迭代器变量)

Iterable接口中的forEach

该接口中是默认default方法

  • 利用函数接口的优势,java8添加了另一个基于 for-each 循环的迭代列表选项 foreach 方法
default void forEach(Consumer<? super T> action)

在 Iterable 接口中定义的,所以不仅可以用于 list,而且可以用于该接口的所有实现和子类接口(Collection),比如 Queues、 Sets、 Deques,甚至一些与 sql 相关的异常,比如 SQLException
image.png
image.png
这是一个默认方default法,很多实现类都可以覆盖,主要用于处理并发修改。

words.forEach(new Consumer<String>() {
            @Override
            public void accept(String t) {
                System.out.println(t);
            }
        });
        words.forEach(t -> System.out.println(t));
        words.forEach(System.out::println);
  • 在匿名类或 lambda 表达式中使用final规则。下列代码无效
int max = 0;
words.forEach(t -> {
    // The following line won't compile, you can't modify max
    max = Math.max(max, t.length());
    System.out.println(t);
});

因为lambda和匿名类的堆栈原因,导致出入栈,其所使用的变量必须为final

Stream接口中的forEach

Stream接口中也定义了forEach方法,但不是默认default方法,而是抽象方法

void forEach(Consumer<? super T> action)

image.png
此方法为void,不返回stream,是一个终止操作

        Stream<String> stream = words.stream();
// As an anonymous class
        stream.forEach((new Consumer<String>() {
            @Override
            public void accept(String strings) {
                System.out.println(strings);
            }
        }));
// As a lamba expression
        stream.forEach(t -> System.out.println(t));
        stream.sorted()
                .limit(2)
                .forEach(System.out::println);
// As a method reference
        stream.forEach(System.out::println);
  • stream流的又是在于可以进行连锁操作
  • 如果使用stream的终止操作,则stream流会被销毁
    image.png
    所以使用stream流的forEach后就不能在使用,会提示已经被操作或关闭,抛出异常
  • 如果在终止操作后想重新使用stream流则必须重新创建一个新的流
Stream.of(wordList).forEach(t -> System.out.println(t.length()));
Stream.of(wordList).forEach(System.out::println);
  • 或者将需要调用的方法包装到一个lambda表达式中
Consumer<String> print = t -> {
    System.out.println(t.length());
    System.out.println(t);
};
words.forEach(print);
  • 不能使用 return、 break 或 continue 来终止迭代。
    break、continue不能在循环之外使用,会产生编译错误。
    return因为在方法体内,所以不会有影响

Map接口中的forEach

在JDK1.8中Map接口新增的默认default方法。因为Map接口中含有K-V,所以该方法需要BiConsumer接口
image.png

  • 源码
default void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
            action.accept(k, v);
        }
    }

其等价于增强for循环

for (Map.Entry<K, V> entry : map.entrySet()) {
    action.accept(entry.getKey(), entry.getValue());
}

过滤Filtering

从集合中筛选(或删除)与特定条件不匹配的元素

  • 将匹配的元素复制到另一个集合来实现
List<String> words = Arrays.asList("a","b",null) ;
        List<String> nonEmptyWords = new ArrayList<String>();
        for(String w : words) {
            if(w != null && !w.isEmpty()) {
                nonEmptyWords.add(w);
            }
        }
        System.out.println(nonEmptyWords);
  • 使用迭代器删除集合本身中的不匹配元素(仅当集合支持删除时)
for (Iterator<String> it = words.iterator(); it.hasNext(); ) {
            String w = it.next();
            if (w == null || w.isEmpty()) {
                it.remove();
            }
        }

image.png
因为word1的ArrayList是Arrays的内部类,其没有重写Iterator接口的默认remove方法会抛出异常
image.png
而正统的ArrayList重写了remove方法
image.png

Collection接口新的默认default方法removeIf

default boolean removeIf(Predicate<? super E> filter)

image.png

  • 匿名内部类
words.removeIf(new Predicate<String>() {
            @Override
            public boolean test(String t) {
                return t == null || t.isEmpty();
            }
        });
  • lambda表达式
words.removeIf(t -> t == null || t.isEmpty());

filter

将匹配元素复制到另一个集合可以使用Stream接口filter方法

Stream<T> filter(Predicate<? super T> predicate)

因为这个方法返回一个流,它表示一个中间操作,这基本上意味着你可以链接任意数量的过滤器或者其他中间操作
image.png

List<String> words = Arrays.asList("hello", null, "");
words.stream()
    .filter(t -> t != null) // ["hello", ""]
    .filter(t -> !t.isEmpty()) // ["hello"]
    .forEach(System.out::println);
  • 可以在某个类上创建一个方法(或者使用一个现有的方法) ,通过一个方法引用来实现
        List<String> words2 = Arrays.asList("hello", null, "");
// Using an anonymous class
        words2.stream()
                .filter(new Predicate<String> () {
                    @Override
                    public boolean test(String t) {
                        return StringUtils.isNotNullOrEmpty(t);
                    }
                })
                .forEach(System.out::println);
// Using a lambda expression
        words2.stream()
                .filter(t -> StringUtils.isNotNullOrEmpty(t))
                .forEach(System.out::println);
// Using a lambda expression
        words2.stream()
                .filter(StringUtils::isNotNullOrEmpty)
                .forEach(System.out::println);
    }
}
class StringUtils {
    public static boolean isNotNullOrEmpty(String s) {
        return s != null && !s.isEmpty();
    }
}

distinct

根据 Object.equals(Object)方法,Stream 接口还具有过滤重复元素的独特方法

Stream<T> distinct()

它返回一个新的流,所以这是一个中间操作。
它必须知道元素的值以找出哪些是重复的,所以这个操作也是有状态的

总结

  • foreach迭代
    1.Iterable接口
    Java8将以下方法添加到Iterable接口中
    default void forEach(Consumer<? super T> action)
    2.Stream接口
    Stream 也有该方法为abstract
    void forEach(Consumer<? super T> action)
    这是一个终止操作
  • filter集合过滤
    1.collection接口
    default boolean removeIf(Predicate<? super E> filter)
    移除集合中满足给定谓词的所有元素
    2.Stream接口
    Stream filter(Predicate<? super T> predicate)
    此方法返回流,中间操作,可以链接任意数量的筛选器或其他中间操作
  • distinct去重
    1.Stream接口
    Stream distinct()
    中间操作,因为它必须知道元素的值才能找出哪些元素是重复的,所以这个操作也是有状态的

这个家伙很懒,啥也没有留下😋