JavaRush /Blog Java /Random-PL /10 najważniejszych pytań dotyczących kolekcji w Javie
FedoraLinux
Poziom 21
Москва

10 najważniejszych pytań dotyczących kolekcji w Javie

Opublikowano w grupie Random-PL
Artykuł jest tłumaczeniem artykułu 10 najważniejszych pytań na temat kolekcji Java . Poniżej znajdują się najpopularniejsze pytania dotyczące kolekcji w Javie, zadawane i omawiane na Stackowerze. Zanim zajmiesz się tymi pytaniami, dobrze byłoby przyjrzeć się diagramowi hierarchii klas. 1. Kiedy używać LinkedList zamiast ArrayList? ArrayList jest w rzeczywistości tablicą; dostęp do jej elementów można uzyskać bezpośrednio za pomocą indeksu. Jeśli tablica się przepełni, konieczna będzie nowa, z większą ilością miejsca. Umieszczanie i przesuwanie wszystkich elementów zajmie czas O(n). Ponadto dodawanie i usuwanie elementów jest konieczne, aby przenieść istniejące elementy w szyku. Jest to chyba największa niedogodność korzystania z ArrayList. LinkedList to podwójna lista łączy elementów. Zatem, aby dostać się do elementu znajdującego się pośrodku, należy przeszukać arkusz od początku do końca. Z drugiej strony dodawanie i usuwanie elementu na liście LinkedList jest szybsze, ponieważ te operacje zmieniają jedynie samą listę. Poniżej porównano najgorsze czasy:
metoda Lista tablic Połączona lista
uzyskać (indeks) O(1) NA)
dodać (E) NA) O(1)
dodaj(E, indeks) NA) NA)
usuń (indeks) NA) NA)
Iterator.usuń() NA) O(1)
Iterator.add(E) NA) O(1)
Pomimo czasu działania, w przypadku dużych list zużycie pamięci należy rozpatrywać indywidualnie. W LinkedList każdy węzeł musi mieć co najmniej dwa dodatkowe wskaźniki, aby połączyć poprzedni i następny węzeł, podczas gdy w ArrayList potrzebna jest tylko tablica elementów. Więcej porównań list ArrayList, LinkedList i Vector . 2. Wydajny odpowiednik usuwania elementów podczas iteracji kolekcji Jedynym poprawnym sposobem modyfikowania (usuwania elementów) kolekcji podczas iteracji jest użycie Iterator.remove() . Na przykład: Najczęstszym błędem jest: Podczas uruchamiania powyższego kodu otrzymasz wyjątek ConcurrentModificationException . Dzieje się tak dlatego, że wygenerowano iterator do poruszania się po całej liście, ale jednocześnie następuje zmiana arkusza poprzez wywołanie metody Iterator.remove(). Jak napisano w dokumentacji tego wyjątku, Iterator itr = list.iterator(); while(itr.hasNext()) { // do something itr.remove(); } for(Integer i: list) { list.remove(i); }
„ogólnie nie jest dozwolone, aby jeden wątek modyfikował kolekcję, podczas gdy inny wątek iteruje po niej.”
Ogólnie rzecz biorąc, niedopuszczalne jest, aby jeden wątek modyfikował kolekcję, podczas gdy inny wątek przez nią przechodzi. 3. Jak przekonwertować listę na tablicę int[]? Najłatwiej to zrobić, używając narzędzia ArrayUtils , znajdującego się w bibliotece Apache Commons Lang . int[] array = ArrayUtils.toPrimitive(list.toArray(new Integer[0])); W JDK nie ma skrótu do tego wyrażenia. Pamiętaj, że nie możesz użyć List.toArray(), ponieważ to wyrażenie konwertuje List na Integer[] (która nie jest typem pierwotnym). Prawidłowym sposobem byłaby następująca opcja: int[] array = new int[list.size()]; for(int i=0; i < list.size(); i++) { array[i] = list.get(i); } 4. Jak przekonwertować tablicę int[] na listę? Najłatwiej jest także użyć ArrayUtils w bibliotece Apache Commons Lang , jak wyżej. List list = Arrays.asList(ArrayUtils.toObject(array)); Ponadto w JDK nie ma skrótu do tego wyrażenia. 5. Jaki jest najlepszy sposób filtrowania kolekcji? Aby zwiększyć funkcjonalność , możesz użyć pakietów stron trzecich, takich jak Guava lub Apache Commons Lang . Obydwa te pakiety posiadają metodę filter() (w klasie Collections2 z Guava i CollectionUtils z Apache). Metoda filter() zwróci elementy pasujące do podanego predykatu. W JDK wszystko jest bardziej skomplikowane. Dobra wiadomość jest taka, że ​​predykaty zostaną dodane w Javie 8 , ale na razie musisz używać Iteratora do iteracji po całej kolekcji. Można oczywiście naśladować ścieżkę, którą podążają Guava i Apache, zapoznając się z nowym interfejsem Predicate. Teraz możemy użyć poniższego kodu do filtrowania kolekcji: 6. Jak łatwo przekonwertować Listę na Set? Można to zrobić na dwa sposoby, w zależności od tego, jak chcesz zdefiniować równość. Pierwszy fragment kodu umieszcza listę w zestawie HashSet. Duplikat w tym przypadku jest określany głównie przez hashCode(). Zazwyczaj to zadziała. Jeśli jednak trzeba wziąć pod uwagę ścieżkę porównania, lepiej byłoby skorzystać z drugiej części kodu, w której możesz zdefiniować własny komparator. 7. Jak mogę usunąć zduplikowane elementy z listy ArrayList? To pytanie jest w pewnym stopniu powiązane z pytaniem powyżej. Jeśli kolejność elementów na ArrayList nie ma dla Ciebie znaczenia, mądrym posunięciem byłoby umieszczenie arkusza w zestawie w celu usunięcia duplikatów, a następnie zwrócenie go z powrotem na listę. Poniżej znajduje się przykład. Jeśli kolejność elementów ma dla Ciebie znaczenie, to kolejność można zapewnić umieszczając listę w LinkedHashSet , który znajduje się w standardowym JDK. 8. Posortowana kolekcja int[] array = {1,2,3,4,5}; List list = new ArrayList (); for(int i: array) { list.add(i); } Iterator itr = list.iterator(); while(itr.hasNext()) { int i = itr.next(); if (i > 5) { // filter all ints bigger than 5 itr.remove(); } } public interface Predicate { boolean test(T o); } public static void filter(Collection collection, Predicate predicate) { if ((collection != null) && (predicate != null)) { Iterator itr = collection.iterator(); while(itr.hasNext()) { T obj = itr.next(); if (!predicate.test(obj)) { itr.remove(); } } } } filter(list, new Predicate () { public boolean test(Integer i) { return i <= 5; } }); Set set = new HashSet (list); Set set = new TreeSet (aComparator); set.addAll(list); ArrayList** list = ... // initial a list with duplicate elements Set set = new HashSet (list); list.clear(); list.addAll(set); Istnieje kilka sposobów obsługi posortowanej kolekcji w Javie. Wszystkie zapewniają zbiór w porządku naturalnym lub według określonego komparatora. W przypadku porządku naturalnego należy także zaimplementować na elemencie interfejs Comparable .
  1. Kolekcje.sort() może sortować listę. Jak stwierdzono w dokumentacji Java, ten rodzaj jest stabilny i gwarantuje wydajność n log(n).
  2. PriorityQueue zapewnia uporządkowaną kolejkę. Różnica między PriorityQueue i Collections.sort() polega na tym, że PriorityQueue przez cały czas utrzymuje porządek w kolejce, ale można pobrać tylko pierwszy element kolejki. Nie można losowo uzyskać dostępu do elementu takiego jak PriorityQueue.get(4).
  3. Jeśli w kolekcji nie ma duplikatów, możesz wybrać TreeSet . Podobnie jak PriorityQueue, TreeSet przez cały czas utrzymuje uporządkowany zestaw. Możesz uzyskać najmniejszy lub największy element z TreeSet, ale nadal nie możesz mieć losowego dostępu do elementów.
Mówiąc najprościej, Collections.sort() udostępnia jednorazowo uporządkowaną listę. PriorityQueue i TreeSet przez cały czas utrzymują uporządkowaną kolekcję, kosztem braku indeksowanego dostępu do elementów. 9. Kolekcje.emptyList() lub nowa instancja To samo pytanie dotyczy metody pustyMap() i pustySet(). Obie metody zwracają pustą listę, ale Collections.emptyList() jest listą niezmienną. Oznacza to, że nie można dodawać nowych elementów do „pustej” listy. W tle każde wywołanie metody Collections.emptyList() w rzeczywistości nie tworzy nowej instancji pustej listy. Zamiast tego ponownie wykorzysta już istniejącą pustą instancję. Jeśli znasz Singleton jako wzorzec projektowy , powinieneś zrozumieć, o co chodzi. Powinno to zapewnić lepszą wydajność w przypadku częstego wywoływania. 10 Kopiowanie kolekcji, Collections.copy() Istnieją dwa sposoby kopiowania listy źródłowej na listę docelową. Jednym ze sposobów jest użycie konstruktora ArrayList. Innym sposobem jest użycie metody Collections.copy() . Zwróć uwagę na pierwszą linię: alokujemy listę, która jest co najmniej tak długa, jak długość oryginalnej listy, ponieważ dokumentacja Java dotycząca kolekcji mówi: ArrayList dstList = new ArrayList (srcList);
Lista docelowa musi być co najmniej tak długa, jak lista źródłowa.
Oznacza to, że ostateczna lista nie może być krótsza niż pierwotna. Obie metody polegają na płytkim kopiowaniu. Jaka jest więc różnica między tymi dwiema metodami? Po pierwsze, funkcja Collections.copy() nie spowoduje ponownego przydzielenia pojemności kolekcji dstList, nawet jeśli na dstList nie ma wystarczającej ilości miejsca, aby pomieścić wszystkie elementy z listy srcList. Zamiast tego wyrzuci IndexOutOfBoundsException . Ktoś mógłby zapytać, czy jest z tego jakaś korzyść. Powodem jest to, że zapewnia to liniowy przebieg metody w czasie. Jest to również przydatne, gdy chcesz ponownie wykorzystać tablice zamiast ponownie przydzielać pamięć w konstruktorze ArrayList. Zamiast podsumowania Jeśli po przeczytaniu artykułu nadal masz pytania, możesz je zadać w komentarzach. Ponadto, jeśli znajdziesz jakąś nieścisłość w tłumaczeniu lub inny błąd, napisz do PM, zostanie to poprawione i będziesz wdzięczny. Oryginalny. ArrayList dstList = new ArrayList (srcList.size()); Collections.copy(dstList, srcList);
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION