12-15__google_collections.pdf
(
595 KB
)
Pobierz
406260720 UNPDF
Biblioteka miesiąca
Google
Collections Library
Kolekcje to nieodłączny element skrzynki narzędziowej każdego
programisty. Jeśli programujesz w Javie i chciałbyś uprościć oraz
zoptymalizować Twój kod odpowiedzialny za obsługę kolekcji, to
trafiłeś na właściwy artykuł. Zapraszam do lektury!
Dowiesz się:
• Co oferuje biblioteka Google Collections Li-
brary i dlaczego warto jej używać.
• Jak można ją wykorzystać w praktyce.
Powinieneś wiedzieć:
• Jak programować w języku Java.
• Solidne podstawy biblioteki Java Collection
Framework.
wanie
) określa kontener podobny do
Map
(
mapo-
wanie
) z JCF, z tą jednak różnicą, że potrafi on
łączyć wiele wartości z jednym kluczem. Dla
przykładu, jeśli użytkownik biblioteki dwu-
krotnie wywoła metodę
put(K, V)
na konte-
nerze typu
Multimap
, przy czym klucz (
K
) za
każdym razem będzie taki sam, zaś wartości
(
V
) będą różne, to w rezultacie kontener bę-
dzie zawierał odwzorowania klucza
K
do oby-
dwu wartości. W przypadku kontenerów ty-
pu
Multimap
metoda
get()
zwraca kolekcję za-
wierającą wszystkie wartości powiązane z zada-
nym kluczem:
Poziom
trudności
Przegląd możliwości
Pisząc w największym możliwym skrócie, Go-
ogle Collection Library to zestaw nowych in-
terfejsów kolekcji, ich implementacji oraz klas
pomocniczych. GCL należy postrzegać jako
bibliotekę stanowiącą rozszerzenie Java Col-
lections Framework. Poniżej przedstawio-
na jest lista najbardziej kluczowych składni-
ków GCL:
W
dzisiejszych czasach, kiedy progra-
Collection<V> get(K key);
mowanie często polega na składa-
niu aplikacji z gotowych klocków,
a budowane programy są coraz bardziej i bar-
dziej złożone, zestaw podstawowych klas kolek-
cji jest elementem absolutnie podstawowym i
nieodzownym. Z tego względu kolekcje stały
się integralnym składnikiem bibliotek standar-
dowych nowoczesnych języków programowa-
nia. Java nie jest w tym przypadku wyjątkiem.
Standardowy pakiet Java Collection Frame-
work to solidny zestaw kontenerów ogólnego
użytku i pomimo tego, że został on zaprojekto-
wany i zaimplementowany kilkanaście lat temu
– ciągle jest z powodzeniem stosowany. Jednak-
że, użytkownicy JCF mogą od czasu do cza-
su odnieść wrażenie, że pewne operacje na ko-
lekcjach można by wykonywać nieco prościej,
szybciej i efektywnej. Do tego samego wnio-
sku doszli dwaj pracownicy firmy Google: Ke-
vin Bourrillion oraz Jared Levy. Co więcej, na
myśleniu się nie skończyło. W efekcie prac tych
dwóch panów powstała biblioteka Google Col-
lections Library, stanowiąca świetne uzupełnie-
nie i rozszerzenie pakietu Java Collection Fra-
mework. W niniejszym artykule przedstawię
pokrótce genezę tej biblioteki, opiszę jej pod-
stawowe składniki i pokażę jak można zastoso-
wać ją w praktyce.
• Nowe interfejsy kolekcji:
Multimap
,
Multiset
oraz
BiMap
.
• Wysoko wydajne, niemodyikowalne (ang.
immutable
) implementacje standardowych
klas kolekcji JCF, np.
ImmutableSet
.
• Użytkowe klasy
Lists
,
Sets
oraz
Maps
,
wspomagające pracę ze standardowymi
kontenerami biblioteki JCF.
• Użytkowe klasy
Iterators
i
Iterables
zawierające zestawy statycznych metod
operujących na interfejsach
Iterator
i
Iterable
.
• Klasa
Ordering
, czyli znacznie ułatwiająca
operacje sortowania obiektów według wie-
lu, potencjalnie złożonych kryteriów.
• Szereg dodatkowych udogodnień związa-
nych z obsługą kontenerów w języku Java.
GCL dostarcza cały szereg implementacji in-
terfejsu
Multimap
:
ArrayListMultimap
,
ForwardingMultimap
,
HashMultimap
,
ImmutableListMultimap
,
ImmutableMultimap
,
ImmutableSetMultimap
,
LinkedHashMultimap
,
LinkedListMultimap
,
TreeMultimap
.
Interfejs
Multiset
(
wielozbiór
) definiuję ko-
lekcję, która zachowuje się podobnie jak
Set
(
zbiór
) z JCF, aczkolwiek pozwala na dupli-
kację elementów. Powtarzające się elementy
wielozbioru, zwane są
wystąpieniami
zaś su-
maryczna ich liczba określana jest jako
licznik
wystąpień
. Kontener typu
Multiset
jest w sta-
nie przechować maksymalnie
Integer.MAX_
VALUE
wystąpień danego elementu. Interfejs
ten dostarcza oczywiście szereg odpowiednich
metod, np.
count()
zwracającą licznik wystą-
pień dla zadanego obiektu. GCL dostarcza na-
stępujące implementacje interfejsu
Multiset
:
ConcurrentHashMultiset
,
EnumMultiset
,
ForwardingMultiset
,
HashMultiset
,
ImmutableMultiset
,
LinkedHashMultiset
,
TreeMultiset
. Interfejs BiMap (
mapowanie
dwukierunkowe
) definiuje kolekcję-mapowa-
nie, w które gwarantuje unikalność zarówno
kluczy jak i wartości. Dzięki temu mapowa-
nie dwukierunkowe wspiera tzw. widok od-
W kolejnych podpunktach przyjrzymy się
składnikom GCL wymienionym w pierw-
szych pięciu punktach powyższej listy.
Multimap, Multiset oraz BiMap
Multimap
,
Multiset
i
BiMap
to nowe interfejsy
kolekcji wprowadzone w ramach Google Col-
lections Library. Interfejs
Multimap
(
wielomapo-
12
12/2009
Eleganckie i efektywne kolekcje w Javie
Google Collections Library
wrotny, w którym wartości mapowania stają
się kluczami zaś klucze – wartościami:
samego klucza.
Multiset
to idealna kolekcja do
budowania histogramów, więc wykorzystałem
ją w celu zliczania wystąpień znaków zawar-
tych w argumentach przekazanych z linii pole-
ceń.
BiMap
z kolei został użyty jako słownik, któ-
ry można
odpytywać
w dwie strony.
mutable
). Stałość obiektu to bardzo ważny idiom
programistyczny (bardzo ciekawa dyskusja na
ten temat przedstawiona jest w świetnej książce
p.t.
Effective Java, 2nd Edition
). JCF udostępnia
specjalne opakowania na kolekcje, które czynią je
niemodyfikowalnymi, aczkolwiek zarówno efek-
tywność jak i wygoda związana z ich używaniem
pozostawiają wiele do życzenia. GCL oferuje w
zmian specjalizowane, niemodyfikowalne kolek-
cje, które są znacznie prostsze w użyciu, szybsze
i zużywają mniej pamięci. Listing 2 przedstawia
typowy przykłady wykorzystania niemodyfiko-
walnego zbioru liczb całkowitych w stylu JCF.
Kontrprzykład opary na GCF mieści się w
dwóch linijkach kodu:
BiMap<V,K> inverse();
GCL dostarcza następujące implementacje
interfejsu
BiMap
:
EnumBiMap
,
EnumHashBiMap
,
HashBiMap
oraz
ImmutableBiMap
.
Na Listingu 1 przedstawione są przykłado-
we przypadki użycia przedstawionych powy-
żej trzech nowych typów kolekcji. W przypad-
ku wielomapowania warto zwrócić uwagę na
to, jak można dodawać różne wartości dla tego
Kolekcje niemodyfikowalne
Często zdarza się, że wiemy z góry, jaka będzie za-
wartość danego kontenera (np. tuż po jego stwo-
rzeniu wypełniamy go specyficznym obiektami)
i chcielibyśmy aby reszta naszego programu trak-
towała go jako byt niemodyfikowalny (ang.
im-
Listing 1.
Proste przypadki użycia kontenerów Multimap, Multiset i BiMap
import
com.google.common.collect.*
;
import
java.util.List
;
public
class
GclListing1
{
public
static
void
main
(
String
[]
args
)
{
Multimap
<
String
,
Integer
>
mmap
=
ArrayListMultimap
.
create
();
mmap
.
put
(
"odd"
,
1
);
mmap
.
put
(
"odd"
,
3
);
mmap
.
put
(
"odd"
,
5
);
mmap
.
put
(
"odd"
,
7
);
mmap
.
put
(
"even"
,
2
);
mmap
.
put
(
"even"
,
4
);
mmap
.
put
(
"even"
,
6
);
mmap
.
put
(
"even"
,
8
);
System
.
out
.
println
(
mmap
);
System
.
out
.
println
(
mmap
.
get
(
"even"
));
Multiset
<
Character
>
histogram
=
HashMultiset
.
create
();
for
(
String
arg
:
args
)
for
(
int
i
=
0
;
i
<
arg
.
length
(); ++
i
)
histogram
.
add
(
arg
.
charAt
(
i
));
System
.
out
.
println
(
histogram
);
BiMap
<
String
,
String
>
en2PlDict
=
HashBiMap
.
create
();
en2PlDict
.
put
(
"bird"
,
"ptak"
);
en2PlDict
.
put
(
"sky"
,
"niebo"
);
en2PlDict
.
put
(
"star"
,
"gwiazda"
);
en2PlDict
.
put
(
"bell"
,
"dzwon"
);
en2PlDict
.
put
(
"tree"
,
"drzewo"
);
en2PlDict
.
put
(
"gate"
,
"brama"
);
en2PlDict
.
put
(
"house"
,
"dom"
);
System
.
out
.
println
(
en2PlDict
);
System
.
out
.
println
(
en2PlDict
.
inverse
());
}
}
public static inal ImmutableSet<Integer>
MY_HAPPY_NUMBERS
= ImmutableSet.of(0, 1, 3, 7, 13, 44);
Komentarz porównujący te dwa fragmenty ko-
du wydaje się być zbyteczny... GCF udostępnia
cały szereg niemodyikowalnych kontenerów,
pełna ich lista jest dostępna w dokumentacji
API biblioteki (patrz: ramka
W sieci
).
Klasy użytkowe:
Lists, Sets i Maps
Trzy użytkowe klasy wymienione w tytule niniej-
szego podpunktu oferują zestawy statycznych
metod służących przede wszystkim do tworzenie
i manipulacji obiektami kolekcji typu
List
,
Set
i
Map
. Przeznaczenie tych klas najłatwiej będzie po-
kazać na praktycznym przykładzie. Spójrzmy za-
tem na Listing 3. W pierwszej linijce przedstawio-
nego Listingu konstruujemy nową listę obiektów
typu
String
. Warto w tym miejscu zauważyć jak
bardzo uproszczony jest ten proces dzięki zasto-
sowaniu metody
Lists.newArrayList(...)
.
W dalszej kolejności wykorzystujemy algorytm
Lists.transform(...)
w celu przekształcenia
zadanej listy zgodnie z przekazanym obiektem
funkcyjnym. W naszym przypadku przekształ-
camy listę napisów na listę odpowiadających im
wartości numerycznych. Miłym dodatkiem w
bibliotece GCL jest klasa
Joiner
, która pozwa-
la w łatwy sposób przekształcać zadany kontener
w pożądany napis. Po uruchomieniu programu
przedstawionego na Listingu 3 otrzymamy na-
stępujący wynik:
Listing 2.
Typowy przykład wykorzystania niemodyikowalnego zbioru liczb całkowitych w stylu JCF
public
static
inal
Set
<
Integer
>
MY_HAPPY_NUMBERS
;
static
{
Set
<
Integer
>
set
=
new
HashSet
<
Integer
>();
set
.
add
(
0
);
set
.
add
(
1
);
set
.
add
(
3
);
set
.
add
(
7
);
set
.
add
(
13
);
set
.
add
(
44
);
MY_HAPPY_NUMBERS
=
Collections
.
unmodiiableSet
(
set
);
}
3, 5, 7
W klasach
Lists
,
Sets
i
Maps
znaleźć można ca-
ły szereg operacji ułatwiających życie programi-
sty pracującego z kontenerami w Javie – zain-
teresowanych Czytelników odsyłam do doku-
mentacji API biblioteki (patrz: ramka
W sieci
).
Klasy użytkowe:
Iterators i Iterables
Na nieco podobnej zasadzie jak w przypad-
ku klas użytkowych opisanych w poprzednim
www.sdjournal.org
13
Biblioteka miesiąca
podpunkcie, klasy
Iterators
i
Iterables
z biblioteki GCL udostępniają szereg metod
– tym razem odpowiedzialnych za uprosz-
czenie procesu iteracji po kolekcjach elemen-
tów. Spójrzmy zatem na Listing 4, który za-
wiera próbkę możliwości zastosowania klasy
Iterables
.
Tym razem wykorzystujemy statycz-
ną metodę
filter()
, która filtruje zada-
ną kolekcję i zwraca drugą kolekcję, w któ-
rej wszystkie obiekty spełniają zadany pre-
dykat. Oprócz operacji filtrowania, klasa
Iterables
pozwala wykonywać łączenie ko-
lekcji (
concat
), wyszukiwanie elementów
w kolekcji (
find
), zliczanie elementów w ko-
lekcji (
frequency
) i wiele, wiele innych. Po-
dobnie działa klasa
Iterators
– tyle, że za-
miast obiektów typu
Iterable
przyjmuje
obiekty typu
Iterator
.
Klasa Ordering
Klasa
Ordering
jest jednym z ciekawszych roz-
wiązań zaaplikowanych w GCL. Pozwala ona
w łatwy i efektywny sposób łączyć różne kom-
paratory w celu wykonywaniu elastycznych po-
równań elementów przechowywanych w kon-
tenerach. Załóżmy, że mając prostą (a wręcz
podręcznikową) klasę
Person
(patrz: Listing 5)
chcielibyśmy mieć możliwość uszeregowania
obiektów tej klasy w kontekście poszczególnych
jej składowych pól. Zadanie to można bardzo ła-
two zrealizować przy pomocy GCL. Spójrzmy
na Listing 6.
W pierwszej kolejności tworzymy listę
obiektów klasy
Person
. Następnie konstruuje-
my instancje dwóch komparatorów: pierwszy
z nich porównuje obiekty według nazwiska zaś
drugi – według imienia danej osoby. Ostatnie
dwie linijki pokazują jak można wykorzystać
klasę
Ordering
. Szczególną uwagę proponuję
zwrócić na dwie ostatnie linijki tego przykła-
du, a w szczególności na fragment:
ordering = ordering.reverse().compound(irs
tNameComparator);
W tym miejscu tworzymy uszeregowanie
odwrotne do istniejącego i dodatkowo, przy
pomocy metody
compound
, dołączamy do
niego drugi komparator, który będzie brany
pod uwagę w sytuacji, gdy pierwszy kompa-
rator stwierdzi iż obiekty są identyczne.
Jako pożyteczne zadanie dla dociekliwych
Czytelników pozostawiam rozpisanie sobie na
kartce jak będzie wyglądać wyjście przedstawio-
nego programu, a następnie uruchomienie go
na komputerze i zweryfikowanie wyników.
Podsumowanie
W ramach podsumowania niniejszego artyku-
łu przedstawię główne powody, które (w mojej
opinii) sprawiają, że Google Collection Library
to rozwiązanie godne ze wszech miar uwagi:
Listing 3.
Przykład wykorzystania klasy Lists
import
com.google.common.base.Function
;
import
com.google.common.base.Joiner
;
import
com.google.common.collect.Lists
;
import
java.util.List
;
public
class
GclListing3
{
public
static
void
main
(
String
[]
args
)
{
List
<
String
>
strList
=
Lists
.
newArrayList
(
"3"
,
"5"
,
"7"
);
List
<
Integer
>
intList
=
Lists
.
transform
(
strList
,
new
Function
<
String
,
Integer
>() {
public
Integer
apply
(
String
from
) {
return
Integer
.
parseInt
(
from
);
}
});
System
.
out
.
println
(
Joiner
.
on
(
", "
).
join
(
intList
));
}
}
• Dojrzałość: w momencie pisania niniej-
szego tekstu GLC dostępna jest w wer-
sji 1.0 RC3 i z tego co obiecują autorzy
– na tym etapie interfejs biblioteki jest
już bardzo stabilny. Interfejs ten będzie
zamrożony w momencie gdy biblioteka
oznaczona będzie wersją 1.0.
• Stabilność i niezawodność: niewiele jest
na świecie miejsc, w których bibliote-
ka kontenerów możne być tak dobrze
przetestowana jak w Google; korzystając
z GCL mamy pewność, iż rozwiązanie
to zostało intensywnie zweryikowane
w najbardziej ekstremalnych warunkach
z możliwych (przetwarzanie olbrzymich
ilości danych w przeróżnych konigura-
cjach), a przy jego projektowaniu, budo-
waniu i optymalizowaniu brali udział
najlepsi specjaliści z branży.
• Spójność z JCF: w odróżnieniu od innych,
podobnych rozwiązań (np. Jakarta Com-
mons Collections) GLC zachowuje peł-
ną kompatybilność z Java Collection Fram-
work – stanowi niejako naturalne rozsze-
rzenie tej biblioteki i kontynuację ilozoii,
którą zapoczątkowali inżynierowie Sun'a.
W szczególności – obsługa typów uogól-
nionych (ang.
generics
) jest w GCL zaimple-
mentowana w identyczny sposób jak w JCF.
• Lekkość, niezależność i elastyczność: pacz-
ka z biblioteką waży około 500kB, przy
czym GCL jest rozwiązaniem niezależ-
nym (nie wymaga instalacji 15 innych bi-
bliotek od Google...) i elastycznym (łatwo
dopasować je do swoich potrzeb).
• Dokumentacja: jest pełna i profesjonal-
nie przygotowana. Gorąco polecam jej
przeczytanie – chociażby w celu zapo-
Listing 4.
Przykład wykorzystania klasy Iterables
import
com.google.common.base.Joiner
;
import
com.google.common.base.Predicate
;
import
com.google.common.collect.*
;
import
java.util.List
;
public
class
GclListing4
{
public
static
void
main
(
String
[]
args
) {
List
<
String
>
names
=
Lists
.
newArrayList
(
"Ola"
,
"Agnieszka"
,
"Monika"
,
null
);
Iterable
<
String
>
iltered
=
Iterables
.
ilter
(
names
,
new
Predicate
<
String
>() {
public
boolean
apply
(
String
input
) {
return
input
==
null
||
input
.
startsWith
(
"O"
);
}
});
System
.
out
.
println
(
Joiner
.
on
(
" and "
).
useForNull
(
"Rafal"
).
join
(
iltered
));
}
}
14
12/2009
Google Collections Library
znania się ze wszystkim komponentami
dostępnymi w ramach GCL.
• Licencja: Apache License 2.0, która daje
możliwość wykorzystania GCL w komer-
cyjnych projektach bez żadnych ograniczeń.
przez Google. W momencie pisania tego arty-
kułu Guava składa się z trzech pakietów:
dobrze współpracuje z Guava i równoległe ko-
rzystanie z tych dwóch rozwiązań daje bar-
dzo dobre efekty. Jednakże jest to temat na od-
dzielny artykuł, który w niedługim czasie Czy-
telnicy SDJ będą mogli przeczytać na łamach
kolumny Biblioteka Miesiąca. Na ten moment
zachęcam do eksperymentów z Google Collec-
tion Library i dziękuję za czas poświęcony na
przeczytanie powyższego tekstu.
•
com.google.common.primitives
•
com.google.common.io
•
com.google.common.util.concurrent
Co więcej – według zapowiedzi autorów, Go-
ogle Collection Library stanie się w niedługim
czasie częścią bibliteki Guava – łączącej w so-
bie cały szereg użytecznych bibliotek i kom-
ponentów ogólnego użytku wypracowanych
Google Collection Library ma zostać dołączo-
na do tego grona w momencie uzyskania statu-
su pełnej wersji 1.0 i zamrożenia API. Na ten
moment wiadomo już jednak, że GCF bardzo
RAFAŁ KOCISZ
Pracuje na stanowisku Dyrektora Technicznego
w irmie Gamelion, wchodzącej w skład Grupy
BLStream. Rafał specjalizuje się w technologiach
związanych z produkcją oprogramowania na plat-
formy mobilne, ze szczególnym naciskiem na two-
rzenie gier. Grupa BLStream powstała, by efektyw-
niej wykorzystywać potencjał dwóch szybko roz-
wijających się producentów oprogramowania –
BLStream i Gamelion. Firmy wchodzące w skład
grupy specjalizują się w wytwarzaniu oprogramo-
wania dla klientów korporacyjnych, w rozwiąza-
niach mobilnych oraz produkcji i testowaniu gier.
Kontakt z autorem: rafal.kocisz@game-lion.com
W Sieci
•
http://code.google.com/p/google-collections/
– strona domowa Google Collection Library
•
http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?http://google-collectio
ns.googlecode.com/svn/trunk/javadoc/com/google/common/collect/package-summary.html
–
dokumentacja API biblioteki GCL
•
http://www.javalobby.org/articles/google-collections/
– wywiad z autorami GCL przedstawiają-
cy genezę biblioteki
•
http://publicobject.com/2007/09/series-recap-coding-in-small-with.html
– szereg praktycznych
przykładów wykorzystania GCL
•
http://www.youtube.com/watch?v=ZeO_J2OcHYM
– video-prezentacja biblioteki (część pierwsza)
•
http://www.youtube.com/watch?v=9ni_KEkHfto
– video-prezentacja biblioteki (część druga)
Listing 5.
Implementacja przykładowej klasy Person
public
class
Person
{
private
String
irstName
;
private
String
lastName
;
public
Person
(
String
irstName
,
String
lastName
) {
this
.
setFirstName
(
irstName
);
this
.
setLastName
(
lastName
);
}
public
String
getFirstName
() {
return
irstName
;
}
public
void
setFirstName
(
String
irstName
) {
this
.
irstName
=
irstName
;
}
public
String
getLastName
() {
return
lastName
;
}
public
void
setLastName
(
String
lastName
) {
this
.
lastName
=
lastName
;
}
@Override
public
String
toString
() {
return
getFirstName
() +
" "
+
getLastName
();
}
}
Listing 6.
Przykład wykorzystania klasy Ordering
import
com.google.common.base.Joiner
;
import
com.google.common.base.Predicate
;
import
com.google.common.collect.*
;
import
java.util.Comparator
;
import
java.util.List
;
public
class
GclListing6
{
public
static
void
main
(
String
[]
args
) {
List
<
Person
>
persons
=
Lists
.
newArrayList
(
new
Person
(
"Jan"
,
"Kowalski"
),
new
Person
(
"Adam"
,
"Malinowski"
),
null
,
new
Person
(
"Tadeusz"
,
"Nowak"
),
null
,
new
Person
(
"Zenon"
,
"Kowalski"
));
Comparator
<
Person
>
lastNameComparator
=
new
Comparator
<
Person
>() {
public
int
compare
(
Person
p1
,
Person
p2
) {
return
p1
.
getLastName
().
compareTo
(
p2
.
getLas
tName
());
}
};
Comparator
<
Person
>
irstNameComparator
=
new
Comparator
<
Person
>() {
public
int
compare
(
Person
p1
,
Person
p2
) {
return
p1
.
getFirstName
().
compareTo
(
p2
.
getFi
rstName
());
}
};
Ordering
<
Person
>
ordering
=
Ordering
.
from
(
lastNameC
omparator
);
System
.
out
.
println
(
ordering
.
nullsLast
().
sortedCopy
(
persons
));
ordering
=
ordering
.
reverse
().
compound
(
irstNameCom
parator
);
System
.
out
.
println
(
ordering
.
nullsLast
().
sortedCopy
(
persons
));
}
}
www.sdjournal.org
15
Plik z chomika:
sqakwis
Inne pliki z tego folderu:
Software 2.0 02_2005.pdf
(12093 KB)
sdj-02-2010-PL.pdf
(15301 KB)
sdj_Extra_34_Biblia.pdf
(10774 KB)
sdj_12_2009_PL.pdf
(11848 KB)
sdj_07_2010_PL.pdf
(7777 KB)
Inne foldery tego chomika:
Cztery kopyta
Kurs rysunku MANGA
Linux
Newsweek
Next
Zgłoś jeśli
naruszono regulamin