Dlaczego ludzie nadal używają prymitywnych typów w Javie?

Od Javy 5, mieliśmy Boks / unboxing prymitywnych typów tak, że int jest owinięty java.lang.Integer, i tak dalej i tak dalej.

Widzę ostatnio wiele nowych projektów Java (które zdecydowanie wymagają JRE przynajmniej w wersji 5, jeśli nie 6), które używają int zamiast java.lang.Integer, chociaż jest to znacznie wygodniejsze, ponieważ ma kilka pomocniczych metod konwersji do wartości long i in.

Dlaczego niektóre nadal używają prymitywnych typów w Javie? Czy jest jakieś wymierne korzyści?

Author: nbro, 2011-03-05

19 answers

W Joshua Bloch 's skuteczna Java, Pozycja 5: "unikaj tworzenia niepotrzebnych obiektów", zamieszcza następujący przykład kodu:

public static void main(String[] args) {
    Long sum = 0L; // uses Long, not long
    for (long i = 0; i <= Integer.MAX_VALUE; i++) {
        sum += i;
    }
    System.out.println(sum);
}
Bieg trwa 43 sekundy. Taking the Long into the primitive sprowadza go do 6.8 sekundy... Jeśli to jakiś znak, dlaczego używamy prymitywów. W przypadku braku równości wartości natywnej problemem jest również brak (.equals() jest dość gadatliwy w porównaniu do ==)

Dla biziclopa:

class Biziclop {

    public static void main(String[] args) {
        System.out.println(new Integer(5) == new Integer(5));
        System.out.println(new Integer(500) == new Integer(500));

        System.out.println(Integer.valueOf(5) == Integer.valueOf(5));
        System.out.println(Integer.valueOf(500) == Integer.valueOf(500));
    }
}

Wyniki in:

false
false
true
false

edytujdlaczego (3) zwraca true i (4) zwraca false?

Ponieważ są to dwa różne obiekty. 256 liczb całkowitych najbliżej zera [-128; 127] są buforowane przez JVM, więc zwracają te same obiekty. Poza tym zakresem nie są jednak buforowane, więc tworzony jest nowy obiekt. Aby skomplikować sprawę, JLS wymaga, aby co najmniej 256 flyweightów było buforowanych. Implementatorzy JVM mogą dodać więcej, jeśli zechcą, co oznacza, że może działać na systemie, w którym najbliższe 1024 są buforowane i wszystkie zwracają wartość true... # awkward
 351
Author: corsiKa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-23 08:46:07

Autounboxing może prowadzić do trudnych do wykrycia NPEs]}

Integer in = null;
...
...
int i = in; // NPE at runtime

W większości sytuacji przypisanie null do in jest o wiele mniej oczywiste niż powyżej.

 75
Author: Heiko Rupp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-04 21:38:08

Typy prymitywne:

int x = 1000;
int y = 1000;

Teraz Oceń:

x == y

To true. Trudno się dziwić. Teraz wypróbuj typy pudełkowe:

Integer x = 1000;
Integer y = 1000;

Teraz Oceń:

x == y

To false. Prawdopodobnie. Zależy od czasu trwania. Czy to wystarczający powód?

 39
Author: Daniel Earwicker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-05 10:19:58

Typy pudełkowe mają gorszą wydajność i wymagają więcej pamięci.

 38
Author: Orbit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-04 21:19:43

Oprócz problemów z wydajnością i pamięcią, chciałbym wymyślić inny problem: List interfejs byłby uszkodzony Bez int.
Problemem jest przeciążona Metoda remove()(remove(int) vs.remove(Object)). remove(Integer) zawsze odwoływał się do wywołania tego ostatniego, więc nie można było usunąć elementu przez indeks.

Z drugiej strony, jest pułapka podczas próby dodania i usunięcia int:

final int i = 42;
final List<Integer> list = new ArrayList<Integer>();
list.add(i); // add(Object)
list.remove(i); // remove(int) - Ouch!
 34
Author: xehpuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-18 15:33:21

Czy naprawdę możesz sobie wyobrazić

  for (int i=0; i<10000; i++) {
      do something
  }

Pętla z Javą.lang.Integer zamiast? Java.lang.Liczba całkowita jest niezmienna, więc każda inkrementacja wokół pętli spowoduje utworzenie nowego obiektu java na stercie, a nie tylko zwiększenie liczby int na stosie za pomocą pojedynczej instrukcji JVM. Przedstawienie byłoby diaboliczne.

Naprawdę nie zgadzam się, że korzystanie z Javy jest bardzo wygodne.lang.Liczba całkowita niż int. Wręcz przeciwnie. Autoboxing oznacza, że możesz użyć int tam, gdzie w przeciwnym razie być zmuszony do użycia Integer, a kompilator java zajmie się wstawieniem kodu, aby utworzyć nowy obiekt Integer dla Ciebie. Autoboxing polega na umożliwieniu użycia int, gdzie oczekuje się liczby całkowitej, z kompilatorem wstawiającym odpowiednią konstrukcję obiektu. To w żaden sposób nie usuwa lub zmniejsza potrzebę int w pierwszej kolejności. Dzięki autoboxing zyskujesz to, co najlepsze z obu światów. Otrzymasz liczbę całkowitą utworzoną dla ciebie automatycznie, gdy potrzebujesz obiektu java opartego na stercie, a otrzymasz szybkość i wydajność int, gdy robisz tylko obliczenia arytmetyczne i lokalne.

 27
Author: Tom Quarendon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-04 21:32:57

Prymitywne typy są dużo szybciej:

int i;
i++;

Integer (wszystkie liczby, a także Łańcuch znaków) jest typem niezmiennym : Po utworzeniu nie można go zmienić. Jeśli i będzie liczbą całkowitą, to i++ utworzy nowy obiekt Integer-znacznie droższy pod względem pamięci i procesora.

 18
Author: Peter Knego,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-04 21:24:10

Przede wszystkim przyzwyczajenie. Jeśli kodujesz w Javie przez osiem lat, gromadzisz znaczną ilość bezwładności. Po co zmieniać, skoro nie ma do tego przekonujących powodów? To nie jest tak, jakby używanie pudełkowych prymitywów miało jakieś dodatkowe zalety.

Innym powodem jest stwierdzenie, że null nie jest prawidłową opcją. Bezsensowne i mylące byłoby deklarowanie sumy dwóch liczb lub zmiennej pętli jako Integer.

Jest też aspekt wydajności, podczas gdy wydajność różnica nie jest krytyczna w wielu przypadkach (choć kiedy jest, jest bardzo źle), nikt nie lubi pisać kodu, który można by napisać tak łatwo, w szybszy sposób, do którego jesteśmy już przyzwyczajeni.

 16
Author: biziclop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-21 10:34:16

Nawiasem mówiąc, Smalltalk ma tylko obiekty (bez prymitywów), a mimo to zoptymalizowali swoje małe liczby całkowite (używając Nie wszystkich 32 bitów, tylko 27 lub podobnych), aby nie przydzielać żadnej przestrzeni sterty, ale po prostu użyć specjalnego wzorca bitowego. Również inne popularne obiekty (true, false, null) miały tutaj specjalne wzorce bitowe.

Tak więc, przynajmniej w 64-bitowym JVMs (z 64-bitową przestrzenią nazw wskaźnika) powinno być możliwe, aby w ogóle nie mieć żadnych obiektów o liczbie całkowitej, znakowej, bajtowej, krótkiej, logicznej, Float (i małej długości) (oprócz tych utworzonych przez jawne new ...()), tylko specjalne wzorce bitowe, które mogą być dość efektywnie manipulowane przez zwykłe operatory.

 12
Author: Paŭlo Ebermann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-04 22:10:32

Nie mogę uwierzyć, że nikt nie wspomniał o tym, co moim zdaniem jest najważniejszym powodem: "int" jest o wiele łatwiejsze do wpisania niż "Integer". Myślę, że ludzie nie doceniają znaczenia zwięzłej składni. Wydajność nie jest tak naprawdę powodem, aby ich unikać, ponieważ większość czasu, gdy ktoś używa liczb jest w indeksach pętli, a zwiększanie i porównywanie tych kosztów nic nie kosztuje w żadnej nietrywialnej pętli (czy używasz int lub Integer).

Innym podanym powodem było to, że można uzyskać NPEs ale jest to niezwykle łatwe do uniknięcia w przypadku typów pudełkowych (i gwarantowane jest unikanie tak długo, jak zawsze inicjalizujesz je do wartości innych niż null).

Innym powodem było to, że (new Long (1000))= = (new Long (1000)) jest fałszywe, ale to tylko kolejny sposób powiedzenia, że ".equals "nie ma wsparcia składniowego dla typów pudełkowych (w przeciwieństwie do operatorów ,=, itd.), więc wracamy do" prostszej składni".

Myślę, że nie-prymitywny przykład pętli Steve 'a Yegge' a ilustruje mój punkt widzenia bardzo dobrze.: http://sites.google.com/site/steveyegge2/language-trickery-and-ejb

Pomyśl o tym: jak często używasz typów funkcji w językach, które mają dla nich dobrą składnię (jak każdy język funkcyjny, python, ruby, a nawet C) w porównaniu do Javy, gdzie musisz symulować je za pomocą interfejsów takich jak Klasy Runnable, Callable i nameless.

 9
Author: CromTheDestroyer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-13 03:19:59

Kilka powodów, by nie pozbywać się prymitywów:

  • zwartość Wsteczna.

Jeśli zostanie wyeliminowany, żadne stare programy nawet nie będą działać.

  • JVM rewrite.

Cały JVM musiałby zostać przepisany, aby wspierać tę nową rzecz.

  • większy rozmiar pamięci.

Musisz zapisać wartość i odniesienie, które zużywa więcej pamięci. Jeśli masz ogromną tablicę bajtów, użycie byte ' S jest znacznie mniejsze niż za pomocą Byte ' s.

  • problemy ze wskaźnikiem zerowym.

Deklarowanie int i Następnie robienie rzeczy z i nie powodowałoby żadnych problemów, ale deklarowanie Integer i i robienie tego samego skutkowałoby NPE.

  • kwestie równości.

Rozważ ten kod:

Integer i1 = 5;
Integer i2 = 5;

i1 == i2; // Currently would be false.
To byłoby fałszywe. Operatorzy musieliby być przeciążeni, a to spowodowałoby poważne przepisanie rzeczy.
  • Slow

Owijarki obiektów są znacznie wolniejsze niż ich prymitywne odpowiedniki.

 8
Author: Anubian Noob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-26 14:33:32

Oprócz tego, co powiedzieli inni, prymitywne zmienne lokalne nie są przydzielane ze sterty, ale zamiast tego na stosie. Ale obiekty są przydzielane ze sterty i dlatego muszą być zbierane śmieci.

 7
Author: Eddie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-04 21:25:04

Obiekty są znacznie cięższe niż typy prymitywne, więc typy prymitywne są znacznie wydajniejsze niż instancje klas wrapperów.

Typy prymitywne są bardzo proste: na przykład int ma 32 bity i zajmuje dokładnie 32 bity w pamięci i może być manipulowany bezpośrednio. Obiekt Integer jest kompletnym obiektem, który (jak każdy obiekt) musi być przechowywany na stercie i może być dostępny tylko przez odniesienie (wskaźnik) do niego. Najprawdopodobniej zajmuje również więcej niż 32 bity (4 bajty) pamięci.

To powiedziawszy, fakt, że Java ma rozróżnienie między prymitywnymi i nie-prymitywnymi typami, jest również oznaką wieku języka programowania Java. Nowsze języki programowania nie mają tego rozróżnienia; kompilator takiego języka jest wystarczająco inteligentny, aby sam się zorientować, czy używasz prostych wartości lub bardziej złożonych obiektów.

Na przykład, w Scali nie ma prymitywnych typów; istnieje Klasa Int dla liczb całkowitych, A Int jest rzeczywistym obiektem (który można metody NA itp.). Kiedy kompilator kompiluje kod, używa prymitywnych ints za kulisami, więc używanie Int jest tak samo efektywne jak używanie prymitywnego int w Javie.

 6
Author: u449355,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-04 21:21:41

Trudno wiedzieć, jakie optymalizacje dzieją się pod przykrywką.

Do użytku lokalnego, gdy kompilator ma wystarczająco dużo informacji, aby dokonać optymalizacji wyłączając możliwość wartości null, oczekuję, że wydajność będzie taka sama lub podobna .

Jednak tablice naczelnych są najwyraźniej bardzo różne od zbiorów naczelnych pudełkowych. Ma to sens, biorąc pod uwagę, że bardzo niewiele optymalizacji jest możliwe głęboko w kolekcja.

Ponadto, Integer ma znacznie wyższy poziom logiczny {7]} w porównaniu z int: Teraz musisz się martwić o to, czy int a = b + c; rzuci wyjątek.

Użyłbym prymitywów jak najwięcej i polegałbym na metodach fabrycznych i autoboxingu, aby dać mi bardziej semantycznie potężne typy pudełkowe, gdy są potrzebne.

 5
Author: Matthew Willis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-04 21:34:05
int loops = 100000000;

long start = System.currentTimeMillis();
for (Long l = new Long(0); l<loops;l++) {
    //System.out.println("Long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around Long: "+ (System.currentTimeMillis()- start));

start = System.currentTimeMillis();
for (long l = 0; l<loops;l++) {
    //System.out.println("long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around long: "+ (System.currentTimeMillis()- start));

Milisekundy pobrane do pętli '100000000' razy wokół długości: 468

Milisekundy pobrane do pętli '100000000' razy wokół długości: 31

Na marginesie, nie miałbym nic przeciwko temu, żeby coś takiego znaleźć w Javie.
Integer loop1 = new Integer(0);
for (loop1.lessThan(1000)) {
   ...
}

Gdzie pętla for automatycznie zwiększa loop1 z 0 do 1000 lub

Integer loop1 = new Integer(1000);
for (loop1.greaterThan(0)) {
   ...
}

Gdzie pętla for automatycznie zmniejsza loop1 1000 do 0.

 5
Author: Keith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-03 02:04:10

Prymitywne typy mają wiele zalet:

  • prostszy kod do napisania
  • wydajność jest lepsza, ponieważ nie tworzysz instancji obiektu dla zmiennej
  • ponieważ nie reprezentują odniesienia do obiektu, nie ma potrzeby sprawdzania null
  • używaj prymitywnych typów, chyba że musisz skorzystać z funkcji boksu.
 5
Author: Adriana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-04 09:30:23

Zgadzam się z wcześniejszymi odpowiedziami, używanie primitives wrapper objects może być drogie. Jeśli jednak wydajność nie ma krytycznego znaczenia w aplikacji, można uniknąć przepełnień podczas korzystania z obiektów. Na przykład:

long bigNumber = Integer.MAX_VALUE + 2;

Wartość bigNumber wynosi -2147483647, i można oczekiwać, że będzie to 2147483649. Jest to błąd w kodzie, który można by naprawić wykonując:

long bigNumber = Integer.MAX_VALUE + 2l; // note that '2' is a long now.
2147483649 Tego rodzaju błędy są czasami łatwe do przeoczenia i mogą prowadzić do nieznanego zachowania lub luki (patrz CWE-190).

Jeśli używasz obiektów wrappera, równoważny kod nie zostanie skompilowany.

Long bigNumber = Integer.MAX_VALUE + 2; // Not compiling

Więc łatwiej jest powstrzymać tego rodzaju problemy za pomocą obiektów wrapper primitives.

Twoje pytanie jest tak już odpowiedział, że odpowiadam tylko, aby dodać trochę więcej informacji nie wymienione wcześniej.

 2
Author: Fernando Garcia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-03 13:25:00
  1. potrzebujesz prymitywów do wykonywania operacji matematycznych
  2. prymitywy zużywają mniej pamięci, tak jak powyżej i lepiej działają

Powinieneś zapytać dlaczego Typ klasy / obiektu jest wymagany

Powodem posiadania typu obiektu jest ułatwienie nam życia, gdy mamy do czynienia ze zbiorami. Primitives nie można dodawać bezpośrednio do listy / mapy, zamiast tego trzeba napisać klasę wrapper. Readymade Integer rodzaj klas pomaga tutaj plus ma wiele metod użytkowych, takich jak Liczba całkowita.pareseInt (str)

 1
Author: Prabakaran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-04 09:30:48

Ponieważ JAVA wykonuje wszystkie operacje matematyczne w prymitywnych typach. Rozważ ten przykład:

public static int sumEven(List<Integer> li) {
    int sum = 0;
    for (Integer i: li)
        if (i % 2 == 0)
            sum += i;
        return sum;
}

Tutaj operacje reminder i unary plus nie mogą być zastosowane na typie Integer(Reference), kompilator wykonuje unboxing i wykonuje operacje.

Więc upewnij się, ile operacji autoboxingu i unboxingu odbywa się w programie java. Ponieważ wykonanie tych operacji wymaga czasu.

Ogólnie lepiej zachować argumenty referencji typu I wyniku prymitywnego Typ.

 0
Author: user3462649,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-03 02:04:33