Java8集合操作正確方式
- 流Api是Java8提供的新的api,用來聲明式地處理數據集合。
1 一個Demo案例:
- 需要將List集合中的重量大於0.3kg的水果篩選出來進行排序後拿出它的名字最後放到另一個List中去。
List<Fruit> fruits = new ArrayList<>();
fruits.add(new Fruit("Apple", 0.8));
fruits.add(new Fruit("orange", 0.3));
fruits.add(new Fruit("watermelon", 1.2));
fruits.add(new Fruit("banana", 0.6));
//java8之前的做法
// List<Fruit> tempList = new ArrayList<>();
// for(Fruit fruit : fruits){
// if(fruit.getWeight()>0.3){
// tempList.add(fruit);
// }
// }
//
// List<String> fruitNames = new ArrayList<>();
// tempList.sort(new Comparator<Fruit>() {
// @Override
// public int compare(Fruit o1, Fruit o2) {
// return o1.getWeight().compareTo(o2.getWeight());
// }
// });
//
// for(Fruit fruit :tempList){
// fruitNames.add(fruit.getName());
// }
//
// System.out.println(fruitNames);
//java8的做法
List<String> fruitName = fruits.stream().filter(f -> f.getWeight()>0.3).sorted(Comparator.comparing(Fruit::getWeight)).map((Fruit::getName)).collect(Collectors.toList());
System.out.println(fruitName);
結果:[banana, Apple, watermelon]
通過這個demo,可以很清楚地看到java8對集合的操作非常的簡單並且支持對集合的鏈式操作,而java8之前則需要大量的迭代集合的操作代碼重複很多。
2 Java 8中流對集合的處理:
需要一個數據源(如集合),一箇中間操作鏈(過濾、排序、limit等操作),一個終端操作執行流水線並生成結果
3 Stream API支持的操作
/**
* 預定義的菜單類
*/
public class Menu {
private String name;
private double weight;
public Menu(String name, double weight) {
this.name = name;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
@Override
public String toString() {
return "Menu{" +
"name='" + name + '\'' +
", weight=" + weight +
'}';
}
}
3.1 篩選和切片(filter、distinc、limit、skip)
public class UsefulOperator {
public static List<Menu> factory() {
Menu meatMenu = new Menu("meat", 8.88);
List<Menu> list = Lists.newArrayList();
list.add(new Menu("fish", 1.98));
list.add(new Menu("apple", 0.88));
list.add(new Menu("beaf", 2.18));
list.add(meatMenu);
list.add(meatMenu);
list.add(new Menu("chop", 0.08));
return list;
}
public static void main(String[] args) {
List<Menu> list = factory();
//filter (過濾不符合條件的元素)
List<Menu> newMenu = list.stream().filter(w -> w.getWeight() > 0.88).collect(Collectors.toList());
System.out.println(newMenu);
//distinct (過濾調一樣的元素基於hashcode和equals)
List<Menu> newMenu2 = list.stream().distinct().collect(Collectors.toList());
System.out.println(newMenu2);
//limit (限制選取前幾個元素)
List<Menu> newMenu3 = list.stream().limit(2).collect(Collectors.toList());
System.out.println(newMenu3);
//skip (限制跳過前幾個元素)
List<Menu> newMenu4 = list.stream().skip(2).collect(Collectors.toList());
System.out.println(newMenu4);
}
}
結果:
[Menu{name=‘fish’, weight=1.98}, Menu{name=‘beaf’, weight=2.18}, Menu{name=‘meat’, weight=8.88}, Menu{name=‘meat’, weight=8.88}]
[Menu{name=‘fish’, weight=1.98}, Menu{name=‘apple’, weight=0.88}, Menu{name=‘beaf’, weight=2.18}, Menu{name=‘meat’, weight=8.88}, Menu{name=‘chop’, weight=0.08}]
[Menu{name=‘fish’, weight=1.98}, Menu{name=‘apple’, weight=0.88}]
[Menu{name=‘beaf’, weight=2.18}, Menu{name=‘meat’, weight=8.88}, Menu{name=‘meat’, weight=8.88}, Menu{name=‘chop’, weight=0.08}]
3.2 映射(map、flatMap)
//映射
//map(接受一個函數並且將其映射成一個新的元素)
List<String> newMenu5 = list.stream().map(Menu::getName).collect(Collectors.toList());
System.out.println(newMenu5);
//flatMap 可以將生成的流扁平化爲單個流
List<String> words = Lists.newArrayList("hello", "World");
//將數組words中不同單詞保存下來
System.out.println(words.stream().map(word -> word.split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList()));
結果
[fish, apple, beaf, meat, meat, chop]
[h, e, l, o, W, r, d]
3.3 查找和匹配(allMatch、 anyMatch、 noneMatch、 findFirst和findAny)
//判斷集合中是否有任何符合條件的元素 終端操作返回boolean
if (list.stream().anyMatch(m -> m.getWeight() > 1.8)) {
System.out.println("have right fruit");
}
//判斷集合的所有元素是否符合條件 終端操作返回boolean
if (list.stream().allMatch(m -> m.getWeight() > 1.8)) {
System.out.println("have all right fruit");
}
//集合中是否所有元素都不匹配指定的條件 終端操作返回boolean
if (list.stream().noneMatch(m -> m.getWeight() > 10)) {
System.out.println("have none right fruit");
}
//返回當前流中的任意元素
// //你可能會想,爲什麼會同時有findFirst和findAny呢?答案是並行。找到第一個元素
// //在並行上限制更多。如果你不關心返回的元素是哪個,請使用findAny,因爲它在使用並行流
// //時限制較少。
Optional<Menu> anyMenu = list.stream().findAny();
System.out.println(anyMenu.toString());
//返回當前流中第一個元素
Optional<Menu> firstMenu = list.stream().findFirst();
System.out.println(firstMenu.toString());
結果:
have right fruit
have none right fruit
Optional[Menu{name=‘fish’, weight=1.98}]
Optional[Menu{name=‘fish’, weight=1.98}]
3.4 規約(reduce)
//規約
//元素求和
List<Integer> integers = Lists.newArrayList(1, 2, 3, 4, 5, 6);
//0代表初始值
int sum = integers.stream().reduce(0, Integer::sum);
//可以不指定初始值,返回值變爲Optional<Integer>以防止可能爲空的情況
Optional<Integer> sum2 = integers.stream().reduce(Integer::sum);
System.out.println(sum);
System.out.println(sum2);
//最大值和最小值
Optional<Integer> max = integers.stream().reduce(Integer::max);
// Optional<Integer> min = integers.stream().reduce((a,b)->a<b?a:b);
Optional<Integer> min = integers.stream().reduce(Integer::min);
System.out.println("max==="+max+" min==="+min);
//求menu中fruit的數量
Optional<Integer> fruitCount = list.stream().map(c -> 1).reduce(Integer::sum);
System.out.println("fruit's count is "+ fruitCount);
結果:
21
Optional[21]
max=Optional[6] min=Optional[1]
fruit’s count is Optional[6]
操作總結
3.4 數值流(IntStream、 DoubleStream和LongStream)
//數值流
//映射到數值流
double totalWeight = list.stream().mapToDouble(Menu::getWeight).sum();
System.out.println(totalWeight);
//轉換回對象流
Stream<Double> boxed = list.stream().mapToDouble(Menu::getWeight).boxed();
//默認值OptionalDouble
OptionalDouble optionalDouble = list.stream().mapToDouble(Menu::getWeight).max();
System.out.println(optionalDouble);
System.out.println(optionalDouble.orElse(99.99));
//數值範圍
System.out.println(IntStream.rangeClosed(100,200).boxed().limit(10).collect(Collectors.toList()));
//生成勾股數
Stream<int[]> pythagoreanTriples =
IntStream.rangeClosed(1, 100).boxed()
.flatMap(a ->
IntStream.rangeClosed(a, 100)
.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
.mapToObj(b ->
new int[]{a, b, (int)Math.sqrt(a * a + b * b)})
);
pythagoreanTriples.limit(5)
.forEach(t ->
System.out.println(t[0] + ", " + t[1] + ", " + t[2]));
結果:
22.88
OptionalDouble[8.88]
8.88
[100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
3, 4, 5
5, 12, 13
6, 8, 10
7, 24, 25
8, 15, 17
3.4 構建流
//由值創建流
Stream stream = Stream.of(“a”, “b”, “c”, “d”, “e”);
stream.map(String::toUpperCase).forEach(System.out::println);
//獲取一個空的流
Stream empty = Stream.empty();
//由數組創建流
int[] numbers = {1, 2, 3, 4, 5};
IntStream numberStream = Arrays.stream(numbers);
Long countNumber = numberStream.count();
System.out.println(countNumber);
//由文件生成流
long uniqueWords = 0;
try (Stream<String> lines =
Files.lines(Paths.get("G:\\IdeaProject\\javase\\src\\main\\java\\Java8\\chapter3_stream\\operate\\data.txt"), Charset.defaultCharset())) {
uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
.distinct()
.count();
System.out.println("==diff words==" + uniqueWords);
} catch (IOException e) {
}
//由函數生成流:創建無限流
//根據初始值進行操作
Stream.iterate(100, n -> 100 + new Random().nextInt(100)).limit(10).forEach(System.out::println);
Stream.iterate(new int[]{0, 1}, t->new int[]{t[1],t[0]+t[1]}).limit(20).map(t->t[0]).forEach(t -> System.out.print(t+" "));
//不需要根據生成值進行後續操作
Stream.generate(()->new Random().nextInt(100)+100).limit(20).forEach(System.out::println);