Drools 7 遍历List集合

本文将分享几种Drools7中对List类型数据的遍历方法。

1.场景一:遍历工作内存中的一个List集合,且工作内存中只有一个List时。

entity:

public class Animal {
    private Integer id;
    private String name;

    public Animal(Integer id, String name) {
        this.id = id;
        this.name = name;
    }
}

rule:

//规则处理:打印出所有List集合中的元素
rule "hello_0"
    when
        //依次获取工作内存中的所有List集合
        $li:List(size > 0)

        //$m:Animal(name == "cat") from $li //获取List中name = "cat" 的Animal对象
        //依次获取当前List中的元素
        $m:Animal() from $li
    then
        //依次打印当前List中的message
        //注意,这里会被多次执行,执行次数为所有List集合元素总数(注意不是当前List集合总数)
        //因为规则从工作内存中筛选出一条完全符合conditions的数据,都会在agenda中插入一个规则与匹配数据的组合,再由agenda负责执行规则。
        System.out.println("id:"+$m.getId()+",name:"+$m.getName());
end

创建并插入数据源

KieServices kieServices = KieServices.Factory.get();
KieContainer kieContainer = kieServices.newKieClasspathContainer();
kieSession = kieContainer.newKieSession();
//实例化一个List,并插入到工作内存中
List<Animal> list = new ArrayList<Animal>(){{
                this.add(new Animal(100,"cat"));
                add(new Animal(200,"mouse"));
                add(new Animal(300,"dog"));
            }};
kieSession.insert(list);
kieSession.fireAllRules();

执行结果:

id:300,name:dog
id:200,name:mouse
id:100,name:cat

2.场景二:工作内存中存在多个List,交叉校验多个List集合时

一般情况下与场景一种的写法相同,但是如果需要同时校验多个List中的元素时,例如:cat和mouse分别放在两个List中并插入内存如下,当工作内存中同时存在cat和mouse时,执行猫抓老鼠。

插入数据源:

List<Animal> list = new ArrayList<Animal>(){{
                this.add(new Animal(100,"cat"));
                add(new Animal(200,"mouse"));
                add(new Animal(300,"dog"));
            }};
List<Animal> list1 = new ArrayList<Animal>(){{
   this.add(new Animal(200,"mouse"));
   add(new Animal(300,"dog"));
}};
kieSession.insert(list);
kieSession.insert(list1);
kieSession.fireAllRules();

这时工作内存中有两个List,猫和老鼠放在不同的List中,按场景一中的写法,一次匹配只能获取到一个List,无法同时获取到另一个List中的数据,那么就永远不能执行猫抓老鼠了吗?当然能执行,只是需要对两个List进行预处理,一下给出两个方法:

第一种方法:在将两个List插入工作内存前将两个List合并,再将合并后的集合插入到工作内存,代码如下:

插入数据源:

List<Animal> list = new ArrayList<Animal>(){{
         this.add(new Animal(100,"cat"));
     }};
List<Animal> list1 = new ArrayList<Animal>(){{
         this.add(new Animal(200,"mouse"));
         add(new Animal(300,"dog"));
     }};
list.addAll(list1);
kieSession.insert(list);
kieSession.fireAllRules();

rule:

rule "hello_1"
    when
        //依次获取工作内存中的所有List集合
        $li:List(size > 0)
        //获取cat
        Animal(name == "cat") from $li
        //获取mouse
        Animal(name == "mouse") from $li
    then
        System.out.println("猫抓老鼠!!!喵");
end

执行结果:

猫抓老鼠!!!喵

       当fact中存在多个cat(n个)或mouse(m个)时,规则会被触发多次(n*m次)。有时候我们只想要规则执行一次即可,但是这里是不能用no-loop属性或 lock-on-active属性控制的,因为这里并不是规则被重新评估了,而是多个规则实例进入了agenda。

      这里说一下什么是规则实例,当一个条数据完全匹配一个规则的conditions时,这条规则和这条数据被称作是一个规则实例,drools会将这个规则实例一起放入到agenda等待规则触发。当有工作内存中存在多条数据与同一个规则完全匹配时,会分别组合成规则实例保存到agenda,这也就是no-loop或lock-on-active两个属性不起作用的原因。这里给出一个解决思路,使用accumulate对list集合遍历(点击查看更多关于accumulate),具体做法如下:

(1)同样,需要在插入数据源之前合并list集合

List<Animal> list = new ArrayList<Animal>(){{
                this.add(new Animal(100,"cat"));
                add(new Animal(400,"cat"));
            }};
List<Animal> list1 = new ArrayList<Animal>(){{
                this.add(new Animal(200,"mouse"));
                add(new Animal(300,"dog"));
            }};
list.addAll(list1);//合并list
kieSession.insert(list);//插入数据
kieSession.fireAllRules();

(2)rule

rule "hello_1"
    when
        //分析:当工作内存中同时存在猫和老鼠就触发一次规则,且不受cat和mouse数量的影响,
        Boolean(this == true) from accumulate(
            $li:List(),//从工作内存中获取待处理的数据集合
            //声明两个变量,分别表示“没cat”,“没mouse”
            init(boolean cat = false; boolean mouse = false),
            action(
                //java代码,遍历list
                for(int i = 0;i < $li.size();i++){
                    if(cat && mouse){ break;}
                    Animal am = (Animal)$li.get(i);
                    if("cat".equals(am.getName()) && !cat){
                        cat = true;
                    }
                    if("mouse".equals(am.getName()) && !mouse){
                        mouse = true;
                    }
                }
            ),
            result(cat && mouse)//返回遍历结果
        )

    then
        System.out.println("猫抓老鼠!!!喵");
end

执行结果:

猫抓老鼠!!!喵

 

第二种方法:插入前合并list集合感觉也有不方便的时候,也可以使用collect在rule中合并List然后用,但是这里合并后的集合元素并不是Animal,而是List,代码如下:

插入数据源:

List<Animal> list = new ArrayList<Animal>(){{
                this.add(new Animal(100,"cat"));
                add(new Animal(400,"cat"));
            }};
List<Animal> list1 = new ArrayList<Animal>(){{
                this.add(new Animal(200,"mouse"));
                add(new Animal(300,"dog"));
            }};
//分别插入多个List集合
kieSession.insert(list);
kieSession.insert(list1);
kieSession.fireAllRules();

rule

rule "hello_1"
    when
        //分析:当工作内存中同时存在猫和老鼠就触发一次规则,且不受cat和mouse数量的影响,
        Boolean(this == TRUE) from accumulate(
            $li:List() from collect (List()),//从内存中获取所有List集合并把List分别封装到$li中
            init(boolean cat = false; boolean mouse = false),
            action(
                for(int i = 0;i < $li.size();i++){
                    //依次遍历收集到的List集合
                    List item = (List)$li.get(i);
                    for(int j = 0;j<item.size();j++){
                       if(item.get(j) instanceof Animal){//判断当前对象是Animal
                            Animal ai = (Animal)item.get(j);
                          if("cat".equals(ai.getName()) && !cat){
                            cat = true;
                          }
                          if("mouse".equals(ai.getName()) && !mouse){
                            mouse =true;
                          }

                       }
                       if(cat && mouse) break;
                    }
                   if(cat && mouse) break;
                }
            ),
            result(cat && mouse)
        )

    then
        System.out.println("猫抓老鼠!!!喵");
end

执行结果:

猫抓老鼠!!!喵

     其实,对于多个List集合交叉校验和List集中检验但只触发一次规则的实现方法还可以借助function和global实现,说实话本文中设计的实体和场景很垃圾,很多想写的东西都很难表达出来,所以function和global的实现方法仅说一下思路就不在写代码了。

function实现上述场景的方法很简单,只需要用 collect 把所有List收集起来,然后作为参数调用function,function中对list进行遍历就可以;

global实现的话,就是写遍历List的service类,返回一个boolean值,再把这个service实例set到global里,rule里直接调用就可以了,这种方法是挺有局限性的,主要还是看各位同学在什么场景下应用了。

就这些吧!

  • 8
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值