Podczas pracy z bazami danych zauważyłem, że piszę ciągi zapytań i muszę w nich umieścić kilka ograniczeń w klauzuli where z listy / tablicy / kolekcji. Powinien wyglądać tak:
select * from customer
where customer.id in (34, 26, ..., 2);
Możesz to uprościć, redukując to do pytania, że masz kolekcję ciągów i chcesz utworzyć listę tych ciągów oddzielonych przecinkami w jednym ciągu.
Moje podejście, które stosowałem do tej pory, jest takie:
String result = "";
boolean first = true;
for(String string : collectionOfStrings) {
if(first) {
result+=string;
first=false;
} else {
result+=","+string;
}
}
Ale to jest, jak widać, bardzo brzydkie. Nie widać, co się tam dzieje na pierwszy rzut oka, zwłaszcza gdy konstruowane łańcuchy (jak każde zapytanie SQL) stają się skomplikowane.
Jaki jest Twój (bardziej) elegancki sposób?
Odpowiedzi:
Uwaga: te odpowiedzi były dobre, gdy zostały napisane 11 lat temu, ale teraz są znacznie lepsze opcje, aby zrobić to czysto w jednej linii, zarówno przy użyciu tylko wbudowanych klas Java, jak i biblioteki narzędziowej. Zobacz inne odpowiedzi poniżej.
Ponieważ ciągi są niezmienne, możesz chcieć użyć klasy StringBuilder, jeśli zamierzasz zmienić String w kodzie.
Klasę StringBuilder można postrzegać jako zmienny obiekt String, który przydziela więcej pamięci w przypadku zmiany zawartości.
Oryginalną sugestię w pytaniu można napisać jeszcze jaśniej i wydajniej, zwracając uwagę na zbędne końcowe przecinki :
źródło
Użyj Google guawy API „s
join
metody:źródło
Joiner
; google-collections.googlecode.com/svn/trunk/javadoc/com/google/…Właśnie spojrzałem na kod, który zrobił to dzisiaj. To jest wariacja na temat odpowiedzi AviewAnew.
W StringUtils (<- commons.lang 2.x lub commons.lang Link 3.x ) użyliśmy jest z Apache Commons .
źródło
Sposób, w jaki piszę tę pętlę, to:
Nie martw się o wydajność września. Zadanie jest bardzo szybkie. Hotspot i tak ma tendencję do zrywania pierwszej iteracji pętli (ponieważ często ma do czynienia z dziwactwami, takimi jak zerowe i mono / bimorficzne wbudowane kontrole).
Jeśli używasz go dużo (więcej niż raz), umieść go w wspólnej metodzie.
Jest jeszcze jedno pytanie dotyczące stackoverflow, dotyczące wstawiania listy identyfikatorów do instrukcji SQL.
źródło
Od wersji Java 8 możesz używać:
String String.join(CharSequence delimiter, CharSequence... elements)
String String.join(CharSequence delimiter, Iterable<? extends CharSequence> elements)
Jeśli chcesz wziąć non-
String
s i dołączyć je do aString
, możesz użyćCollectors.joining(CharSequence delimiter)
np:String joined = anyCollection.stream().map(Object::toString).collect(Collectors.joining(","));
źródło
cats.stream().map(cat -> cat.getName()).collect(Collectors.joining(","));
dla pojedynczej zmiennej z Twojej kolekcji.stream
. W przypadku tablic int [] lub long [] lub innych, do których można po prostu rzutować wartośćString
, szukałbym rozwiązania bez przesyłania strumieniowego. W rzeczywistości patrzę.Uważam, że idiom iteratora jest elegancki, ponieważ ma test dla większej liczby elementów (pominięty test zerowy / pusty ze względu na zwięzłość):
źródło
Jest wiele ręcznych rozwiązań tego problemu, ale chciałem powtórzyć i zaktualizować powyższą odpowiedź Julie. Użyj kolekcji Google Joiner class .
Obsługuje zmienne argumenty, iterowalne i tablice oraz poprawnie obsługuje separatory więcej niż jednego znaku (w przeciwieństwie do odpowiedzi gimmela). Jeśli zajdzie taka potrzeba, będzie również obsługiwać wartości null na liście.
źródło
Oto niesamowicie ogólna wersja, którą stworzyłem na podstawie kombinacji poprzednich sugestii:
źródło
dostępne w api Java8.
alternatywa dla (bez potrzeby dodawania zależności od google guava):
źródło
Możesz spróbować
źródło
Będzie to jak dotąd najkrótsze rozwiązanie, z wyjątkiem użycia Guava lub Apache Commons
Dobrze z listą elementów 0, 1 i n. Ale musisz sprawdzić listę zerową. Używam tego w GWT, więc jestem dobry bez StringBuildera. A w przypadku krótkich list zawierających tylko kilka elementów, gdzie indziej jest w porządku;)
źródło
Na wypadek, gdyby ktoś natknął się na to ostatnio, dodałem prostą odmianę za pomocą Java 8
reduce()
. Zawiera również niektóre z wymienionych już rozwiązań innych:źródło
W Androidzie powinieneś użyć tego:
źródło
Myślę, że nie jest dobrym pomysłem tworzenie sql konkatenacji wartości klauzuli where, tak jak robisz:
Skąd
valueX
pochodzi lista ciągów znaków.Po pierwsze, jeśli porównujesz ciągi znaków, muszą być cytowane, a to nie jest trywialne, jeśli ciągi mogą zawierać cudzysłów.
Po drugie, jeśli wartości pochodzą od użytkownika lub innego systemu, możliwy jest atak typu SQL injection.
Jest o wiele bardziej rozwlekły, ale powinieneś stworzyć taki łańcuch:
a następnie powiąż zmienne z
Statement.setString(nParameter,parameterValue)
.źródło
Kolejna metoda radzenia sobie z tym problemem. Nie jest to najkrótsze, ale jest wydajne i wykonuje swoją pracę.
źródło
Istnieją biblioteki Java innych firm, które zapewniają metodę łączenia ciągów, ale prawdopodobnie nie chcesz rozpoczynać korzystania z biblioteki tylko do czegoś takiego. Po prostu stworzyłbym pomocniczą metodę taką jak ta, która moim zdaniem jest nieco lepsza niż twoja wersja, używa StringBuffer, który będzie bardziej wydajny, jeśli będziesz musiał łączyć wiele ciągów, i działa na kolekcjach dowolnego typu.
Inna sugestia dotycząca używania Collection.toString () jest krótsza, ale polega na tym, że Collection.toString () zwraca ciąg w bardzo specyficznym formacie, na którym osobiście nie chciałbym polegać.
źródło
Jeśli używasz Spring, możesz:
(pakiet org.springframework.util)
źródło
Nie jestem pewien, jak to jest „wyrafinowane”, ale z pewnością jest trochę krótsze. Będzie działać z różnymi typami kolekcji, np. Set <Integer>, List <String> itp.
Ćwiczenie dla czytelnika : zmodyfikuj tę metodę, aby poprawnie obsługiwała zerową / pustą kolekcję :)
źródło
To, co sprawia, że kod jest brzydki, to specjalna obsługa pierwszego przypadku. Większość wierszy w tym małym fragmencie poświęcona jest nie rutynowej pracy kodu, ale obsłudze tego specjalnego przypadku. I to właśnie są alternatywy, takie jak rozwiązanie Gimela, poprzez przeniesienie specjalnej obsługi poza pętlę. Jest jeden przypadek specjalny (cóż, można zobaczyć zarówno początek, jak i koniec jako przypadki specjalne - ale tylko jeden z nich wymaga specjalnego traktowania), więc obsługa go wewnątrz pętli jest niepotrzebnie skomplikowana.
źródło
Właśnie zarejestrowałem test na dolara z biblioteki :
stworzy biegle otoki wokół lists / tablice / sznurki / etc przy użyciu tylko jednej statyczny import :
$
.Uwaga :
przy użyciu zakresów poprzednia lista może zostać ponownie zapisana jako
$(1, 5).join(",")
źródło
Zaletą wyrażenia IN jest to, że jeśli masz powtarzające się wartości, nie zmienia to wyniku. Więc po prostu zduplikuj pierwszy element i przetwórz całą listę. Zakłada się, że na liście znajduje się co najmniej jedna pozycja. Jeśli nie ma żadnych elementów, sugerowałbym najpierw sprawdzenie tego, a następnie całkowite powstrzymanie się od wykonywania kodu SQL.
To wystarczy, jest oczywiste w tym, co robi i nie polega na żadnych zewnętrznych bibliotekach:
źródło
Chociaż myślę, że najlepszym rozwiązaniem jest użycie Joinera z Guava, gdybym miał kodować go ręcznie, uważam, że to podejście jest bardziej eleganckie niż „pierwsza” flaga lub odcięcie ostatniego przecinka.
źródło
jeśli masz tablicę, możesz zrobić:
źródło
Inna opcja, na podstawie tego, co tu widzę (z niewielkimi modyfikacjami).
źródło
„Metody” łączenia są dostępne w tablicach i klasach, które rozszerzają metodę,
AbstractCollections
ale jej nie zastępujątoString()
(podobnie jak praktycznie wszystkie kolekcje wjava.util
).Na przykład:
To dość dziwny sposób, ponieważ działa tylko dla liczb podobnych do danych w języku SQL.
źródło
StringUtils z springframeowrk: sprężynowy rdzeń
źródło
Możesz być w stanie użyć LINQ (to SQL) i możesz być w stanie skorzystać z przykładu Dynamic Query LINQ z MS. http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
źródło
źródło
źródło
List token = new ArrayList (result); końcowy konstruktor StringBuilder = new StringBuilder ();
builder.toString ();
źródło