sábado, 3 de novembro de 2007

Listas em Cache

Listas em Caché - abordagem orientada a objeto

Ao contrário do Java, no Caché da Intersystems não temos uma grande API quando se trata de estruturas de dados de coleções (listas, filas, conjuntos, árvores, etc). De fato, quando trabalhamos com orientação a objeto no Caché, basicamente existem dois conceitos de coleção: as listas e os arrays.

Vamos verificar então o que listas e arrays significam para o Caché. Listas são, como o nome bem indica, listas de objetos (como aqueles que você escreve) ou de tipos básicos (como %Integer ou %Float). Cada elemento de uma lista é associado a uma posição da lista. Neste caso, listas em Caché são bem parecidas com listas em Java (em termos de interface, de implementação não dá pra comparar bem, já que em Java podemos ter listas como ArrayList ou LinkedList.

Em Caché, a classe abstrata %Collection.AbstractList fornece a interface básica pela qual podemos trabalhar com listas. Adaptada da documentação, o código abaixo ilustra a criação de uma lista, quatro inserções de elementos nesta lista, e como podemos percorrer a lista, recuperando seus elementos:


; instancia nova lista
Set list=##class(%ListOfDataTypes).%New()
; adiciona quatro elementos na lista
Do list.Insert("DArtagnan")
Do list.Insert("Athos")
Do list.Insert("Porthos")
Do list.Insert("Aramis")
; percorre a lista
For i=1:1:list.Count() Write list.GetAt(i),!



Arrays no Caché, por outro lado, são bem diferentes do que o nome pode sugerir (pelo menos pra mim, que vim de linguagens como Java, Pascal, C/C++). Arrays do Caché são como objetos da interface Map do Java, eles necessitam além do elemento a ser guardado na estrutura (objeto ou tipo básico de dados), uma "chave" associada, para você acessar o elemento. Essas estruturas são muito úteis ao implementar coisas como tabelas de Hash, assim como a Hashtable.

Nada melhor que um exemplo para esclarecer, por isso vamos ver o exemplo adaptado da documentação da classe %Collection.AbstractArray, que serve como interface para as classes de array no Caché:

; instancia um objeto array 
Set arr=##class(%ArrayOfDataTypes).%New()
; coloca itens no array - o segundo parâmetro é a "chave"
Do arr.SetAt("red","color")
Do arr.SetAt("large","size")
Do arr.SetAt("expensive","price")
; iteração no array
Set key=""
For Set value=arr.GetNext(.key) Quit:key="" Write key,":",value,!



(Notem que o método SetAt, correspondente ao put do java.util.Map, tem os parâmetros invertidos com relação a este, enquanto no put a chave vem como primeiro parâmetro, no SetAt a chave vem no segundo.)

Em ambos exemplos acima, usamos tipos de dados básicos como elementos nas lista e array, mas poderíamos ter usado objetos. Para isso, ao invés de termos usado %ListOfDataTypes e %ArrayOfDataTypes, usaríamos %ListOfObjects e %ArrayOfObjects, respectivamente.

Quando usamos coleções como propriedades de classes, podemos ou declarar a propriedade explicitamente como objeto das classes de Array/List (como faríamos como qualquer outra classe), ou então podemos declarar assim:


; uma lista de %Strings:
; em vez de: Property Colors As %ListOfDataTypes
Property Colors As List Of %String


Vale lembrar que esses objetos arrays e lists são do tipo Serial, ou seja, herdam de %SerialObject, e que portanto vão "serializadas" dentro de outra classe, se forem persistidas. Se a sua lista for muito grande, ou mesmo que a lista não tenha muitos elementos, mas os seus elementos sejam %SerialObject, pode ocorrer um erro de MAXSTRING.

Considere a classe abaixo:


Class Teste.Estouro Extends %Persistent [ ClassType = persistent ] {

Property lista As list Of %String;

ClassMethod teste(qtde as %Integer)
{
set a = ##class(Teste.Estouro).%New()
w a.%Save()
For i=1:1:qtde {
do a.lista.Insert(i)
}
w a.%Save()
}
}


Experimente chamar de uma janela de terminal o método teste, passando diferentes valores. Ao se passar uns 10000, você já pode conferir o erro de MAXSTRING. Lembrando que isso vale para versão 5.2 do Caché. Ouvi dizer que na versão 2007, o limite de tamanho de Strings aumentou, então esse tipo de erro deve ocorrer menos frequentemente, mas não cheguei a verificar na documentação isso.

4 comentários:

Unknown disse...

eu juro q tentei pensar num comentário, mas preferi concordar com tudo que você disse! rs

Anônimo disse...

Estranho que "em memória" tudo ocorre normalmente, mas qdo persistido, o "pau" ocorre qdo o tamanho da string alcança por volta de 31900 ou um pouco acima disso...

Qto o tamanho somado do conteúdo da lista é inferior a este valor limite, o Caché salva normalmente a lista.

Class Teste.Estouro Extends %Persistent [ ClassType = persistent ]
{

Property lista As list Of %String;

Property Tamanho As %Integer;

ClassMethod teste(qtde As %Integer)
{
set tSC = $$$OK
set b = 0
set a = ##class(Teste.Estouro).%New()
set tSC = a.%Save()
For i=1:1:qtde {
w i,!
set tSC = a.lista.Insert(i)
set b = b+$l(i)
w "tamanho: "_b,!
w "tSC neste momento:... "_tSC,!
w "valor de LISTA: "_a.lista.GetAt(i),!
if b >= 100000
{
set a.Tamanho = b
w "salvando... "_a.%Save()
goto sair
}
}
set a.Tamanho = b
if '$SYSTEM.Status.Error(tSC)
{
w !,"tSC:...."_tSC,!
w a.%Save()

}
sair
quit tSC

}


Sei lá... alguém tem uma luz?

Abçs,
Carlos

Anônimo disse...

Prezado amigo, parabens pelo seu BLOG.

Sobre esse problema de estouro do List of %String com mais de 32k, tente usar Array Of %String que o problema fica resolvido. Isto porque quando se usa array, o Caché cria internamente uma tabela para guardar o conteudo da coleção, onde cada registro equivale a um item da lista.

um abraço.

PS. Acho q já nos cruzamos antes, não?

Andarilho disse...

Olha o sir Turibio...

Mesmo com o array of %String acontece o erro.

Tente substituir no código do Carlos, o:

As list Of %String;
por
As array Of %String;

e
set tSC = a.lista.Insert(i)
por
set tSC = a.lista.SetAt(i,i)

e vc vai ver que o mesmo erro acontece.