Internowanie łańcuchów
W programowaniu internowanie łańcuchów lub internalizacja łańcuchów oznacza metodę przechowywania i dostępu do obiektów typu String, w której identyfikatorem konkretnego obiektu znajdującego się w pamięci jest unikatowy łańcuch znaków stanowiący jednocześnie jego wartość. Bezpośrednie odwołania do tak przechowywanego obiektu polegają na podaniu jego nazwy jednoznacznie identyfikującej jego instancję, niezależnie od umiejscowienia w kodzie.
Internalizację łańcuchów można zobrazować stosując porównanie do obiektów tworzonych na podstawie klasy string z języka C++:
#include <iostream> #include <string> using std::string; int main (int argc, char *argv[]) { string a("tekst"); string b("tekst"); const char *ap = a.c_str(); const char *bp = b.c_str(); std::cout << "a = " << a << " (" << &ap << ")" << std::endl << "b = " << b << " (" << &bp << ")" << std::endl; return(0); }
Gdyby w powyższym przykładzie użyto łańcuchów internalizowanych zamiast typowych obiektów typu string to wartości stałych wskaźnikowych ap i bp byłyby takie same. Ponieważ w C++ nie istnieje wbudowana internalizacja łańcuchów, więc aby to osiągnąć należy użyć zewnętrznej klasy.
Spis treści |
Obsługa [edytuj]
Większość języków obsługujących internalizację łańcuchów pozwala tworzyć ich nazwy w dynamiczny sposób, na przykład z użyciem innych zmiennych i wyrażeń, tak jak w poniższym kodzie Ruby'ego:
a = "te" b = "kst" c = :tekst puts 'adres symbolu o nazwie w zmiennej c = ' + c.object_id.to_s puts 'adres symbolu o nazwie w zmiennych a i b = ' + :"#{a+b}".object_id.to_s # dynamicznie utworzona nazwa symbolu
Aby dostęp do internalizowanych obiektów był możliwy kompilator lub interpreter danego języka programowania tworzy wewnętrzną pulę internowania łańcuchów (ang. string intern pool), w której dochodzi do odwzorowania łańcuchów tekstowych będących ich identyfikatorami na wskaźniki do miejsc w pamięci pod którymi się znajdują. Nazwa internalizacja lub internowanie oznacza, że ten sposób obsługi obiektów polega na nadawaniu stworzonym w trakcie działania programu łańcuchom "wewnętrznego" znaczenia. Dzięki tej operacji stają się one semantycznie istotnym elementem programu, zbliżonym do nazw zmiennych.
Zastosowania [edytuj]
Łańcuchy internalizowane używana są do obsługi często prezentowanych komunikatów tekstowych (np. stałych informacji diagnostycznych lub ich przedrostków), a także w połączeniu ze strukturami danych, które używają łańcuchów jako indeksów (np. tablice asocjacyjne). To ostatnie zastosowanie daje programiście pewność, że nie dojdzie do przypadkowej zmiany przechowywanego w zmiennej klucza indeksującego.
Dzięki internalizacji można uniknąć zbędnego duplikowania łańcuchów znaków i zaoszczędzić ilość pamięci zajmowanej przez uruchomiony program. Odwoływanie się do łańcuchów wskazywanych za pomocą ich nazw jest też wygodne w użyciu, szczególnie tam, gdzie znaczenie ma dynamiczny ich wybór dokonywany w zależności od warunków umieszczonych w programie – przyspiesza więc proces tworzenia i zwiększa czytelność kodu. Wadą internowania łańcuchów jest nieco dłuższy czas potrzebny na ich utworzenie lub dostęp do nich.
Internalizacja w językach programowania [edytuj]
Internowanie łańcuchów dostępne jest w niektórych nowych, obiektowych językach programowania, włączając w to Pythona, Javę, Ruby'ego i języki działające w ramach platformy programistycznej Microsoft .NET. W niektórych językach użycie internalizacji wymaga jedynie posłużenia się odpowiednią składnią, a w innych konieczne jest wykorzystanie odwołania specjalnej klasy. Na przykład w Javie pojedyncza kopia każdego łańcucha, nazywana też jego internem, jest obsługiwana za pomocą metody klasy String o nazwie String.intern().
Java [edytuj]
W Javie internalizacja obsługiwana jest za pomocą metody intern() należącej do klasy String:
class internalizacja { public static void main(String[] args) { String a = "tekst"; String b = new StringBuffer("te").append("kst").toString(); System.out.println("przed intern"); if (a == b) { System.out.println(" - ten sam obiekt"); } String c = a.intern(); String d = b.intern(); System.out.println("po intern"); if (c == d) { System.out.println(" - ten sam obiekt"); } } }
Warto zauważyć, że stałe tekstowe Javy są internalizowane automatycznie:
class internalizacja { public static void main(String[] args) { String a = "tekst"; String b = new StringBuffer("te").append("kst").toString().intern(); if (a == b) { System.out.println(" - ten sam obiekt"); } } }
Ruby [edytuj]
W języku Ruby internalizacja stosowana jest w odniesieniu podstawowego typu danych zwanego symbolem. Poniższy fragment kodu pokazuje różnicę między zwykłymi łańcuchami tekstowymi a łańcuchami internalizowanymi:
puts "Przypisanie tekstu" a = "tekst" b = "tekst" puts 'adres a = ' + a.object_id.to_s puts 'adres b = ' + b.object_id.to_s puts "Przypisanie symbolu" a = :tekst b = :tekst puts 'adres a = ' + a.object_id.to_s puts 'adres b = ' + b.object_id.to_s
Po jego wykonaniu otrzymamy na przykład:
Przypisanie tekstu adres a = 82110 adres b = 82150 Przypisanie symbolu adres a = 101378 adres b = 101378
W przypadku użycia internalizowanych łańcuchów (w języku Ruby nazywanych symbolami) obiekty a i b zajmują ten sam obszar w pamięci.
Historia [edytuj]
Pierwsze konstrukcje oznaczające internalizowane łańcuchy pojawiły się w języku programowania Lisp pod nazwą symboli atomowych lub atomów. Struktura danych używana do utrzymywania takiej puli łańcuchów nosiła nazwę oblist (w przypadku listy połączonej) lub obarray (w przypadku zaimplementowania jej w formie tablicy).