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

163

Od wersji Java 5 mamy boxing / unboxing typów pierwotnych, więc intjest to opakowane java.lang.Integer, i tak dalej i tak dalej.

Ostatnio widzę wiele nowych projektów Java (które zdecydowanie wymagają JRE co najmniej w wersji 5, jeśli nie 6), które używają intraczej niż java.lang.Integer, chociaż korzystanie z tego drugiego jest znacznie wygodniejsze, ponieważ ma kilka pomocniczych metod konwersji do longwartości et al.

Dlaczego niektórzy nadal używają typów pierwotnych w Javie? Czy są jakieś wymierne korzyści?

Naftuli Kay
źródło
49
kiedykolwiek myślałeś o zużyciu pamięci i wydajności?
Tedil
76
Dodałem tag autoboxu ... i dowiedziałem się, że podążają za nim trzy osoby. Naprawdę? Czy ludzie podążają za tagiem AUTOBOXING?
corsiKa
4
@glowcoder To nie są prawdziwi ludzie, to tylko abstrakcyjne koncepcje, które przyjmują ludzką postać, aby odpowiedzieć na SO. :)
biziclop
9
@TK Kocheran Głównie dlatego, że new IntegeR(5) == new Integer(5)zgodnie z zasadami należy oceniać jako fałsz.
biziclop
10
Zobacz kolekcje GNU Trove, Mahout, HPPC lub ... po rozwiązania dla kolekcji typów pierwotnych. Ci z nas, którym zależy na szybkości, spędzają czas na bardziej prymitywnych typach, a nie mniej.
bmargulies

Odpowiedzi:

395

W Efektywnej Javie Joshua Blocha , 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);
}

i trwa 43 sekundy. Biorąc Long na prymityw, sprowadzamy go do 6,8 sekundy ... Jeśli to jakaś wskazówka, dlaczego używamy prymitywów.

Problemem jest również brak rodzimej równości wartości ( .equals()jest dość rozwlekły w porównaniu z== )

dla biziclop:

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));
    }
}

Prowadzi do:

false
false
true
false

EDYTUJ Dlaczego (3) zwraca truei (4) zwracafalse ?

Ponieważ są to dwa różne obiekty. 256 liczb całkowitych najbliżej zera [-128; 127] są buforowane przez maszynę JVM, więc zwracają dla nich ten sam obiekt. Jednak poza tym zakresem nie są buforowane, więc tworzony jest nowy obiekt. Aby jeszcze bardziej skomplikować sprawę, JLS wymaga buforowania co najmniej 256 balastów. Osoby wdrażające JVM mogą dodać więcej, jeśli chcą, co oznacza, że ​​może to działać w systemie, w którym najbliższe 1024 są buforowane i wszystkie zwracają true ... #awkward

corsiKa
źródło
54
Teraz wyobraź sobie, że również izostały ogłoszone Long!
ColinD,
14
@TREE - specyfikacja faktycznie wymaga, aby maszyny wirtualne tworzyły wagi przelotowe w określonym zakresie. Ale niestety pozwala im to rozszerzyć ten zakres, co oznacza, że ​​programy mogą zachowywać się inaczej na różnych maszynach wirtualnych. To tyle jeśli chodzi o cross-platform ...
Daniel Earwicker,
12
Java poszła w błoto, oferując coraz więcej złych wyborów projektowych. Autoboxing to całkowita awaria, nie jest ani solidna, ani przewidywalna, ani przenośna. Naprawdę zastanawiam się, o czym myśleli ... zamiast naprawiać przerażającą dualność prymitywów i przedmiotów, udało im się to pogorszyć niż na początku.
Pop Catalin
34
@Catalin Nie zgadzam się z Tobą, że autoboxing to całkowita porażka. Ma pewne wady, które nie różnią się od innych projektów, które mogłyby zostać użyte (w tym nic). Bardzo jasno wyjaśniają, czego możesz i czego nie możesz się spodziewać, i jak każdy inny projekt oczekują, że programiści znają i przestrzegają umów tych projektów.
corsiKa
9
@NaftuliTzviKay To nie jest „awaria”. Dzięki nim jest BARDZO WYRAŹNY, że ==operator przeprowadza porównania tożsamości referencyjnej na Integerwyrażeniach i porównania równości wartości na intwyrażeniach. Integer.equals()istnieje właśnie z tego powodu. Nigdy nie należy używać ==do porównywania wartości w żadnym innym typie niż pierwotny. To jest Java 101.
NullUserException
86

Autounboxing może prowadzić do trudnych do wykrycia NPE

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

W większości sytuacji zerowe przypisanie do injest dużo mniej oczywiste niż powyżej.

Heiko Rupp
źródło
43

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

Orbita
źródło
40

Typy pierwotne:

int x = 1000;
int y = 1000;

Teraz oceń:

x == y

To jest true. Nic dziwnego. Teraz wypróbuj wersje pudełkowe:

Integer x = 1000;
Integer y = 1000;

Teraz oceń:

x == y

To jest false. Prawdopodobnie. Zależy od środowiska wykonawczego. Czy to wystarczający powód?

Daniel Earwicker
źródło
36

Oprócz problemów z wydajnością i pamięcią chciałbym wymyślić inny problem: Listinterfejs byłby zepsuty bez int.
Problemem jest remove()metoda przeciążona ( remove(int)vs. remove(Object)). remove(Integer)zawsze zdecydowałoby się wywołać to drugie, więc nie można usunąć elementu według indeksu.

Z drugiej strony istnieje 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!
xehpuk
źródło
7
Byłoby zepsute, tak. Jednak remove (int) jest wadą projektową IMO. Nazwy metod nigdy nie powinny być przeładowane, jeśli istnieje najmniejsza szansa na pomylenie.
MrBackend
4
@MrBackend Wystarczająco uczciwe. Co ciekawe, Vectormiał removeElementAt(int)od samego początku. remove(int)został wprowadzony wraz ze strukturą kolekcji w Javie 1.2.
xehpuk
6
@MrBackend: kiedy Listprojektowano API, nie istniały Generics ani Autoboxing, więc nie było szansy na pomieszanie remove(int)i remove(Object)
Holger
@Franklin Yu: jasne, ale projektując nowy język / wersję bez ograniczeń zgodności, nie przestawałbyś zmieniać tego niefortunnego przeciążenia. Po prostu pozbędziesz się całkowicie rozróżnienia między wartościami pierwotnymi i pudełkowymi, tak aby pytanie, którego użyć, nigdy się nie pojawi.
Holger
27

Czy naprawdę możesz sobie wyobrazić

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

pętla z java.lang.Integer zamiast tego? Element java.lang.Integer jest niezmienny, więc każdy inkrementacja wokół pętli utworzyłby nowy obiekt java na stercie, zamiast po prostu zwiększyć wartość int na stosie za pomocą pojedynczej instrukcji JVM. Przedstawienie byłoby diaboliczne.

Naprawdę nie zgodziłbym się, że korzystanie z trybu java.lang.Integer jest dużo wygodniejsze niż int. Przeciwnie. Autoboxing oznacza, że ​​możesz użyć int, gdzie w innym przypadku byłbyś zmuszony do użycia Integer, a kompilator java zajmie się wstawieniem kodu, aby utworzyć nowy obiekt Integer za Ciebie. Autoboxing polega na umożliwieniu używania int, w przypadku gdy oczekiwana jest liczba całkowita, z kompilatorem wstawiającym odpowiednią konstrukcję obiektu. W żaden sposób nie usuwa ani nie zmniejsza potrzeby int w pierwszej kolejności. Dzięki autoboxowi uzyskujesz to, co najlepsze z obu światów. Otrzymujesz liczbę całkowitą utworzoną automatycznie, gdy potrzebujesz obiektu Java opartego na stercie, a szybkość i wydajność int, gdy wykonujesz tylko obliczenia arytmetyczne i lokalne.

Tom Quarendon
źródło
19

Typy prymitywne są znacznie szybsze:

int i;
i++;

Integer (wszystkie liczby, a także String) jest typem niezmiennym : raz utworzony nie można go zmienić. Gdyby ibyło Integer, i++to stworzyłoby nowy obiekt Integer - znacznie droższy pod względem pamięci i procesora.

Peter Knego
źródło
Nie chcesz, aby jedna zmienna uległa zmianie, jeśli zrobisz to i++na innej zmiennej, więc Integer musi być niezmienny, aby móc to zrobić (lub przynajmniej i++musiałoby to utworzyć nowy obiekt Integer). (A prymitywne wartości też są niezmienne - po prostu nie zauważaj tego, ponieważ nie są one obiektami.)
Paŭlo Ebermann
4
@ Paŭlo: Mówienie, że wartości pierwotne są niezmienne, jest trochę bez znaczenia. Kiedy ponownie przypisujesz zmienną pierwotną do nowej wartości, nie tworzysz niczego nowego. Brak alokacji pamięci. Punkt widzenia Petera jest następujący: i ++ dla prymitywu nie ma alokacji pamięci, ale dla obiektu tak jest.
Eddie
@Eddie: (To niekoniecznie wymaga alokacji pamięci, może również zwrócić wartość z pamięci podręcznej. Myślę, że w przypadku niektórych małych wartości tak.) Chodziło mi o to, że niezmienność liczb całkowitych nie jest tutaj decydującym punktem, i tak chciałbyś mieć inny przedmiot, niezależnie od niezmienności.
Paŭlo Ebermann
@ Paŭlo: moim jedynym celem było to, że liczba całkowita jest o rząd wielkości wolniejsza niż prymitywy. Wynika to z faktu, że typy pudełkowe są niezmienne i za każdym razem, gdy zmieniasz wartość, tworzony jest nowy obiekt. Nie twierdziłem, że coś jest z nimi nie tak, ani że są niezmienne. Tylko tyle, że są wolniejsze i że programista powinien to wiedzieć. Zobacz, jak radzi
Peter Knego
1
Niezmienność i ++jest tu czerwonym śledziem. Wyobraź sobie, że Java została ulepszona, aby obsługiwać przeciążanie operatorów w naprawdę prosty sposób, tak że jeśli klasa (na przykład Integerma metodę plus, to i + 1zamiast tego można pisać i.plus(1).) Załóżmy również, że kompilator jest wystarczająco inteligentny, aby rozszerzyć się i++na i = i + 1. Teraz można powiedzieć i++i efektywnie „zwiększaj zmienną i” bez Integerjej
mutowalności
16

Przede wszystkim nawyk. Jeśli programujesz w Javie przez osiem lat, gromadzisz znaczną ilość bezwładności. Po co zmieniać, jeśli nie ma ku temu ważnego powodu? To nie jest tak, że używanie pudełkowych prymitywów ma jakieś dodatkowe zalety.

Innym powodem jest stwierdzenie, że nullnie jest to prawidłowa opcja. Byłoby bezcelowe i mylące zadeklarowanie sumy dwóch liczb lub zmiennej pętli jako Integer.

Jest to również aspekt wydajnościowy, podczas gdy różnica w wydajności nie jest krytyczna w wielu przypadkach (chociaż kiedy jest, jest dość zła), nikt nie lubi pisać kodu, który można by napisać równie łatwo i szybciej, my już przyzwyczajony.

biziclop
źródło
15
Nie zgadzam się. Aspekt wydajności może być krytyczny. Bardzo niewiele z tego jest prawdopodobnie bezwładnością lub siłą przyzwyczajenia.
Eddie,
7
@Eddie Może, ale bardzo rzadko. Zaufaj mi, dla większości ludzi argumenty dotyczące wyników to tylko wymówka.
biziclop
3
Ja też chciałbym chronić argument dotyczący wydajności. W systemie Android z Dalvik każdy utworzony obiekt zwiększa „ryzyko” wywołania GC, a im więcej obiektów masz, przerwy będą dłuższe. Zatem tworzenie liczb całkowitych zamiast int w pętli prawdopodobnie będzie kosztować kilka utraconych klatek.
Igor Čordaš
1
@PSIXO To słuszna uwaga, napisałem to z myślą wyłącznie o Javie po stronie serwera. Urządzenia mobilne to zupełnie inne zwierzę. Chodziło mi jednak o to, że nawet programiści, którzy w innym przypadku piszą okropny kod bez względu na wydajność, podadzą to jako powód, co dla nich brzmi jak wymówka.
biziclop
12

Nawiasem mówiąc, Smalltalk ma tylko obiekty (bez prymitywów), a mimo to zoptymalizował 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żywali specjalnego wzorca bitowego. Również inne typowe obiekty (prawda, fałsz, null) miały tutaj specjalne wzorce bitowe.

Tak więc przynajmniej na 64-bitowych maszynach JVM (z 64-bitową przestrzenią nazw wskaźników) powinno być możliwe, aby w ogóle nie było żadnych obiektów typu Integer, Character, Byte, Short, Boolean, Float (i small Long) (poza tymi utworzonymi przez wyraźne new ...()), tylko specjalne wzorce bitowe, którymi normalni operatorzy mogliby dość skutecznie manipulować.

Paŭlo Ebermann
źródło
Powinienem był powiedzieć „niektóre implementacje”, ponieważ myślę, że nie jest to regulowane specyfikacjami językowymi. (I niestety nie mogę tu cytować żadnych źródeł, to tylko z tego, co gdzieś słyszałem.)
Paŭlo Ebermann
ŭlo, JIT już utrzymuje meta we wskaźniku; w tym wskaźnik może przechowywać informacje o GC lub Klass (optymalizacja klasy jest o wiele lepszym pomysłem niż optymalizacja liczb całkowitych, na których mniej mnie to obchodzi). Zmiana wskaźnika wymagałaby przesunięcia kodu / cmp / jnz (lub czegoś podobnego) przed każdym załadowaniem wskaźnika. Gałąź prawdopodobnie nie zostanie dobrze przewidziana przez sprzęt (ponieważ może to być zarówno typ wartości, jak i normalny obiekt) i doprowadziłoby to do spadku wydajności.
bestsss
3
Robiłem Smalltalk przez kilka lat. Optymalizacja wciąż była dość kosztowna, ponieważ dla każdej operacji na int trzeba było zdemaskować i ponownie je zastosować. Obecnie java dorównuje C podczas manipulowania liczbami pierwotnymi. Z demaskowaniem + maską prawdopodobnie będzie> 30% wolniejsze.
R.Moeller
9

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

Drugim podanym powodem było to, że możesz uzyskać NPE, ale jest to niezwykle łatwe do uniknięcia w przypadku typów pudełkowych (i gwarantuje się unikanie tego, o ile zawsze inicjujesz je na wartości inne niż null).

Drugim powodem było to, że (new Long (1000)) == (new Long (1000)) jest fałszem, ale to tylko inny sposób na powiedzenie, że „.equals” nie obsługuje składni dla typów pudełkowych (w przeciwieństwie do operatorów <,> , = itd.), więc wracamy do powodu „prostszej składni”.

Myślę, że przykład nieprymitywnej pętli Steve'a Yegge'a bardzo dobrze ilustruje mój punkt widzenia: 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ą dobrą składnię (jak każdy język funkcjonalny, Python, Ruby, a nawet C) w porównaniu z Javą, gdzie musisz symulować je za pomocą interfejsów, takich jak Runnable i Callable oraz bezimienne klasy.

CromTheDestroyer
źródło
8

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

  • Kompatybilność wsteczna.

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

  • Przepisanie JVM.

Cała JVM musiałaby zostać przepisana, aby obsługiwała 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ż użycie Byte' s.

  • Problemy ze wskaźnikiem zerowym.

Zadeklarowanie, int ia następnie zrobienie rzeczy z inie spowodowałoby żadnych problemów, ale zadeklarowanie Integer ii zrobienie tego samego spowodowałoby NPE.

  • Kwestie równości.

Rozważ ten kod:

Integer i1 = 5;
Integer i2 = 5;

i1 == i2; // Currently would be false.

Byłoby fałszywe. Operatorzy musieliby być przeciążeni, a to spowodowałoby poważne przepisanie rzeczy.

  • Powolny

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

Anubian Noob
źródło
i1 == i2; byłoby fałszywe tylko wtedy, gdy i1> = 128. Tak więc, obecny przykład jest błędny
Geniy
7

Obiekty są znacznie cięższe niż typy pierwotne, więc typy pierwotne są znacznie wydajniejsze niż wystąpienia klas opakowujących.

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

To powiedziawszy, fakt, że Java rozróżnia typy prymitywne i nieprymitywne, 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 samodzielnie dowiedzieć się, czy używasz prostych wartości, czy bardziej złożonych obiektów.

Na przykład w Scali nie ma typów pierwotnych; istnieje klasa Int dla liczb całkowitych, a Int jest rzeczywistym obiektem (na którym można stosować metody itp.). Kiedy kompilator kompiluje twój kod, używa za kulisami pierwotnych int, więc używanie int jest tak samo wydajne, jak używanie prymitywnych int w Javie.

Umesh K.
źródło
1
Przypuszczałem, że środowisko JRE będzie wystarczająco „inteligentne”, aby zrobić to również z prymitywami opakowanymi w Javę. Zawieść.
Naftuli Kay
7

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

Eddie
źródło
3
Przepraszam, to źle. Inteligentna maszyna JVM może przeprowadzić analizę ucieczki dla dowolnej alokacji obiektów, a jeśli nie może uciec, przydzielić je na stosie.
rlibby
2
Tak, zaczyna to być cecha nowoczesnych maszyn JVM. Za pięć lat to, co powiesz, będzie prawdą w przypadku większości maszyn JVM, które są wówczas używane. Dziś tak nie jest. Prawie skomentowałem to, ale zdecydowałem się tego nie komentować. Może powinienem był coś powiedzieć.
Eddie,
6

Typy pierwotne mają wiele zalet:

  • Prostszy kod do napisania
  • Wydajność jest lepsza, ponieważ nie tworzysz instancji obiektu dla zmiennej
  • Ponieważ nie reprezentują one odniesienia do obiektu, nie ma potrzeby sprawdzania wartości null
  • Używaj typów prymitywnych, chyba że musisz skorzystać z funkcji boksu.
Adrianna
źródło
5

Trudno powiedzieć, jakie optymalizacje zachodzą pod osłonami.

Do użytku lokalnego, gdy kompilator ma wystarczającą ilość informacji, aby dokonać optymalizacji z wyłączeniem możliwości wartości null, spodziewam się, że wydajność będzie taka sama lub podobna .

Jednak tablice prymitywów są najwyraźniej bardzo różne od kolekcji prymitywów pudełkowych. Ma to sens, biorąc pod uwagę, że w kolekcji jest możliwych bardzo niewiele optymalizacji.

Ponadto Integerma znacznie wyższy koszt logiczny w porównaniu z int: teraz musisz się martwić o to, czy int a = b + c;zgłosi wyjątek.

Używałbym prymitywów tak często, jak to możliwe i polegałbym na metodach fabrycznych i autoboxingu, aby zapewnić mi silniejsze semantycznie typy pudełkowe, gdy są potrzebne.

Matthew Willis
źródło
5
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));

W milisekundach pętla „100000000” razy wokół pozycji Long: 468

Milisekundy potrzebne do zapętlenia „100000000” razy wokół długości: 31

Na marginesie, nie miałbym nic przeciwko, gdyby coś takiego znalazło drogę do Javy.

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

Gdzie pętla for automatycznie zwiększa wartość loop1 od 0 do 1000 lub

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

Gdzie pętla for automatycznie zmniejsza wartość loop1 z 1000 do 0.

Keith
źródło
2
  1. Do wykonywania operacji matematycznych potrzebne są prymitywy
  2. Prymitywy zajmują mniej pamięci, jak opisano powyżej, i mają lepszą wydajność

Powinieneś zapytać, dlaczego wymagany jest typ klasy / obiektu

Powodem posiadania typu Object jest ułatwienie nam życia, gdy mamy do czynienia z kolekcjami. Prymitywy nie mogą być dodawane bezpośrednio do listy / mapy, ale musisz napisać klasę opakowania. Gotowe klasy typu Integer pomagają w tym, a ponadto mają wiele metod użytkowych, takich jak Integer.pareseInt (str)

Prabakaran
źródło
2

Zgadzam się z poprzednimi odpowiedziami, używanie prymitywnych obiektów opakowujących może być kosztowne. Jeśli jednak wydajność nie jest krytyczna w Twojej aplikacji, unikniesz przepełnień podczas korzystania z obiektów. Na przykład:

long bigNumber = Integer.MAX_VALUE + 2;

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

long bigNumber = Integer.MAX_VALUE + 2l; // note that '2' is a long now (it is '2L').

I bigNumberbyłoby to 2147483649. Tego rodzaju błędy czasami można łatwo przeoczyć i mogą prowadzić do nieznanego zachowania lub luk w zabezpieczeniach (patrz CWE-190 ).

Jeśli używasz obiektów opakowujących, odpowiadający im kod nie zostanie skompilowany.

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

Dlatego łatwiej jest zatrzymać tego rodzaju problemy, używając pierwotnych obiektów opakowujących.

Twoje pytanie zostało już tak udzielone, że odpowiadam tylko po to, aby dodać trochę więcej informacji nie wymienionych wcześniej.

Fernando Garcia
źródło
1

Ponieważ JAVA wykonuje wszystkie operacje matematyczne w typach pierwotnych. Rozważmy 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;
}

W tym przypadku operacje przypominające i jednoargumentowe plus nie mogą być stosowane na typie Integer (Reference), kompilator wykonuje rozpakowywanie i wykonuje operacje.

Więc upewnij się, ile operacji autoboxingu i rozpakowywania ma miejsce w programie java. Ponieważ wykonanie tej operacji zajmuje trochę czasu.

Ogólnie lepiej jest zachować argumenty typu Reference i wynik typu pierwotnego.

user3462649
źródło
1

Te prymitywne typyznacznie szybciej i wymaga dużo mniej pamięci . Dlatego wolelibyśmy ich używać.

Z drugiej strony, aktualna specyfikacja języka Java nie pozwala na użycie typów pierwotnych w typach parametryzowanych (rodzajach), w kolekcjach Java lub w interfejsie API Reflection.

Gdy nasza aplikacja potrzebuje kolekcji z dużą liczbą elementów, powinniśmy rozważyć użycie tablic o jak najbardziej „ekonomicznym” typie.

* Aby uzyskać szczegółowe informacje, patrz źródło: https://www.baeldung.com/java-primitives-vs-objects

Arun Joshla
źródło
0

Krótko mówiąc: typy prymitywne są szybsze i wymagają mniej pamięci niż wersje pudełkowe

White Link
źródło