集合流和过滤器。使用 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
这是一个默认方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)
此方法为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流会被销毁
所以使用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接口
- 源码
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();
}
}
因为word1的ArrayList是Arrays的内部类,其没有重写Iterator接口的默认remove方法会抛出异常
而正统的ArrayList重写了remove方法
Collection接口新的默认default方法removeIf
default boolean removeIf(Predicate<? super E> filter)
- 匿名内部类
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)
因为这个方法返回一个流,它表示一个中间操作,这基本上意味着你可以链接任意数量的过滤器或者其他中间操作
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接口
Streamfilter(Predicate<? super T> predicate)
此方法返回流,中间操作,可以链接任意数量的筛选器或其他中间操作 - distinct去重
1.Stream接口
Streamdistinct()
中间操作,因为它必须知道元素的值才能找出哪些元素是重复的,所以这个操作也是有状态的
Comments | 0 条评论