静态代码块和构造方法在项目中的具体使用
在今天小项目的书写中学习到了静态代码块的具体使用方法和构造方法的好处,在做项目时,常常一些在页面启动的时候我们就需要加载的东西,一般都放在构造方法里,构造方法会跟着我们new这个类的对象时自动的开始执行,但是这些全都是每一次new的时候创建的,有一些属性或者部分方法其实是固定的,每次都在创建对象时重新创建的话会太浪费资源了,例如在写斗地主游戏时,每次游戏开始前的所有牌一定是固定的,我们只需要将这些固定的牌经过洗牌,发牌,看牌就能玩一把开心的斗地主了,那么如何实现呢,这就得用到静态代码块的知识
具体实现如下:
在下列代码中,我们直接准备一个Map集合充当容器,将一张张牌通过键值对的方式放入容器中,而这些牌和容器都是每次玩斗地主所必须的,不管玩不玩都是需要的,那么我们就能使用静态代码块的方法将放牌的容器和牌写入进去,这样在后面的构造方法实现时,也就是每次这个类被创建时,就能在后面构造方法实现的时候直接拿来用
static Map<Integer,String> m = new HashMap<>();
static ArrayList<Integer> list = new ArrayList<>();
static {
//准备牌
String[] color = {"♠","♥","♦","♣"};
String[] number = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
int temp = 1;
for (String s : number) {
for (String s1 : color) {
m.put(temp,s+s1);
list.add(temp);
temp++;
}
}
m.put(temp,"大王");
list.add(temp);
temp++;
m.put(temp,"小王");
list.add(temp);
}
下面为该类的构造方法,在每次创建对象时,该构造方法会被自动调用
public PokerGame111() {
//洗牌
Collections.shuffle(list);
//发牌
TreeSet<Integer> lord = new TreeSet<>();
TreeSet<Integer> player1 = new TreeSet<>();
TreeSet<Integer> player2 = new TreeSet<>();
TreeSet<Integer> player3 = new TreeSet<>();
int temp = list.size();
for (int i = 1; i < list.size(); i++) {
int value = list.get(i);
if(i<=3){
lord.add(value);
}else if(i%3==0){
player1.add(value);
}else if(i%3==1){
player2.add(value);
}else if(i%3==2){
player3.add(value);
}
}
//看牌
System.out.println(pokerLook(lord));
System.out.println(pokerLook(player1));
System.out.println(pokerLook(player2));
System.out.println(pokerLook(player3));
}
下列为pokerLook方法,也就是将键值对根据键来找到相应的值
public ArrayList<String> pokerLook(TreeSet<Integer> a){
ArrayList<String> temp = new ArrayList<>();
for (Integer integer : a) {
String tempstr = m.get(integer);
temp.add(tempstr);
}
return temp;
}
这样,一个控制台版的斗地主前面流程的小游戏就写好了,主要包括准备牌,洗牌,发牌,看牌这几样功能,这里主要运用了Map集合,将每一张牌对应一个键,再将键存储到一个单列集合list中
创建每个玩家对应的Treeset容器,再将list里的数据也就是每张牌对应的键根据循环判断放到各自的Treeset容器中(使用Treeset容器的好处是,他会自动将里面的数据排序,也就是会自动将存储的键排序,这样的话看牌的时候我们就不用从小给键排序,直接根据键在Map集合中取出对应的值就可以实现看牌功能了)
Stream流
Stream流的中间方法
Stream流是可以理解为一个流水线,而这个流水线能得到该流水线上相应的数据并可以对其进行筛选,具体是如何使用的呢(Stream流只能对单列集合和数组进行使用,或者对一些杂乱的数据进行使用,但是这些杂乱的数据的数据类型必须相同,这样的话才能对相关的数据进行一定的筛选,而双列集合例如map集合对象则需要先将其转化为单列集合再对其进行使用,且stream流当中存在一些中间操作和终端操作,在进行了终端操作后就不能再使用中间操作,这可以理解为工厂里将产品打包了以后就不能再进行加工了)
另外,在stream流当中,如果实现了一个操作例如filter方法过滤筛选,他在使用方法过后是会将返回一个新的stream流,之前的stream流是不能再被使用了,通俗的说所有的stream流只能被使用一次,所以建议在使用stream流的时候采用链式编程
也就是下列这种类型
list2.stream().filter(s->s.startsWith("a")).forEach(s -> System.out.println(s));
具体代码实现如下:
这里的foreach方法里是lamda表达式,s相当于是暂时创建出来的变量用于接受该流中相应的字符串,再将其打印出来
//单列集合获取Stream流
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a","b","c","d","e");
//通过获取list单列集合的stream流并且通过链式编程打印该流上面的所有数据
list.stream().forEach(s -> System.out.print(s+ " "));
使用stream流获取双列集合元素
因为stream流不能直接对双列集合使用,那么我们可以通过使用双列集合中的entryset方法和keyset方法将双列集合里面的属性值转移到单列集合中,再通过单列集合的stream流获取这些属性,最后通过双列集合的方法使用这些属性
1、通过entryset方法实现
//双列集合获取stream流,使用entryset方法,这样获取出的stream流是键值对
//双列集合不能直接获取stram流但是我们可以将键值对转化为一个单列集合
//通过单列集合的Stream流来获取到双列集合的键值对
Set<Map.Entry<String, String>> set = m.entrySet();
set.stream().forEach(s-> System.out.println(s));
2、通过keyset方法实现
System.out.println("---------这是使用keyset方法-----------");
//双列集合使用keyset方法获取stream流,这时候获取出来的单列集合就是map集合里的键
//再通过map里的get方法通过键来找值,这样就能通过stram流获取到双列集合的键和值
Set<String> key = m.keySet();
key.stream().forEach(s-> System.out.println(s + "=" + m.get(s)));
使用stream流对数组进行操作
在用stream流对数组进行操作的时候,是使用的数组的工具类Arrays里面的stream方法,而这样就能获取到相关数组的stream流,具体实现代码如下:
//对数组使用stream流方法
int[] arr = {1,2,3,4,5,6,7,8,9,10};
Arrays.stream(arr).forEach(s-> System.out.println(s));
使用stream流对零散的数据进行操作
对零散的数据进行stream流操作时,是使用的Stream接口里自带的of静态方法,而对零散的数据使用该方法时需要这些零散的数据,他们的数据类型一致,这样才能对这些零散的数据进行使用stream流
具体实现代码如下:
//对一些零散数据进行stream流的处理,是使用Stream里的静态of方法
//但是这些零散数据使用stream流方法时,必须保持数据类型相同
Stream.of(1,2,3,4,5,6).forEach(s-> System.out.println(s));
stream接口里的filter过滤方法
使用filter方法对stream流里的数据进行过滤,满足条件的留下,没满足条件的舍弃(注意:这里舍弃只是在该流水线也就是stream流上舍弃,并不会对原集合造成影响)
实现代码如下:这段代码是使用filter方法筛选出满足a开头的字符串
//使用filter过滤方法
ArrayList<String> list2 = new ArrayList<>();
Collections.addAll(list2,"abb","acc","aee","bbb","edf");
list2.stream().filter(s->s.startsWith("a")).forEach(s -> System.out.println(s));
stream流的limit方法(获取前n个元素)
limit方法能在该stream流里取出前n个元素,如下:下列代码表示获取前三个元素
//使用limit方法获取前n个元素
list2.stream().limit(3)
.forEach(s-> System.out.println(s));
steam流的skip方法(跳过前n个元素)
skip方法能跳过stream流里的前n个元素,并留下后面的元素
如下,跳过前4个元素留下后面的元素
//使用skip方法跳过前n个元素,获取后面的元素
list2.stream()
.skip(4).forEach(s-> System.out.println(s));
stream流的distinct去重方法
distinct能去除该stream流中重复的元素,如果是自定义的元素的话,需要从写equals方法和hashcode方法,因为他是根据这两个方法进行去重的,而这两个方法主要是依赖地址值来进行比较两个元素是否相等的
string类型的去重代码如下:(写这里因为Java中已经重写好了string的equals方法和hashcode方法所以这里不需要重写)
//distinct去重方法的使用
ArrayList<String> list3 = new ArrayList<>();
Collections.addAll(list3,"aaa","aaa","baa","ccc");
list3.stream().distinct().forEach(s -> System.out.println(s));
}
而我们自定义的数据类型因为采用的是默认的equas方法和hashcode方法,所以我们需要重写这两个方法,让他们处理的时候比较属性值而不是方法默认的地址值
如下:
Student类(重写了equals方法和hashcode方法)
package com.itazhang.myStreamDemo;
import java.util.Objects;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
运行测试类代码如下:
package com.itazhang.myStreamDemo;
import java.util.ArrayList;
import java.util.Collections;
public class SteamDemo2 {
public static void main(String[] args) {
Student stu1 =new Student("azhang",22);
Student stu2 =new Student("lei",23);
Student stu3 =new Student("azhang",22);
ArrayList<Student> list = new ArrayList<>();
Collections.addAll(list,stu1,stu2,stu3);
list.stream().distinct().forEach(student -> System.out.println(student));
}
}
stream流里的静态concat方法
使用concat方法时,会将两个stream流合并为一个steam流,但是得尽可能保证两个stream流里的数据类型相同,如果不同,合并出来的新stream流会默认将该流里面的数据转化为前两个数据类型的父类,如int和char都是object的子类,如果int和char两个类型的stream流合并那么合并出来的新stream流会将里面的数据转成object类型
而这个方法在使用的时候,因为concat是Stream流中的静态方法,所以使用时直接调用,且具体格式如下:
Stream.concat(list1.stream(),list2.stream())
这时就是将list1.stream()和list2.stream()合并成几个新的Stream流,这个新的stream流就是这一串代码总体,如果想使用直接在后面使用相关Stream流的方法,如下:
//stream流中concat的使用,这个方法时stream接口中的静态方法,所以通过Stream直接调用
ArrayList<String> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
list1.add("aaa");
list2.add("bbb");
Stream.concat(list1.stream(),list2.stream()).forEach(s -> System.out.println(s));
Stream流中的map方法
Stream流中的map方法主要时将流中的每一个数据按照一定的规则转化为相应的数据类型,例如将Integer类型转化为String类型。
具体实现如下,这里要求将一个字符串集合中的字符串里面的数字取出,而不取出其他的数据类型的值,这个时候就能用到map方法。当然这里map方法里面的转换规则能用lamda表达式来表达。
package com.itazhang.myStreamDemo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Function;
public class StreamDemo3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a-18","b-17","c-16");
//要求将集合list里的数字全部拿出,而不取出其他的字符,此时就能使用map方法
list.stream().map(new Function<String, Object>() {
@Override
//此时的s表示该Stream流中的每一个数据
public Object apply(String s) {
//通过split方法将该字符串分为两个字符串,将前面和后面的字符串存入数组
//再将后面的字符串从数组中取出并用Integer的parseInt方法将该字符串转为int类型
String[] str = s.split("-");
int i = Integer.parseInt(str[1]);
return i;
}
}).forEach(s-> System.out.println(s));//这里的s表示已经转化为int类型的每一个数字
}
}
用lamda表达式书写上诉代码,逻辑是一样的,先用list的stream流,再用map方法去更改里面相应数据的数据类型,在map方法里先将stream流取出并用数组存储,再用split方法将该数组通过"-"切割开,再通过数组[1]得到字符串里的数字,最后将该数字通过Integer的parseint方法转化为整数类型,最后再将该stream流中的int类型的数字打印出,从而实现与上诉匿名内部类相同的功能。
package com.itazhang.myStreamDemo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Function;
public class StreamDemo4 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a-18","b-17","c-16");
//要求将集合list里的数字全部拿出,而不取出其他的字符,此时就能使用map方法
//下面是这个方法的lamda表达式,跟重写的匿名内部类属于一个道理
list.stream().map(s -> Integer.parseInt(s.split("-")[1])).forEach(s-> System.out.println(s));
}
}
Stream流的终结方法
再stream流使用完中间方法之后,最后能使用终结方法,相当于是将之前流水线上的产品打包,所以后面是没办法再使用中间方法,也就相当于商品打包之后没办法重新加工。
foreach方法
stream流里的foreach方法是遍历该stream流里的所有数据,且该方法没有返回值,所以在该方法之后不能再使用该stream流,代码如下:遍历该stream流的数据并将其打印出来
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a-18","b-17","c-16");
//下面是测试stream流里相关的终结方法
//foreach方法,遍历stream流里的每个数
list.stream().forEach(s-> System.out.print(s+ " "));
System.out.println();
count方法
stream流里的count方法是统计该stream流里所拥有的元素个数,且返回值为int类型,也就是该stream流中元素的个数,所以也不能再继续使用该stream流
//count方法,统计该stream流里数据的个数
System.out.println(list.stream().count());
toArray方法
toArray方法是将该stream流里的数据存储到一个数组中,且返回值为该数组,所以在使用了该方法之后也不能继续使用该stream流
//toArray方法,将stream流里的方法放到数组中
for (int i = 0; i < list.stream().toArray().length; i++) {
System.out.println(list.stream().toArray()[i]);
}
但是如果我想将stream流里的数据放入一个固定数据类型的数组中,这个时候我们应该在使用
toArray方法的时候就new一个想要存储的数据类型数组,格式如下:我想将stream流里的数据放入一个String类型的数组
list.stream().toArray(s->new String [s]);
//toArray方法,将stream流里的方法放到数组中
//如果要添加特定数据类型的数组,再toArray方法里自己new一个想要存储的数据类型数组,再将其放入
list.stream().toArray(s->new String [s]);
toList方法
toList方法是将该stream流的数据存储到一个集合中,且返回值为该集合,所以使用了该方法之后也不能再使用本stream流,
//toList方法,将stream流中的数放入到一个list集合中
List<String> listtemp = list.stream().toList();
for (String s : listtemp) {
System.out.println(s);
}
collect方法
collect方法是可以将一个stream流用list,set,map收集起来
具体的格式为
一、
将数据收集到list集合当中:
list.stream().collect(Collectors.toList());如下:
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"azhang-男-22","lei-女-21","lisi-男-22","azhang-男-22");
//要求将男性通过筛选出来放入到一个集合newlist当中
List<String> newlist = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toList());
二、
将数据收集到set集合当中,这个时候会自动创建一个hashset来接受数据,根据hashset的特点,这个时候就不会将重复的数据添加到这个集合中,就能实现添加数据不重复的特点:
list.stream().collect(Collectors.toSet());
//要求将男性通过筛选后放入到一个集合中,且要求对象不重复,这个时候就可以使用set集合存储
Set<String> newset = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toSet());
三、
将数据放入到Map集合中,要求男生姓名为键,年龄为值
创建格式如下
s -> s.split("-")[0]为键
s -> s.split("-")[2])为值
.collect(Collectors.toMap(s -> s.split("-")[0], s -> s.split("-")[2]));
具体实现代码如下:
Map<String, String> map = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
//在collect使用map接收返回数据类型的时候,需要重写两个接口里的方法
//一个为键的重写方法,一个为值的重写返回方法
.collect(Collectors.toMap(s -> s.split("-")[0], s -> s.split("-")[2]));
System.out.println(map);
最终小练习:要求如下
实现代码如下:
package com.itazhang.myStreamDemo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamExercise3 {
public static void main(String[] args) {
ArrayList<String> listman = new ArrayList<>();
ArrayList<String> listgirl = new ArrayList<>();
Collections.addAll(listman,"张三,23","李四,24","张得帅,25","张的美,26","张的戳,24","张的仇,23");
Collections.addAll(listgirl,"杨三,23","杨四,24","杨得帅,25","杨的美,26","李的戳,24","李的仇,23");
Stream<String> manstream1 = listman.stream().filter(s -> s.split(",")[0].length() == 3).limit(2);
Stream<String> girlstream1 = listgirl.stream().filter(s -> s.startsWith("杨")).skip(1);
List<Actor> collectactor = Stream.concat(manstream1, girlstream1).map(s -> {
String name = s.split(",")[0];
int age = Integer.parseInt(s.split(",")[1]);
Actor a = new Actor(name, age);
return a;
}).collect(Collectors.toList());
System.out.println(collectactor);
}
}