code: public Collection<AbstractAttribute> findAttributesByAttributeFilter(
              final boolean parallel,
              final Predicate<? super AbstractAttribute> filter)
              throws NullValueException {
  
          if (filter == null) {
              throw new NullValueException("The filter should not be null");
          }
  
          final Collection<Lock> locks = getReadLocks(rootTags);
          for (final Lock lock : locks) {
              lock.lock();
          }
  
          try {
              final Stream<AbstractAttribute> attributesStream = buildAllTagsStream(
                      parallel).filter(tag -> tag.getAttributes() != null)
                              .map(tag -> {
                                  return tag.getAttributes();
                              }).flatMap(attributes -> attributes.stream());
  
              return attributesStream.filter(filter).collect(Collectors.toSet());
          } finally {
              for (final Lock lock : locks) {
                  lock.unlock();
              }
  
          }
      }
docs: Finds and returns the collection of attributes (including from nested
  tags) of the tags filtered by the given filter
  
  @param parallel
  true to internally use parallel stream. If true it
  will split the finding task to different batches and
  will execute the batches in different threads in
  parallel consuming all CPUs. It will perform faster
  in finding from extremely large number of tags but at
  the same time it will less efficient in finding from
  small number of tags.
  @param filter
  the filter lambda expression containing return true
  to include and false to exclude.
  @return the collection of attributes by the given filter.
  @throws NullValueException
  if the {@code tagName} is null
  @since 3.0.0
  @author WFF