Tôi cần nối hai String
mảng trong Java.
void f(String[] first, String[] second) {
String[] both = ???
}
cách dễ nhất để làm điều này là gì?
array1 + array2
ghép đơn giản .
Tôi cần nối hai String
mảng trong Java.
void f(String[] first, String[] second) {
String[] both = ???
}
cách dễ nhất để làm điều này là gì?
array1 + array2
ghép đơn giản .
Câu trả lời:
Tôi đã tìm thấy một giải pháp một dòng từ thư viện Apache Commons Lang cũ.
ArrayUtils.addAll(T[], T...)
Mã số:
String[] both = ArrayUtils.addAll(first, second);
Đây là một phương pháp đơn giản sẽ ghép hai mảng và trả về kết quả:
public <T> T[] concatenate(T[] a, T[] b) {
int aLen = a.length;
int bLen = b.length;
@SuppressWarnings("unchecked")
T[] c = (T[]) Array.newInstance(a.getClass().getComponentType(), aLen + bLen);
System.arraycopy(a, 0, c, 0, aLen);
System.arraycopy(b, 0, c, aLen, bLen);
return c;
}
Lưu ý rằng nó sẽ không hoạt động với các kiểu dữ liệu nguyên thủy, chỉ với các kiểu đối tượng.
Phiên bản phức tạp hơn một chút sau đây hoạt động với cả mảng đối tượng và mảng nguyên thủy. Nó thực hiện điều này bằng cách sử dụng T
thay vì T[]
làm kiểu đối số.
Nó cũng cho phép ghép các mảng của hai loại khác nhau bằng cách chọn loại chung nhất làm loại thành phần của kết quả.
public static <T> T concatenate(T a, T b) {
if (!a.getClass().isArray() || !b.getClass().isArray()) {
throw new IllegalArgumentException();
}
Class<?> resCompType;
Class<?> aCompType = a.getClass().getComponentType();
Class<?> bCompType = b.getClass().getComponentType();
if (aCompType.isAssignableFrom(bCompType)) {
resCompType = aCompType;
} else if (bCompType.isAssignableFrom(aCompType)) {
resCompType = bCompType;
} else {
throw new IllegalArgumentException();
}
int aLen = Array.getLength(a);
int bLen = Array.getLength(b);
@SuppressWarnings("unchecked")
T result = (T) Array.newInstance(resCompType, aLen + bLen);
System.arraycopy(a, 0, result, 0, aLen);
System.arraycopy(b, 0, result, aLen, bLen);
return result;
}
Đây là một ví dụ:
Assert.assertArrayEquals(new int[] { 1, 2, 3 }, concatenate(new int[] { 1, 2 }, new int[] { 3 }));
Assert.assertArrayEquals(new Number[] { 1, 2, 3f }, concatenate(new Integer[] { 1, 2 }, new Number[] { 3f }));
Array.newInstance(a.getClass().getComponentType(), aLen + bLen);
. Tôi đã ngạc nhiên chưa bao giờ thấy điều đó trước đây. @beaudet Tôi nghĩ rằng chú thích ở đây là tốt, xem xét lý do tại sao nó bị đàn áp.
Có thể viết một phiên bản hoàn toàn chung thậm chí có thể được mở rộng để nối bất kỳ số lượng mảng nào. Các phiên bản này yêu cầu Java 6, vì chúng sử dụngArrays.copyOf()
Cả hai phiên bản đều tránh tạo ra bất kỳ List
đối tượng trung gian nào và sử dụng System.arraycopy()
để đảm bảo rằng sao chép các mảng lớn càng nhanh càng tốt.
Đối với hai mảng, nó trông như thế này:
public static <T> T[] concat(T[] first, T[] second) {
T[] result = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, result, first.length, second.length);
return result;
}
Và đối với số lượng mảng tùy ý (> = 1), nó trông như thế này:
public static <T> T[] concatAll(T[] first, T[]... rest) {
int totalLength = first.length;
for (T[] array : rest) {
totalLength += array.length;
}
T[] result = Arrays.copyOf(first, totalLength);
int offset = first.length;
for (T[] array : rest) {
System.arraycopy(array, 0, result, offset, array.length);
offset += array.length;
}
return result;
}
T
bằng byte
(và mất <T>
).
ByteBuffer buffer = ByteBuffer.allocate(array1.length + array2.length); buffer.put(array1); buffer.put(array2); return buffer.array();
concat(ai, ad)
, nơi ai
là Integer[]
và ad
là Double[]
. (Trong trường hợp này, tham số loại <T>
được giải quyết <? extends Number>
bởi trình biên dịch.) Mảng được tạo bởi Arrays.copyOf
sẽ có kiểu thành phần của mảng đầu tiên, tức là Integer
trong ví dụ này. Khi chức năng sắp sao chép mảng thứ hai, một ArrayStoreException
sẽ được ném. Giải pháp là có thêm một Class<T> type
tham số.
Sử dụng Stream
trong Java 8:
String[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b))
.toArray(String[]::new);
Hoặc như thế này, sử dụng flatMap
:
String[] both = Stream.of(a, b).flatMap(Stream::of)
.toArray(String[]::new);
Để làm điều này cho một loại chung, bạn phải sử dụng sự phản chiếu:
@SuppressWarnings("unchecked")
T[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b)).toArray(
size -> (T[]) Array.newInstance(a.getClass().getComponentType(), size));
.boxed()
như vậy Stream
chứ không phải là ví dụ IntStream
không thể được truyền dưới dạng tham số Stream.concat
.
a
và b
đang int[]
, sử dụngint[] both = IntStream.concat(Arrays.stream(a), Arrays.stream(b)).toArray();
System.arrayCopy
. Nhưng cũng không đặc biệt chậm. Bạn có thể phải làm điều này rất nhiều lần với các mảng lớn trong các bối cảnh thực sự nhạy cảm về hiệu suất cho sự khác biệt về thời gian thực hiện.
Hoặc với quả ổi yêu quý :
String[] both = ObjectArrays.concat(first, second, String.class);
Ngoài ra, có các phiên bản cho mảng nguyên thủy:
Booleans.concat(first, second)
Bytes.concat(first, second)
Chars.concat(first, second)
Doubles.concat(first, second)
Shorts.concat(first, second)
Ints.concat(first, second)
Longs.concat(first, second)
Floats.concat(first, second)
Bạn có thể nối hai mảng trong hai dòng mã.
String[] both = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, both, first.length, second.length);
Đây là một giải pháp nhanh chóng và hiệu quả và sẽ hoạt động cho các loại nguyên thủy cũng như hai phương pháp liên quan bị quá tải.
Bạn nên tránh các giải pháp liên quan đến ArrayLists, luồng, v.v. vì những giải pháp này sẽ cần phân bổ bộ nhớ tạm thời cho mục đích hữu ích.
Bạn nên tránh for
các vòng lặp cho các mảng lớn vì chúng không hiệu quả. Các phương thức tích hợp sử dụng các hàm sao chép khối cực kỳ nhanh.
Sử dụng API Java:
String[] f(String[] first, String[] second) {
List<String> both = new ArrayList<String>(first.length + second.length);
Collections.addAll(both, first);
Collections.addAll(both, second);
return both.toArray(new String[both.size()]);
}
both.toArray(new String[0])
sẽ nhanh hơn both.toArray(new String[both.size()])
, ngay cả khi nó mâu thuẫn với trực giác ngây thơ của chúng ta. Đó là lý do tại sao nó rất quan trọng để đo hiệu suất thực tế khi tối ưu hóa. Hoặc chỉ sử dụng cấu trúc đơn giản hơn, khi không thể chứng minh được lợi thế của biến thể phức tạp hơn.
Một giải pháp java cũ 100% và không có System.arraycopy
(chẳng hạn trong ứng dụng khách GWT):
static String[] concat(String[]... arrays) {
int length = 0;
for (String[] array : arrays) {
length += array.length;
}
String[] result = new String[length];
int pos = 0;
for (String[] array : arrays) {
for (String element : array) {
result[pos] = element;
pos++;
}
}
return result;
}
null
séc. Và có lẽ đặt một số biến của bạn thành final
.
null
Kiểm tra @TrippKinetic sẽ ẩn NPE thay vì hiển thị chúng và sử dụng cuối cùng cho các vars cục bộ không có bất kỳ lợi ích nào (chưa).
Gần đây tôi đã chiến đấu với các vấn đề với vòng quay bộ nhớ quá mức. Nếu a và / hoặc b được biết là thường trống, thì đây là một sự thích ứng khác của mã silvertab (cũng được khái quát hóa):
private static <T> T[] concatOrReturnSame(T[] a, T[] b) {
final int alen = a.length;
final int blen = b.length;
if (alen == 0) {
return b;
}
if (blen == 0) {
return a;
}
final T[] result = (T[]) java.lang.reflect.Array.
newInstance(a.getClass().getComponentType(), alen + blen);
System.arraycopy(a, 0, result, 0, alen);
System.arraycopy(b, 0, result, alen, blen);
return result;
}
Chỉnh sửa: Một phiên bản trước của bài đăng này đã tuyên bố rằng việc sử dụng lại mảng như thế này sẽ được ghi lại rõ ràng. Như Maarten chỉ ra trong các ý kiến, nói chung sẽ tốt hơn nếu chỉ loại bỏ các câu lệnh if, do đó làm mất đi sự cần thiết phải có tài liệu. Nhưng sau đó, một lần nữa, những tuyên bố nếu là toàn bộ điểm tối ưu hóa đặc biệt này ở nơi đầu tiên. Tôi sẽ để câu trả lời này ở đây, nhưng hãy cảnh giác!
System.arraycopy
sao chép nội dung của mảng?
if
tuyên bố sẽ là cách khắc phục dễ dàng nhất.
Các Java chức năng thư viện có một lớp wrapper mảng trang bị cho mảng với các phương pháp tiện dụng như nối.
import static fj.data.Array.array;
...và sau đó
Array<String> both = array(first).append(array(second));
Để lấy lại mảng chưa mở, hãy gọi
String[] s = both.array();
ArrayList<String> both = new ArrayList(Arrays.asList(first));
both.addAll(Arrays.asList(second));
both.toArray(new String[0]);
both.toArray(new String[both.size()])
;)
Đây là bản phóng tác của giải pháp silvertab, với thuốc generic được trang bị thêm:
static <T> T[] concat(T[] a, T[] b) {
final int alen = a.length;
final int blen = b.length;
final T[] result = (T[]) java.lang.reflect.Array.
newInstance(a.getClass().getComponentType(), alen + blen);
System.arraycopy(a, 0, result, 0, alen);
System.arraycopy(b, 0, result, alen, blen);
return result;
}
LƯU Ý: Xem câu trả lời của Joachim cho giải pháp Java 6. Nó không chỉ loại bỏ cảnh báo; Nó cũng ngắn hơn, hiệu quả hơn và dễ đọc hơn!
Nếu bạn sử dụng cách này để bạn không cần phải nhập bất kỳ lớp bên thứ ba nào.
Nếu bạn muốn nối String
Mã mẫu để nối hai chuỗi Array
public static String[] combineString(String[] first, String[] second){
int length = first.length + second.length;
String[] result = new String[length];
System.arraycopy(first, 0, result, 0, first.length);
System.arraycopy(second, 0, result, first.length, second.length);
return result;
}
Nếu bạn muốn nối Int
Mã mẫu để nối hai mảng Integer
public static int[] combineInt(int[] a, int[] b){
int length = a.length + b.length;
int[] result = new int[length];
System.arraycopy(a, 0, result, 0, a.length);
System.arraycopy(b, 0, result, a.length, b.length);
return result;
}
Đây là phương pháp chính
public static void main(String[] args) {
String [] first = {"a", "b", "c"};
String [] second = {"d", "e"};
String [] joined = combineString(first, second);
System.out.println("concatenated String array : " + Arrays.toString(joined));
int[] array1 = {101,102,103,104};
int[] array2 = {105,106,107,108};
int[] concatenateInt = combineInt(array1, array2);
System.out.println("concatenated Int array : " + Arrays.toString(concatenateInt));
}
}
Chúng ta cũng có thể sử dụng cách này.
Xin vui lòng tha thứ cho tôi vì đã thêm một phiên bản khác vào danh sách đã dài này. Tôi đã xem xét mọi câu trả lời và quyết định rằng tôi thực sự muốn có một phiên bản chỉ có một tham số trong chữ ký. Tôi cũng đã thêm một số kiểm tra đối số để hưởng lợi từ thất bại sớm với thông tin hợp lý trong trường hợp đầu vào bất ngờ.
@SuppressWarnings("unchecked")
public static <T> T[] concat(T[]... inputArrays) {
if(inputArrays.length < 2) {
throw new IllegalArgumentException("inputArrays must contain at least 2 arrays");
}
for(int i = 0; i < inputArrays.length; i++) {
if(inputArrays[i] == null) {
throw new IllegalArgumentException("inputArrays[" + i + "] is null");
}
}
int totalLength = 0;
for(T[] array : inputArrays) {
totalLength += array.length;
}
T[] result = (T[]) Array.newInstance(inputArrays[0].getClass().getComponentType(), totalLength);
int offset = 0;
for(T[] array : inputArrays) {
System.arraycopy(array, 0, result, offset, array.length);
offset += array.length;
}
return result;
}
Bạn có thể thử chuyển đổi nó thành một Arraylist và sử dụng phương thức addAll sau đó chuyển đổi trở lại thành một mảng.
List list = new ArrayList(Arrays.asList(first));
list.addAll(Arrays.asList(second));
String[] both = list.toArray();
Đây là một triển khai có thể có trong mã làm việc của giải pháp mã giả được viết bởi silvertab.
Cảm ơn silvertab!
public class Array {
public static <T> T[] concat(T[] a, T[] b, ArrayBuilderI<T> builder) {
T[] c = builder.build(a.length + b.length);
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
}
Tiếp theo là giao diện xây dựng.
Lưu ý: Trình xây dựng là cần thiết vì trong java không thể thực hiện được
new T[size]
do loại chung chung xóa:
public interface ArrayBuilderI<T> {
public T[] build(int size);
}
Ở đây một người xây dựng cụ thể thực hiện giao diện, xây dựng một Integer
mảng:
public class IntegerArrayBuilder implements ArrayBuilderI<Integer> {
@Override
public Integer[] build(int size) {
return new Integer[size];
}
}
Và cuối cùng là ứng dụng / kiểm tra:
@Test
public class ArrayTest {
public void array_concatenation() {
Integer a[] = new Integer[]{0,1};
Integer b[] = new Integer[]{2,3};
Integer c[] = Array.concat(a, b, new IntegerArrayBuilder());
assertEquals(4, c.length);
assertEquals(0, (int)c[0]);
assertEquals(1, (int)c[1]);
assertEquals(2, (int)c[2]);
assertEquals(3, (int)c[3]);
}
}
Ồ rất nhiều câu trả lời phức tạp ở đây bao gồm một số câu trả lời đơn giản phụ thuộc vào sự phụ thuộc bên ngoài. làm thế nào để làm điều đó như thế này:
String [] arg1 = new String{"a","b","c"};
String [] arg2 = new String{"x","y","z"};
ArrayList<String> temp = new ArrayList<String>();
temp.addAll(Arrays.asList(arg1));
temp.addAll(Arrays.asList(arg2));
String [] concatedArgs = temp.toArray(new String[arg1.length+arg2.length]);
Điều này hoạt động, nhưng bạn cần chèn kiểm tra lỗi của riêng bạn.
public class StringConcatenate {
public static void main(String[] args){
// Create two arrays to concatenate and one array to hold both
String[] arr1 = new String[]{"s","t","r","i","n","g"};
String[] arr2 = new String[]{"s","t","r","i","n","g"};
String[] arrBoth = new String[arr1.length+arr2.length];
// Copy elements from first array into first part of new array
for(int i = 0; i < arr1.length; i++){
arrBoth[i] = arr1[i];
}
// Copy elements from second array into last part of new array
for(int j = arr1.length;j < arrBoth.length;j++){
arrBoth[j] = arr2[j-arr1.length];
}
// Print result
for(int k = 0; k < arrBoth.length; k++){
System.out.print(arrBoth[k]);
}
// Additional line to make your terminal look better at completion!
System.out.println();
}
}
Đây có thể không phải là hiệu quả nhất, nhưng nó không dựa vào bất cứ thứ gì ngoài API riêng của Java.
for
vòng lặp thứ hai bằng cách đó:for(int j = 0; j < arr2.length; j++){arrBoth[arr1.length+j] = arr2[j];}
String[] arrBoth = java.util.Arrays.copyOf(arr1, arr1.length + arr2.length)
để bỏ qua for
vòng lặp đầu tiên . Tiết kiệm thời gian tỷ lệ thuận với kích thước của arr1
.
Đây là một hàm được chuyển đổi cho một mảng String:
public String[] mergeArrays(String[] mainArray, String[] addArray) {
String[] finalArray = new String[mainArray.length + addArray.length];
System.arraycopy(mainArray, 0, finalArray, 0, mainArray.length);
System.arraycopy(addArray, 0, finalArray, mainArray.length, addArray.length);
return finalArray;
}
Đơn giản thế nào
public static class Array {
public static <T> T[] concat(T[]... arrays) {
ArrayList<T> al = new ArrayList<T>();
for (T[] one : arrays)
Collections.addAll(al, one);
return (T[]) al.toArray(arrays[0].clone());
}
}
Và chỉ cần làm Array.concat(arr1, arr2)
. Miễn là arr1
và arr2
cùng loại, điều này sẽ cung cấp cho bạn một mảng khác cùng loại chứa cả hai mảng.
public String[] concat(String[]... arrays)
{
int length = 0;
for (String[] array : arrays) {
length += array.length;
}
String[] result = new String[length];
int destPos = 0;
for (String[] array : arrays) {
System.arraycopy(array, 0, result, destPos, array.length);
destPos += array.length;
}
return result;
}
Đây là phiên bản cải tiến của Joachimerer của tôi. Nó có thể hoạt động trên Java 5 hoặc 6, sử dụng System.arraycopy của Java 6 nếu nó có sẵn trong thời gian chạy. Phương pháp này (IMHO) hoàn hảo cho Android, vì nó hoạt động trên Android <9 (không có System.arraycopy) nhưng sẽ sử dụng phương pháp nhanh hơn nếu có thể.
public static <T> T[] concatAll(T[] first, T[]... rest) {
int totalLength = first.length;
for (T[] array : rest) {
totalLength += array.length;
}
T[] result;
try {
Method arraysCopyOf = Arrays.class.getMethod("copyOf", Object[].class, int.class);
result = (T[]) arraysCopyOf.invoke(null, first, totalLength);
} catch (Exception e){
//Java 6 / Android >= 9 way didn't work, so use the "traditional" approach
result = (T[]) java.lang.reflect.Array.newInstance(first.getClass().getComponentType(), totalLength);
System.arraycopy(first, 0, result, 0, first.length);
}
int offset = first.length;
for (T[] array : rest) {
System.arraycopy(array, 0, result, offset, array.length);
offset += array.length;
}
return result;
}
Một cách khác để suy nghĩ về câu hỏi. Để nối hai hoặc nhiều mảng, người ta phải làm là liệt kê tất cả các phần tử của mỗi mảng, sau đó xây dựng một mảng mới. Điều này âm thanh như tạo ra một List<T>
và sau đó gọi toArray
nó. Một số câu trả lời khác sử dụng ArrayList
, và điều đó tốt. Nhưng làm thế nào về việc thực hiện của chúng ta? Nó không khó:
private static <T> T[] addAll(final T[] f, final T...o){
return new AbstractList<T>(){
@Override
public T get(int i) {
return i>=f.length ? o[i - f.length] : f[i];
}
@Override
public int size() {
return f.length + o.length;
}
}.toArray(f);
}
Tôi tin rằng ở trên là tương đương với các giải pháp sử dụng System.arraycopy
. Tuy nhiên tôi nghĩ cái này có vẻ đẹp riêng của nó.
Làm thế nào về :
public String[] combineArray (String[] ... strings) {
List<String> tmpList = new ArrayList<String>();
for (int i = 0; i < strings.length; i++)
tmpList.addAll(Arrays.asList(strings[i]));
return tmpList.toArray(new String[tmpList.size()]);
}
Chỉ sử dụng API riêng của Javas:
String[] join(String[]... arrays) {
// calculate size of target array
int size = 0;
for (String[] array : arrays) {
size += array.length;
}
// create list of appropriate size
java.util.List list = new java.util.ArrayList(size);
// add arrays
for (String[] array : arrays) {
list.addAll(java.util.Arrays.asList(array));
}
// create and return final array
return list.toArray(new String[size]);
}
Bây giờ, mã này không phải là hiệu quả nhất, nhưng nó chỉ dựa trên các lớp java tiêu chuẩn và rất dễ hiểu. Nó hoạt động với bất kỳ số lượng Chuỗi [] (thậm chí là không có mảng).