czy string jest typem wartosciowym czy referencyjnym

0

Hej, mam pytanie takie jak w temacie ;)
moze mi ktos to wyjasnic bo w kilku zrodlach znalazlem rozne informacje

pozdrawiam!

0
Sharpik napisał(a)

Hej, mam pytanie takie jak w temacie ;)
moze mi ktos to wyjasnic bo w kilku zrodlach znalazlem rozne informacje

pozdrawiam!

string jest typem referencyjnym, aczkolwiek nietypowym o tyle, że jego wartości nie można zmodyfikować po momencie jej utworzenia (immutable).

0

innymi slowy, jesli bedziesz mial Pana Tadeusza w 12 ksiegach w jednej zmiennej typu string, i zrobisz: panTadesz[4] = 'X'; to defacto nowy obiekt zostanie utworzony, caly string zostanie skopiowany z ta drobna poprawka i 'umiesczony' w Twojej zmiennej, a ow stary byc moze zaraz zostanie zebrany przez GC.. i jescze dopisze, ze .Net stara sie aby dwa napisy "mama" i "mama" zawsze wskazywaly na jeden i ten sam obiekt string, no bo skoro sa immutable no to nie ma sensu miec 2 identycznych kopii - ale nie zawsze mu to wychodzi :) String w Javie dziala identycznie, i tez mu nie wychodzi :)

0
quetzalcoatl napisał(a)

i jescze dopisze, ze .Net stara sie aby dwa napisy "mama" i "mama" zawsze wskazywaly na jeden i ten sam obiekt string, no bo skoro sa immutable no to nie ma sensu miec 2 identycznych kopii - ale nie zawsze mu to wychodzi :) String w Javie dziala identycznie, i tez mu nie wychodzi :)

może chodzi o przypadek compile-time constant expressions - w javie optymalizacja kompilatora jeśli chodzi o utrzymanie stringów działa tylko dla wyrażeń stałych w momencie kompilacji. Posługując się zaczerpniętym przykładem:

String can1 = 7 + "Up";  // Value of compile-time constant expression: "7Up"
String can2 = "7Up";     // "7Up"

obie referencje odnoszą się do jednego obiektu (interned strings).

W przypadku:

String word = "Up";
String can4 = 7 + word;  // Not a compile-time constant expression.

już tak nie będzie (patrz komentarz w 2 linii)

P.S. Informacje pochodzą sprzed 3 lat - jeśli coś się zmieniło, poprawcie mnie, tym bardziej że moja styczność z javą jest minimalna :)

0

nie wiem, ale zmienilo sie prawie na pewno, mamy juz JRE1.6, ciagle cos zmieniaja i "poprawiaja".. w JRE6 np. dorzucli m.in. mozliwosc..... uwaga uwaga - bytecode injection :) chyba nie trzeba tlumaczyc.

z tego co pamietam to kazdy obiekt w Javie jest opartzony hash'em, dzieki czemu mozna w miare szybko okreslic czy obiekty sa rozne. ze stringami to jest jakos tak, ze podczas dzialania instancje identycznych stringow tez maja szanse sie 'zlepic w jedna'.. nie wiem na jakiej zasadzie to dziala, ale chyba GC jak odsmieca pamiec to przy okazji weryfikuje to.. a moze konstruktor stringa szuka czy juz taka nie istnieje aby.. ale to tylko przeswity z jakeigos artykulu ktory dawno temu czytalem..

0

wychodzi chyba na to, że w C# automatycznie "internowane" są tylko string literals.

Przykład:

string s1 = "7up";
string s2 = 7 + "up";
string s3 = "7up";
int i1 = 7;
string s4 = i1 + "up";

Debug.WriteLine(string.Format("[Values]: s1={0}; s2={0}; s3={0}; s4={0}", s1, s2, s3, s4));

Debug.WriteLine(string.Format("[EQ]: s1==s2: {0}", s1 == s2));
Debug.WriteLine(string.Format("[EQ]: s1==s3: {0}", s1 == s3));
Debug.WriteLine(string.Format("[EQ]: s1==s4: {0}", s1 == s4));

Debug.WriteLine(string.Format("[Obj EQ]: s1==s2: {0}", (object)s1 == (object)s2));
Debug.WriteLine(string.Format("[Obj EQ]: s1==s3: {0}", (object)s1 == (object)s3));
Debug.WriteLine(string.Format("[Obj EQ]: s1==s4: {0}", (object)s1 == (object)s4));

Wyjście:

[Values]: s1=7up; s2=7up; s3=7up; s4=7up
[EQ]: s1=s2: True
[EQ]: s1=s3: True
[EQ]: s1=s3: True
[Obj EQ]: s1=s2: False
[Obj EQ]: s1=s3: True
[Obj EQ]: s1=s4: False

Uwagi:

  1. Kiedy operator == jest używany dla porównania 2 stringów, wywoływany tak naprawdę jest operator równości dla stringów.

C#:

class MainClass
{
	public static void Main(string[] args)
	{
	    string s1 = "7up";
            string s2 = 7 + "up";

            Console.WriteLine(string.Format("[EQ]: s1=s2: {0}", s1 == s2));

            Console.WriteLine(string.Format("[Obj EQ]: s1=s2: {0}", (object)s1 == (object)s2));
	}
}

IL:

.method public hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    // Code size       78 (0x4e)
    .maxstack  3
    .locals init ([0] string s1,
             [1] string s2)
    IL_0000:  nop
    IL_0001:  ldstr      "7up"
    IL_0006:  stloc.0
    IL_0007:  ldc.i4.7
    IL_0008:  box        [mscorlib]System.Int32
    IL_000d:  ldstr      "up"
    IL_0012:  call       string [mscorlib]System.String::Concat(object,
                                                                object)
    IL_0017:  stloc.1
    IL_0018:  ldstr      "[EQ]: s1=s2: {0}"
    IL_001d:  ldloc.0
    IL_001e:  ldloc.1
    // porównanie stringów
    IL_001f:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                   string)
    IL_0024:  box        [mscorlib]System.Boolean
    IL_0029:  call       string [mscorlib]System.String::Format(string,
                                                                object)
    IL_002e:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_0033:  nop
    IL_0034:  ldstr      "[Obj EQ]: s1=s2: {0}"
    IL_0039:  ldloc.0
    IL_003a:  ldloc.1
    // równość obiektów
    IL_003b:  ceq
    IL_003d:  box        [mscorlib]System.Boolean
    IL_0042:  call       string [mscorlib]System.String::Format(string,
                                                                object)
    IL_0047:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_004c:  nop
    IL_004d:  ret
  } // end of method MainClass::Main
  1. Do porówywnania zawartość stringów należy używać metody Compare(). (

According to nprof, the Good Comparison (Compare) takes 1.69% of the total execution time of the code, while the Bad Comparison (==) takes 5.50% of the total execution time.
)

  1. Aby "ręcznie" używać interned strings, moża użyć metod klasy string: IsInterned (zwraca referencję do stringa, jeśli jest on w intern pool, lub null w przeciwnym wypadku) i Intern (podobnie jak IsIntered, z tą różnicą, że zawsze zwróci referencję).

  2. Cytat z Programmer's Guide to Java™ Certification, A: A Comprehensive Primer, Second Edition

General Contract of the hashCode() method
The general contract of the hashCode() method stipulates:

Consistency during execution: Multiple invocations of the hashCode() method on an object must consistently return the same hash code during the execution of an application, provided the object is not modified to affect the result returned by the equals() method. The hash code need not remain consistent across different executions of the application. This means that using a pseudorandom number generator to produce hash values is not a valid strategy.

Object value equality implies hash value equality: If two objects are equal according to the equals() method, then the hashCode() method must produce the same hash code for these objects. This tenet ties in with the general contract of the equals() method.

Object value inequality places no restrictions on the hash value: If two objects are unequal according to the equals() method, then the hashCode() method need not produce distinct hash codes for these objects. It is strongly recommended that the hashCode() method produce unequal hash codes for unequal objects.

Note that the hash contract does not imply that objects with equal hash codes are equal. Not producing unequal hash codes for unequal objects can have an adverse effect on performance, as unequal objects will hash to the same bucket.

A tu trochę z MSDN:

The default implementation of GetHashCode does not guarantee uniqueness or consistency; therefore, it must not be used as a unique object identifier for hashing purposes. Derived classes must override GetHashCode with an implementation that returns a unique hash code. For best results, the hash code must be based on the value of an instance field or property, instead of a static field or property.

0

@przyklad:
minimalistyczne sprawdzanie nie jest wykladnia czy sa internowane.. runtime'owe moze zachodzic gdzies bardzo gleboko, np. podczas boxingu i np. dla stringow ponad 1kB, 10kB, 100kB.. trzeba by przeszukac msdn'a i sprawdzic czy gdzies jest napisane explicite ze .Net nie internuje runtimeowo. ja tylko trafialem na zapisy ze "moze, ale nie musi", przyznam - nie szukalem zbyt uparcie

@uwagi:
IL: dzieki za wysilek wlozony w zaglebienie sie na samo dno, dobrze wiedziec ze ktos jeszcze wie ze jest cos ponizej tekstu zrodla..

hash: dlatego napisalem ze hashCode sa dobre do szybkiego sprawdzania czy obiekty sa rozne, a nie rowne!! jesli hash sa rozne, to sa rozne. jak sa rowne, to trzeba odpalic wlasciwy kod porownania. z hash jest czysty zysk na szybkosci, nawet jesli sie zdarza ze czasem rozne maja ten sam.

0
quetzalcoatl napisał(a)

@przyklad:

być może należałoby sprawdzić dla większych stringów, choć mi osobiście wydaje się, że runtime obsługuje stringi w ten sam sposób, niezależnie od wielkości (w końcu wszędzie można się natknąć na zalecane używanie stringbuildera :)), a test równości referencji pokazuje korzystanie z puli internowanych stringów. Ale nie upieram się bardzo :) - jeśli ktoś się kiedyś natknie na dokładny opis, niech się podzieli linkiem.

1 użytkowników online, w tym zalogowanych: 0, gości: 1