Java Language
Listy
Szukaj…
Wprowadzenie
Lista jest uporządkowanym zbiorem wartości. W Javie listy są częścią Java Collections Framework . Listy implementują interfejs java.util.List
, który rozszerza java.util.Collection
.
Składnia
- ls.add (element E); // Dodaje element
- ls.remove (element E); // Usuwa element
- for (element E: ls) {} // Iteruje po każdym elemencie
- ls.toArray (new String [ls.length]); // Konwertuje listę ciągów znaków na tablicę ciągów znaków
- ls.get (indeks wewnętrzny); // Zwraca element o określonym indeksie.
- ls.set (indeks wewnętrzny, element E); // Zastępuje element w określonej pozycji.
- ls.isEmpty (); // Zwraca true, jeśli tablica nie zawiera żadnych elementów, w przeciwnym razie zwraca false.
- ls.indexOf (Object o); // Zwraca indeks pierwszej lokalizacji określonego elementu o lub, jeśli nie jest obecny, zwraca -1.
- ls.lastIndexOf (Object o); // Zwraca indeks ostatniej lokalizacji określonego elementu o lub, jeśli nie jest obecny, zwraca -1.
- ls.size (); // Zwraca liczbę elementów na liście.
Uwagi
Lista to obiekt przechowujący uporządkowany zbiór wartości. „Zamówione” oznacza, że wartości są przechowywane w określonej kolejności - jeden element jest pierwszy, drugi jest drugi i tak dalej. Poszczególne wartości są powszechnie nazywane „elementami”. Listy Java zwykle zawierają następujące funkcje:
- Listy mogą zawierać zero lub więcej elementów.
- Listy mogą zawierać zduplikowane wartości. Innymi słowy, element można wstawić do listy więcej niż jeden raz.
- Listy przechowują swoje elementy w określonej kolejności, co oznacza, że jeden element jest na pierwszym miejscu, drugi na końcu i tak dalej.
- Każdy element ma indeks wskazujący jego pozycję na liście. Pierwszy element ma indeks 0, następny ma indeks 1 i tak dalej.
- Listy umożliwiają wstawianie elementów na początku, na końcu lub w dowolnym indeksie na liście.
- Testowanie, czy lista zawiera określoną wartość, ogólnie oznacza sprawdzenie każdego elementu na liście. Oznacza to, że czas wykonania tej kontroli wynosi O (n) , proporcjonalnie do wielkości listy.
Dodanie wartości do listy w innym punkcie niż koniec spowoduje przesunięcie wszystkich następujących elementów „w dół” lub „w prawo”. Innymi słowy, dodanie elementu o indeksie n przenosi element, który był wcześniej o indeksie n do indeksu n + 1 i tak dalej. Na przykład:
List<String> list = new ArrayList<>();
list.add("world");
System.out.println(list.indexOf("world")); // Prints "0"
// Inserting a new value at index 0 moves "world" to index 1
list.add(0, "Hello");
System.out.println(list.indexOf("world")); // Prints "1"
System.out.println(list.indexOf("Hello")); // Prints "0"
Sortowanie ogólnej listy
Klasa Collections
oferuje dwie standardowe metody statyczne do sortowania listy:
-
sort(List<T> list)
dotyczy list, na którychT extends Comparable<? super T>
i -
sort(List<T> list, Comparator<? super T> c)
dotyczy list dowolnego typu.
Zastosowanie tego pierwszego wymaga zmiany klasy sortowanych elementów listy, co nie zawsze jest możliwe. Może to być również niepożądane, ponieważ chociaż zapewnia domyślne sortowanie, inne porządki sortowania mogą być wymagane w różnych okolicznościach lub sortowanie jest jednorazowym zadaniem.
Rozważmy, że mamy zadanie sortowania obiektów, które są instancjami następującej klasy:
public class User {
public final Long id;
public final String username;
public User(Long id, String username) {
this.id = id;
this.username = username;
}
@Override
public String toString() {
return String.format("%s:%d", username, id);
}
}
Aby korzystać z Collections.sort(List<User> list)
, musimy zmodyfikować klasę User
aby wdrożyć interfejs Comparable
. Na przykład
public class User implements Comparable<User> {
public final Long id;
public final String username;
public User(Long id, String username) {
this.id = id;
this.username = username;
}
@Override
public String toString() {
return String.format("%s:%d", username, id);
}
@Override
/** The natural ordering for 'User' objects is by the 'id' field. */
public int compareTo(User o) {
return id.compareTo(o.id);
}
}
(Na marginesie: wiele standardowych klas Javy takie jak String
, Long
, Integer
wdrożyć Comparable
interfejs To sprawia, że wykazy tych elementów sortable domyślnie i upraszcza wdrażanie. compare
lub compareTo
w innych klasach).
Dzięki powyższej modyfikacji możemy łatwo sortować listę obiektów User
na podstawie naturalnego uporządkowania klas. (W tym przypadku zdefiniowaliśmy, że będzie to porządek oparty na wartościach id
). Na przykład:
List<User> users = Lists.newArrayList(
new User(33L, "A"),
new User(25L, "B"),
new User(28L, ""));
Collections.sort(users);
System.out.print(users);
// [B:25, C:28, A:33]
Załóżmy jednak, że chcieliśmy sortować obiekty User
według name
a nie id
. Ewentualnie załóżmy, że nie byliśmy w stanie zmienić klasy, aby uczynić ją implementacją Comparable
.
W tym przypadku przydatna jest metoda sort
z argumentem Comparator
:
Collections.sort(users, new Comparator<User>() {
@Override
/* Order two 'User' objects based on their names. */
public int compare(User left, User right) {
return left.username.compareTo(right.username);
}
});
System.out.print(users);
// [A:33, B:25, C:28]
W Javie 8 możesz użyć lambda zamiast anonimowej klasy. Ta ostatnia redukuje się do jednej linijki:
Collections.sort(users, (l, r) -> l.username.compareTo(r.username));
Ponadto, Java 8 dodaje domyślną metodę sort
w interfejsie List
, co jeszcze bardziej upraszcza sortowanie.
users.sort((l, r) -> l.username.compareTo(r.username))
Tworzenie listy
Podanie typu listy
Do utworzenia listy potrzebny jest typ (dowolna klasa, np. String
). To jest typ twojej List
. List
będzie przechowywać tylko obiekty określonego typu. Na przykład:
List<String> strings;
Może przechowywać "string1"
, "hello world!"
, "goodbye"
itp., ale nie można zapisać wersji 9.2
, jednak:
List<Double> doubles;
Może przechowywać 9.2
, ale nie "hello world!"
.
Inicjalizacja listy
Jeśli spróbujesz dodać coś do powyższych list, otrzymasz NullPointerException, ponieważ zarówno strings
i doubles
są równe null !
Istnieją dwa sposoby inicjowania listy:
Opcja 1: Użyj klasy, która implementuje listę
List
jest interfejsem, co oznacza, że nie ma konstruktora, a raczej metody, które klasa musi zastąpić. ArrayList
jest najczęściej używaną List
, choć LinkedList
jest również powszechna. Inicjujemy naszą listę w następujący sposób:
List<String> strings = new ArrayList<String>();
lub
List<String> strings = new LinkedList<String>();
Począwszy od Java SE 7, możesz użyć operatora diamentowego :
List<String> strings = new ArrayList<>();
lub
List<String> strings = new LinkedList<>();
Opcja 2: użyj klasy Kolekcje
Klasa Collections
zapewnia dwie przydatne metody tworzenia list bez zmiennej List
:
-
emptyList()
: zwraca pustą listę. -
singletonList(T)
: tworzy listę typu T i dodaje określony element.
I metoda wykorzystująca istniejącą List
do wypełnienia danych:
-
addAll(L, T...)
: dodaje wszystkie określone elementy do listy przekazanej jako pierwszy parametr.
Przykłady:
import java.util.List; import java.util.Collections; List<Integer> l = Collections.emptyList(); List<Integer> l1 = Collections.singletonList(42); Collections.addAll(l1, 1, 2, 3);
Pozycyjne operacje dostępu
Interfejs API listy ma osiem metod operacji dostępu pozycyjnego:
-
add(T type)
-
add(int index, T type)
-
remove(Object o)
-
remove(int index)
-
get(int index)
-
set(int index, E element)
-
int indexOf(Object o)
-
int lastIndexOf(Object o)
Więc jeśli mamy listę:
List<String> strings = new ArrayList<String>();
I chcieliśmy dodać ciąg „Hello world!” i „Do widzenia świat!” zrobilibyśmy to w ten sposób:
strings.add("Hello world!");
strings.add("Goodbye world!");
Nasza lista zawierałaby dwa elementy. Powiedzmy teraz, że chcieliśmy dodać „Uruchamianie programu!” na początku listy. Zrobilibyśmy to w ten sposób:
strings.add(0, "Program starting!");
UWAGA: Pierwszym elementem jest 0.
Teraz, jeśli chcielibyśmy usunąć „Świat do widzenia!” linia, możemy to zrobić w następujący sposób:
strings.remove("Goodbye world!");
A jeśli chcielibyśmy usunąć pierwszy wiersz (który w tym przypadku byłby „Program startujący!”, Moglibyśmy to zrobić w następujący sposób:
strings.remove(0);
Uwaga:
Dodanie i usunięcie elementów listy modyfikuje listę, a to może prowadzić do
ConcurrentModificationException
jeśli lista jest iterowana jednocześnie.Dodawanie i usuwanie elementów może być
O(1)
lubO(N)
zależności od klasy listy, zastosowanej metody oraz tego, czy dodajesz / usuwasz element na początku, na końcu czy na środku listy.
Aby pobrać element listy z określonej pozycji, możesz użyć E get(int index);
metoda interfejsu API listy. Na przykład:
strings.get(0);
zwróci pierwszy element listy.
Możesz zastąpić dowolny element w określonej pozycji za pomocą set(int index, E element);
. Na przykład:
strings.set(0,"This is a replacement");
Spowoduje to ustawienie ciągu „To zamiennik” jako pierwszego elementu listy.
Uwaga: Ustawiona metoda nadpisze element w pozycji 0. Nie doda nowego łańcucha w pozycji 0 i nie popchnie starego do pozycji 1.
The int indexOf(Object o);
zwraca pozycję pierwszego wystąpienia obiektu przekazanego jako argument. Jeśli na liście nie ma żadnych obiektów, zwracana jest wartość -1. Kontynuując poprzedni przykład, jeśli zadzwonisz:
strings.indexOf("This is a replacement")
Oczekuje się, że wartość 0 zostanie zwrócona, ponieważ ustawiamy ciąg „To jest zamiennik” na pozycji 0 naszej listy. W przypadku, gdy na liście występuje więcej niż jedno wystąpienie, gdy int indexOf(Object o);
nazywa się wtedy, jak wspomniano, indeks pierwszego wystąpienia zostanie zwrócony. Przez wywołanie int lastIndexOf(Object o)
możesz pobrać indeks ostatniego wystąpienia na liście. Więc jeśli dodamy kolejne „To jest zamiennik”:
strings.add("This is a replacement");
strings.lastIndexOf("This is a replacement");
Tym razem 1 zostanie zwrócone, a nie 0;
Iterowanie po elementach na liście
Na przykład załóżmy, że mamy Listę typu String, która zawiera cztery elementy: „hello”, „how”, „are”, „you?”
Najlepszym sposobem na iterację każdego elementu jest użycie pętli dla każdego:
public void printEachElement(List<String> list){
for(String s : list){
System.out.println(s);
}
}
Które wydrukuje:
hello,
how
are
you?
Aby wydrukować je wszystkie w tym samym wierszu, możesz użyć StringBuilder:
public void printAsLine(List<String> list){
StringBuilder builder = new StringBuilder();
for(String s : list){
builder.append(s);
}
System.out.println(builder.toString());
}
Wydrukuje:
hello, how are you?
Alternatywnie można użyć indeksowania elementów (zgodnie z opisem w temacie Uzyskiwanie dostępu do elementu i-tego indeksu z ArrayList ) w celu iteracji listy. Ostrzeżenie: takie podejście jest nieskuteczne w przypadku list połączonych.
Usuwanie elementów z listy B, które są obecne na liście A
Załóżmy, że masz 2 listy A i B i chcesz usunąć z B wszystkie elementy, które masz w A , w tym przypadku metodą jest
List.removeAll(Collection c);
#Przykład:
public static void main(String[] args) {
List<Integer> numbersA = new ArrayList<>();
List<Integer> numbersB = new ArrayList<>();
numbersA.addAll(Arrays.asList(new Integer[] { 1, 3, 4, 7, 5, 2 }));
numbersB.addAll(Arrays.asList(new Integer[] { 13, 32, 533, 3, 4, 2 }));
System.out.println("A: " + numbersA);
System.out.println("B: " + numbersB);
numbersB.removeAll(numbersA);
System.out.println("B cleared: " + numbersB);
}
to zostanie wydrukowane
Odp .: [1, 3, 4, 7, 5, 2]
B: [13, 32, 533, 3, 4, 2]
B rozliczone: [13, 32, 533]
Znajdowanie wspólnych elementów między 2 listami
Załóżmy, że masz dwie listy: A i B i musisz znaleźć elementy, które istnieją na obu listach.
Możesz to zrobić, po prostu wywołując metodę List.retainAll()
.
Przykład:
public static void main(String[] args) {
List<Integer> numbersA = new ArrayList<>();
List<Integer> numbersB = new ArrayList<>();
numbersA.addAll(Arrays.asList(new Integer[] { 1, 3, 4, 7, 5, 2 }));
numbersB.addAll(Arrays.asList(new Integer[] { 13, 32, 533, 3, 4, 2 }));
System.out.println("A: " + numbersA);
System.out.println("B: " + numbersB);
List<Integer> numbersC = new ArrayList<>();
numbersC.addAll(numbersA);
numbersC.retainAll(numbersB);
System.out.println("List A : " + numbersA);
System.out.println("List B : " + numbersB);
System.out.println("Common elements between A and B: " + numbersC);
}
Konwertuj listę liczb całkowitych na listę ciągów znaków
List<Integer> nums = Arrays.asList(1, 2, 3);
List<String> strings = nums.stream()
.map(Object::toString)
.collect(Collectors.toList());
To jest:
- Utwórz strumień z listy
- Odwzoruj każdy element za pomocą
Object::toString
- Zbierz wartości
String
doList
za pomocąCollectors.toList()
Tworzenie, dodawanie i usuwanie elementu z ArrayList
ArrayList
jest jedną z wbudowanych struktur danych w Javie. Jest to tablica dynamiczna (w której wielkość struktury danych nie musi być deklarowana jako pierwsza) do przechowywania elementów (obiektów).
Rozszerza klasę AbstractList
i implementuje interfejs List
. ArrayList
może zawierać zduplikowane elementy, w których zachowuje porządek wstawiania. Należy zauważyć, że klasa ArrayList
nie jest zsynchronizowana, dlatego należy zachować ostrożność podczas obsługi współbieżności z ArrayList
. ArrayList
umożliwia losowy dostęp, ponieważ tablica działa na podstawie indeksu. Manipulacja jest powolna w ArrayList
powodu przesunięcia, które często występuje, gdy element jest usuwany z listy tablic.
ArrayList
można utworzyć w następujący sposób:
List<T> myArrayList = new ArrayList<>();
Gdzie T
( Generics ) to typ, który będzie przechowywany w ArrayList
.
Typ ArrayList
może być dowolnym obiektem. Typ nie może być typem pierwotnym (zamiast tego użyj ich klas opakowań ).
Aby dodać element do ArrayList
, użyj metody add()
:
myArrayList.add(element);
Lub dodać element do określonego indeksu:
myArrayList.add(index, element); //index of the element should be an int (starting from 0)
Aby usunąć element z ArrayList
, użyj metody remove()
:
myArrayList.remove(element);
Lub usunąć element z określonego indeksu:
myArrayList.remove(index); //index of the element should be an int (starting from 0)
Zastąpienie w miejscu elementu List
Ten przykład dotyczy zastąpienia elementu List
, zapewniając jednocześnie, że element zastępujący znajduje się w tej samej pozycji co element, który jest zastępowany.
Można to zrobić za pomocą następujących metod:
- zestaw (indeks wewnętrzny, typ T)
- int indexOf (typ T)
Rozważ ArrayList
zawierającą elementy „Program start!”, „Hello world!” i „Do widzenia świat!”
List<String> strings = new ArrayList<String>();
strings.add("Program starting!");
strings.add("Hello world!");
strings.add("Goodbye world!");
Jeśli znamy indeks elementu, który chcemy zastąpić, możemy po prostu użyć set
w następujący sposób:
strings.set(1, "Hi world");
Jeśli nie znamy indeksu, możemy go najpierw wyszukać. Na przykład:
int pos = strings.indexOf("Goodbye world!");
if (pos >= 0) {
strings.set(pos, "Goodbye cruel world!");
}
Uwagi:
-
set
operacja nie spowodujeConcurrentModificationException
. - Operacja
set
jest szybka (O(1)
) dlaArrayList
ale powolna (O(N)
) dlaLinkedList
. - Wyszukiwanie
indexOf
naArrayList
lubLinkedList
jest powolne (O(N)
).
Uczynić listę niemodyfikowalną
Klasa Kolekcje zapewnia sposób, aby lista była niemodyfikowalna:
List<String> ls = new ArrayList<String>();
List<String> unmodifiableList = Collections.unmodifiableList(ls);
Jeśli chcesz mieć niemodyfikowalną listę z jednym przedmiotem, możesz użyć:
List<String> unmodifiableList = Collections.singletonList("Only string in the list");
Przenoszenie obiektów na liście
Klasa Kolekcje pozwala przenosić obiekty na liście przy użyciu różnych metod (ls to Lista):
Odwracanie listy:
Collections.reverse(ls);
Obracanie pozycji elementów na liście
Metoda obracania wymaga argumentu całkowitego. To o ile miejsc należy przesunąć go wzdłuż linii. Przykład tego znajduje się poniżej:
List<String> ls = new ArrayList<String>();
ls.add(" how");
ls.add(" are");
ls.add(" you?");
ls.add("hello,");
Collections.rotate(ls, 1);
for(String line : ls) System.out.print(line);
System.out.println();
Spowoduje to wydrukowanie „cześć, jak się masz?”
Tasowanie elementów na liście
Korzystając z tej samej listy powyżej, możemy tasować elementy na liście:
Collections.shuffle(ls);
Możemy również nadać mu obiekt java.util.Random, którego używa do losowego umieszczania obiektów w miejscach:
Random random = new Random(12);
Collections.shuffle(ls, random);
Klasy wdrażające listę - zalety i wady
Interfejs List
jest implementowany przez różne klasy. Każdy z nich ma swój własny sposób na wdrożenie go z różnymi strategiami i zapewnienie różnych zalet i wad.
Klasy wdrażające listę
Są to wszystkie klasy public
w Javie SE 8, które implementują interfejs java.util.List
:
- Klasy abstrakcyjne:
- AbstractList
- AbstractSequentialList
- Klasy betonowe:
- ArrayList
- AttributeList
- CopyOnWriteArrayList
- Połączona lista
- RoleList
- RoleUnresolvedList
- Stos
- Wektor
Plusy i minusy każdego wdrożenia pod względem złożoności czasowej
ArrayList
public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable
ArrayList jest implementacją tablicy List o zmiennym rozmiarze. Przechowując listę w tablicy, ArrayList udostępnia metody (oprócz metod implementujących interfejs List ) do manipulowania rozmiarem tablicy.
Zainicjuj ArrayList of Integer o rozmiarze 100
List<Integer> myList = new ArrayList<Integer>(100); // Constructs an empty list with the specified initial capacity.
- Wady:
Operacje size, isEmpty, get , set , iterator i listIterator działają w stałym czasie. Tak więc uzyskanie i ustawienie każdego elementu listy ma taki sam koszt czasu :
int e1 = myList.get(0); // \
int e2 = myList.get(10); // | => All the same constant cost => O(1)
myList.set(2,10); // /
- CONS:
Wdrożenie za pomocą tablicy (struktura statyczna) dodawania elementów ponad rozmiar tablicy ma duży koszt ze względu na fakt, że należy dokonać nowej alokacji dla całej tablicy. Jednak z dokumentacji :
Operacja dodawania działa w zamortyzowanym stałym czasie, to znaczy dodanie n elementów wymaga czasu O (n)
Usunięcie elementu wymaga czasu O (n).
AttributeList
Przyjedzie
CopyOnWriteArrayList
Przyjedzie
Połączona lista
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable
LinkedList jest implementowany przez podwójnie połączoną listę połączoną strukturę danych, która składa się z zestawu kolejno powiązanych rekordów zwanych węzłami.
Iitialize LinkedList of Integer
List<Integer> myList = new LinkedList<Integer>(); // Constructs an empty list.
- Wady:
Dodawanie lub usuwanie elementu na początku listy lub na końcu ma stały czas.
myList.add(10); // \
myList.add(0,2); // | => constant time => O(1)
myList.remove(); // /
- CONS: Z dokumentacji :
Operacje, które indeksują listę, będą przechodzić przez listę od początku lub na końcu, w zależności od tego, który jest bliżej określonego indeksu.
Operacje takie jak:
myList.get(10); // \
myList.add(11,25); // | => worst case done in O(n/2)
myList.set(15,35); // /
RoleList
Przyjedzie
RoleUnresolvedList
Przyjedzie
Stos
Przyjedzie
Wektor
Przyjedzie