Speed comparison:

Class POI.Test
{

/// do ##class(POI.Test).main()
ClassMethod main(rounds As %Integer = 1000, size As %Integer = 10, nullPercent As %Integer = 20)
{
    set list = ..getList(size, nullPercent)
    
    write !,"Rounds: ", rounds
    write !,"Size: ", size
    write !,"NullPercent: ", nullPercent
    write !,"List: "
    zwrite list
        
    set ways = $lb("listnext", "listfind", "lfs", "base")
    for i=1:1:$ll(ways) {
        set way = $lg(ways, i)
        set start = $zh
        
        set list2 = $classmethod(,way, rounds, list)
        
        set end = $zh
        
        set time = end - start
        
        write !,"Way: ", way
        write !,"Time: ", time
        write !,"List: "
        zwrite list2
        write !
    }
}

ClassMethod base(rounds As %Integer = 1000, list As %List) As %List
{
    for i=1:1:rounds {
        set origList = list
    }
    quit origList
}

ClassMethod listfind(rounds As %Integer = 1000, list As %List)
{
    for i=1:1:rounds {
        set origList = list
        set ptr=0
        for  set ptr=$LISTFIND(origList, $c(0), ptr) quit:'ptr  set $LIST(origList, ptr, ptr)=$lb()
    }
    quit origList
}

ClassMethod listnext(rounds As %Integer = 1000, list As %List)
{
    for i=1:1:rounds {
        set list2 = ""
        set ptr = 0
        while $listnext(list, ptr, elem) {
            if elem'=$C(0) {
                set list2 = list2 _ $LB(elem)
            } else {
                set list2 = list2 _ $LB()
            }
        }
    }
    quit list2
}

ClassMethod lfs(rounds As %Integer = 1000, list As %List)
{
    for i=1:1:rounds {
        set str = $lts(list)
        set str = $tr(str, $c(0))
        set list2 = $lfs(str)    
    }
    quit list2
}

ClassMethod getList(size As %Integer = 10, nullPercent As %Integer = 20) As %List
{
    set list = ""
    for i=1:1:size {
        set list = list _ $lb($select($random(101)<=nullPercent:$c(0),1:$random(50)))
    }
    quit list
}

Results:

POI>do ##class(POI.Test).main(1000000)
 
Rounds: 1000000
Size: 10
NullPercent: 20
List: list=$lb(25,20,$c(0),$c(0),4,42,$c(0),28,44,3)
 
Way: listnext
Time: .944814
List: list2=$lb(25,20,,,4,42,,28,44,3)
 
 
Way: listfind
Time: .610244
List: list2=$lb(25,20,,,4,42,,28,44,3)
 
 
Way: lfs
Time: .430088
List: list2=$lb("25","20","","","4","42","","28","44","3")
 
 
Way: base
Time: .032151
List: list2=$lb(25,20,$c(0),$c(0),4,42,$c(0),28,44,3)

listfind solution seems optimal.

There are several ways to do that. Let's say you have a persistent property

Property Problem As %String;

That you don't want anyone to see.

1. Make it private:

Property Problem As %String [ Private ];

It would not be displayed altogether

2. Add accessor methods:

Property Problem As %String;

Method ProblemGet() As %String [ ServerOnly = 1 ]
{
    Quit "****"
}

Method ProblemRealGet()
{
    Quit i%Problem2
}

Method ProblemSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    Set i%Problem2 = Arg
    Quit $$$OK
}

This way default callers would get **** and only your app code can access the real value.

3. Do not project property to XML

Property Problem As %String(XMLPROJECTION = "none");

As ensemble message viewer is XML-based it would hide property from it.

4. Create a special datatype. For example MyApp.Datatype.Password datatype returns **** as Display and ODBC values:

Class MyApp.Datatype.Password Extends %String
{

ClassMethod LogicalToDisplay(%val As %String) As %String [ Internal ]
{
    q $case(%val,"":"",:"*****")
}

ClassMethod LogicalToOdbc(%val As %String) As %String [ Internal ]
{
    q $case(%val,"":"",:"*****")
}

}

To use it:

Property Problem As MyApp.Datatypes.Password;

5. Extract the property into a separate class and reference the object of that class.

These approaches could be combined.