Java 8 альтернатива для проверки данных внутри нескольких вложенных циклов - PullRequest
0 голосов
/ 05 декабря 2018

У меня есть вопрос, касающийся проверки данных во вложенном цикле for.

public class Object1{
  private String obj1Name;

  private String obj1Desc;

  private List<Object2> object2List;

  //Setters and getters
}


public class Object2{
  private String obj2Name;

  private String obj2Desc;

  private List<Object3> object3List;

  //Setters and getters
}

public class Object3{
  private String obj3Name;

  private String obj3Desc;
  //Setters and getters
}

Я хочу проверить name и desc во всех объектах вместо использования вложенного цикла, подобного следующему:

List<Object1> object1List = getObject1List();

for(Object1 object1 : object1List ){
   if(object1.getObj1Name() == null){
     //throw error
   }

   if(object1.getObj1Desc() == null){
     //throw error
   }

   for(Object2 object2 : object1.getObject2List()){
        if(object2.getObj2Name() == null){
            //throw error
        }

        if(object2.getObj2Desc() == null){
            //throw error
        }

        //loop Object 3 ...
   }
}

Есть ли лучший способ сделать это?

Ответы [ 6 ]

0 голосов
/ 05 декабря 2018

Если их валидация кажется одинаковой, только изменение типа, то используйте интерфейс, в котором Object1, Object2 и Object3 реализует ICommon:

interface ICommon {
  String getField1();
  int getField2();
  Iterable<? extends ICommon> getChildren();
}

Тогда имейте это:

private void validate0(String prefix, ICommon common) {
  if (common.getField1() == null) throw new ValidationException(prefix + ".field1 can't be null");
  if (common.getField2() == null) throw new ValidationException(prefix + ".field2 can't be null");
  int index = 0;
  for (ICommon child : getChildren()) {
     validate0(prefix + ".children[" + index + "]", child);
    ++index;
  }
}

public void validate(ICommon common) {
   validate0("common", common);
}

Вы также можете инвертировать это, используя BiConsumer<String, ICommon> (или Consumer<ICommon>) для проверки, если вам не нужны правильные сообщения об ошибках).

  • Это будеттакже проясните, какая проверка может применяться к вашему объекту
  • Использование потока не всегда делает его более ясным (см. Peter Walser ответ и попытка понять большой поток :)).
0 голосов
/ 05 декабря 2018

Я просто собираюсь сказать это здесь, чтобы никто не делал то, что вы хотите сделать - используйте для этого подходящие рамки, лично я бы выбрал Hibernate Validator - очень легко интегрировать и использоватьИМО.Он будет поддерживать то вложение, которое у вас есть, без каких-либо проблем, и есть тонны обучающих программ в Интернете (даже их собственные - это хорошо) и как этого добиться;подсказка: несколько зависимостей и несколько аннотаций, и все готово.

0 голосов
/ 05 декабря 2018

A валидатор в качестве функционального интерфейса представляет собой Consumer, который использует значение определенного типа, выполняет проверки и выдает исключение, если что-то отключено.

Обходструктура данных (дерево) может быть выполнена через потоков (peek до посещение узла, flatmap до рекурса в дочерние элементы).Для проверки введем операцию NO-OP map , которая проверяет и возвращает значение, позволяя потоку продолжить.

BiConsumer<String, Object> checkRequired = (name, value) -> {
    if (value == null) {
        throw new IllegalArgumentException(name + " is required");
    }
};

Consumer<Object1> obj1Validator = obj1 -> {
    checkRequired.accept("Object1", obj1);
    checkRequired.accept("obj1Name", obj1.getObj1Name());
    checkRequired.accept("obj1Desc", obj1.getObj1Desc());
};

Consumer<Object2> obj2Validator = obj2 -> {
    checkRequired.accept("Object2", obj2);
    checkRequired.accept("obj2Name", obj2.getObj2Name());
    checkRequired.accept("obj2Desc", obj2.getObj2Desc());
};

Consumer<Object3> obj3Validator = obj3 -> {
    checkRequired.accept("Object3", obj3);
    checkRequired.accept("obj3Name", obj3.getObj3Name());
    checkRequired.accept("obj3Desc", obj3.getObj3Desc());
};

Object1 obj1 = ...; // assign some value

Stream.of(obj1)
    .peek(obj1Validator)
    .filter(x -> x.getObject2List() != null)
    .map(Object1::getObject2List)
    .filter(Objects::nonNull)
    .flatMap(List::stream)
    .peek(obj2Validator)
    .map(Object2::getObject3List)
    .filter(Objects::nonNull)
    .flatMap(List::stream)
    .peek(obj3Validator)
    .count();
0 голосов
/ 05 декабря 2018

Ну, вы можете использовать функцию / цикл, который просеивает через объект (универсальный объект).Таким образом, вам не нужно будет запускать отдельный цикл for для каждого объекта, если имя переменной-члена «desc» одинаково во всех трех объектах.Но это еще один уровень вложенности, поэтому не уверен, что вы захотите использовать его, если просто хотите избежать вложения.

0 голосов
/ 05 декабря 2018

РЕДАКТИРОВАТЬ: Идея для внешней проверки, вам нужно создать функциональный интерфейс

ObjectValidatorInterface ov = new ObjectValidator();
if(!object1List.stream().allMatch(o -> ov.validate(o, Object1.class))) {
        // throw error;
}

И интерфейс

@FunctionalInterface
interface ObjectValidatorInterface {
    boolean validate(Object object, Class clazz);
}

class ObjectValidator implements ObjectValidatorInterface {

    public boolean validate(Object object, Class clazz) {
        boolean valid = false;

        if(Object1.class.getName().equals(clazz.getName())) {
            valid = validateObject1((Object1) object);

        } else if(Object2.class.getName().equals(clazz.getName())) {
            valid = validateObject2((Object2) object);

        } else if(Object3.class.getName().equals(clazz.getName())) {
            valid = validateObject3((Object3) object);
        }

        return valid;
    }

    private boolean validateObject1(Object1 o) {
        boolean valid;
        valid = o.getObj1Name() != null && o.getObj1Desc() != null;
        if(!(o.getObject2List() == null || o.getObject2List().isEmpty())) {
            valid = valid && o.getObject2List().stream().allMatch(o2 -> validate(o2, Object2.class));
        }
        return valid;
    }

    private boolean validateObject2(Object2 o) {
        boolean valid;
        valid = o.getObj2Name() != null && o.getObj2Desc() != null;
        if(!(o.getObject3List() == null || o.getObject3List().isEmpty())) {
            valid = valid && o.getObject3List().stream().allMatch(o3 -> validate(o3, Object3.class));
        }
        return valid;
    }

    private boolean validateObject3(Object3 o) {
        return o.getObj3Name() != null && o.getObj3Desc() != null;
    }
}

ОРИГИНАЛЬНЫЙ ОТВЕТ

Вы можете сделать это, делегировав проверку каждому объекту:

List<Object1> object1List = getObject1List();

if(!object1List.stream().allMatch(Object1::isValid)) {
    //throw error
}

И добавив метод isValid к каждому объекту

public class Object1 {
    private String obj1Name;
    private String obj1Desc;
    private List<Object2> object2List;

    public boolean isValid() {
        return obj1Name != null
            && obj1Desc != null
            && object2List.stream().allMatch(Object2::isValid);
    }
}

public class Object2 {
    private String obj2Name;
    private String obj2Desc;
    private List<Object3> object3List;

    public boolean isValid() {
        return obj2Name != null
            && obj2Desc != null
            && object3List.stream().allMatch(Object3::isValid);
    }
}

public class Object3 {
    private String obj3Name;
    private String obj3Desc;

    public boolean isValid() {
        return obj3Name != null
            && obj3Desc != null;
    }
}
0 голосов
/ 05 декабря 2018

Что ж, вы определенно можете избежать «вложенности» с помощью Stream API:

if(object1List.stream()
                .anyMatch(a -> a.getObj1Name() == null ||
                        a.getObj1Desc() == null)){
    // throw error
}else if(object1List.stream()
                .anyMatch(a -> a.getObject2List().stream()
                       .anyMatch(b -> b.getObj2Name() == null ||
                                            b.getObj2Desc() == null))){
    // throw error
}else if(object1List.stream()
                .anyMatch(a -> a.getObject2List().stream()
                        .anyMatch(b -> b.getObject3List().stream()
                                .anyMatch(c -> c.getObj3Name() == null ||
                                                      c.getObj3Desc() == null)))){
     // throw error
}

Другой подход, более компактный, но, вероятно, менее эффективный:

boolean result = object1List.stream()
                .flatMap(a -> a.getObject2List().stream()
                        .flatMap(b -> b.getObject3List().stream()
                                .flatMap(c -> Stream.of(a.getObj1Name(),
                                        a.getObj1Desc(), b.getObj2Name(),
                                        b.getObj2Desc(), c.getObj3Name(), c.getObj3Desc()))))
                .anyMatch(Objects::isNull); 

if(result){ // throw error }

Итак,Чтобы сделать вывод о том, является ли проблема производительностью, перейдите к вашему подходу или попробуйте и посмотрите, может ли API параллельного потока принести вам пользу, в противном случае этого достаточно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...