Câu hỏi Làm thế nào tôi có thể nối hai mảng trong Java?


Tôi cần phải nối hai mảng String trong Java.

void f(String[] first, String[] second) {
    String[] both = ???
}

cách dễ nhất để làm điều này là gì?


1136


gốc


Bytes.concat từ Guava - Ben Page
Tôi thấy rất nhiều câu trả lời ở đây, nhưng câu hỏi được diễn đạt như vậy ('cách dễ nhất'?) Mà nó không cho phép chỉ ra câu trả lời tốt nhất ... - Artur Opalinski
Hàng chục câu trả lời ở đây là sao chép dữ liệu vào một mảng mới vì đó là những gì được yêu cầu - nhưng sao chép dữ liệu khi không thực sự cần thiết là một điều xấu để làm đặc biệt trong Java. Thay vào đó, hãy theo dõi các chỉ mục và sử dụng hai mảng như thể chúng được nối với nhau. Tôi đã thêm một giải pháp minh họa kỹ thuật. - Douglas Held
Đơn giản nhất là bạn có lẽ không nên sử dụng mảng ở nơi đầu tiên, bạn nên sử dụng ArrayLists, và đầu ra của bạn phải là một ArrayList. Một khi bạn đã thực hiện những điều kiện tiên quyết của bạn, hoạt động được xây dựng trong - first.addAll (thứ hai). Trường hợp duy nhất mà điều này sẽ không được tự động khá nhiều là khi các mảng của bạn không phải là các kiểu đối tượng (int, long, double, ...), trong trường hợp đó mảng nội tại có thể có lợi thế lớn hơn ArrayLists - nhưng đối với Strings --meh - Bill K
Thực tế là một câu hỏi như thế này hiện có 50 câu trả lời khác nhau khiến tôi tự hỏi tại sao Java không bao giờ đơn giản array1 + array2nối. - JollyJoker


Các 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 cũ của Apache Commons Lang.
  ArrayUtils.addAll(T[], T...)

Mã số:

String[] both = (String[])ArrayUtils.addAll(first, second);

923



Tôi không biết, đây là loại gian lận. Tôi nghĩ rằng hầu hết mọi người có lẽ sẽ không muốn phụ thuộc thêm cho phương pháp này. - Outlaw Programmer
Làm thế nào là nó "gian lận" nếu nó trả lời câu hỏi? Chắc chắn, có một sự phụ thuộc phụ có lẽ là quá mức cần thiết cho tình huống cụ thể này, nhưng không có hại gì được thực hiện khi gọi nó tồn tại, đặc biệt là vì có rất nhiều bit chức năng tuyệt vời trong Apache Commons. - Rob
Tôi đồng ý, điều này không thực sự trả lời câu hỏi. Thư viện cấp cao có thể là tuyệt vời, nhưng nếu bạn muốn tìm hiểu một cách hiệu quả để làm điều đó, bạn muốn xem mã mà phương thức thư viện đang sử dụng. Ngoài ra, trong nhiều tình huống, bạn không thể chỉ thông qua một thư viện khác trong sản phẩm khi đang bay. - AdamC
Tôi nghĩ đây là một câu trả lời hay. Các giải pháp POJO cũng đã được cung cấp, nhưng nếu OP đang sử dụng Apache Commons trong chương trình của họ thì có thể vẫn chưa biết giải pháp này. Sau đó, ông sẽ không "thêm một phụ thuộc cho phương pháp này," nhưng sẽ sử dụng tốt hơn một thư viện hiện có. - Adam
Nếu bạn luôn lo lắng về việc không thêm một thư viện cho một phương thức duy nhất, sẽ không có thư viện mới nào được thêm vào. Với các tiện ích tuyệt vời có trong Apache Commons, tôi khuyên bạn nên thêm nó khi trường hợp sử dụng đầu tiên phát sinh. - Hindol


Đây là một phương thức đơn giản sẽ nối 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 ý như nó sẽ không làm việc với nguyên thủy, chỉ với các loại đối tượng.

Phiên bản hơi phức tạp hơn 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 loại đối số.

Nó cũng làm cho nó có thể ghép nối 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 }));

722



Tôi thích gợi ý này vì nó ít phụ thuộc vào các phiên bản Java mới nhất. Trong các dự án của tôi, tôi thường bị mắc kẹt khi sử dụng các phiên bản cũ hơn của các cấu hình Java hoặc CLDC, nơi một số các cơ sở như những cơ sở được đề cập bởi Antti không có sẵn. - Kevin
Dòng sau sẽ phá vỡ phần chung: nối (chuỗi mới [] {"1"}, đối tượng mới [] {đối tượng mới ()}) - dragon66


Có thể viết một phiên bản hoàn toàn chung mà thậm chí có thể được mở rộng để ghép 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, khi chúng sử dụng Arrays.copyOf()

Cả hai phiên bản đều tránh tạo ra bất kỳ trung gian nào List đối tượng và sử dụng System.arraycopy() để đảm bảo rằng việc 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 giống như sau:

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 một số tùy ý của mảng (> = 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;
}

440



@ djBO: đối với mảng nguyên thủy, bạn cần phải thực hiện quá tải cho từng loại: chỉ cần sao chép mã và thay thế từng mã T với byte (và mất <T>). - Joachim Sauer
bạn có thể plese cho tôi biết làm thế nào để sử dụng <T> loại nhà điều hành trong lớp học của tôi? - Johnydep
Tôi sẽ thêm điều này vào đầu, chỉ để phòng thủ. if (first == null) {if (thứ hai == null) {return null; } trở về thứ hai; } if (second == null) {return trước; } - marathon
@ djBo: những gì về:ByteBuffer buffer = ByteBuffer.allocate(array1.length + array2.length); buffer.put(array1); buffer.put(array2); return buffer.array(); - Sam Goldberg
Có một lỗi trong cách tiếp cận này trở nên rõ ràng nếu bạn gọi các hàm này với các mảng của các kiểu thành phần khác nhau, ví dụ concat(ai, ad), Ở đâu ai Là Integer[] và ad Là Double[]. (Trong trường hợp này, tham số kiểu <T> được giải quyết cho <? extends Number> bởi trình biên dịch.) Mảng được tạo bởi Arrays.copyOf sẽ có loại thành phần của mảng đầu tiên, tức là Integer trong ví dụ này. Khi hàm sắp sao chép mảng thứ hai, ArrayStoreException sẽ bị ném. Giải pháp là có thêm Class<T> type tham số. - T-Bull


Một lớp lót trong Java 8:

String[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b))
                      .toArray(String[]::new);

Hoặc là:

String[] both = Stream.of(a, b).flatMap(Stream::of)
                      .toArray(String[]::new);

328



Làm thế nào hiệu quả là điều này? - Supuhstar
Đáng đọc: jaxenter.com/…  tl; dr - suối có thể được thực hiện hay không, nó phụ thuộc vào những gì bạn đang làm với họ và những hạn chế của vấn đề (không phải luôn luôn là câu trả lời? lol) - Trevor Brown
Ngoài ra, nếu một hoặc là b là các mảng nguyên thủy, luồng của chúng sẽ cần phải .boxed() vì vậy chúng thuộc loại Stream thay vì ví dụ: IntStream không thể chuyển thành tham số Stream.concat. - Will Hardwick-Smith
@Will Hardwick-Smith: không, bạn chỉ phải chọn lớp học phù hợp, ví dụ: nếu a và b là int[], sử dụng int[] both = IntStream.concat(Arrays.stream(a), Arrays.stream(b)).toArray(); - Holger
@Supuhstar: Có thể không nhanh bằng System.arrayCopy. Nhưng cũng không chậm. Bạn có thể phải làm điều này rất nhiều lần với khổng lồ mảng trong có thật không bối cảnh nhạy cảm về hiệu suất cho sự khác biệt thời gian thực hiện cho vấn đề. - Lii


Hoặc với người yêu Trái ổi:

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)

168



Nhiều như tôi yêu ổi, phương pháp từ Apache Commons giao dịch tốt hơn với nullables. - Ravi Wallau
Mặc dù rất tốt khi sử dụng thư viện nhưng thật không may là vấn đề đã bị trừu tượng hóa. Do đó, giải pháp cơ bản vẫn còn khó nắm bắt. - user924272
Whats vấn đề với trừu tượng? Dunno thỏa thuận với việc tái phát minh bánh xe ở đây là gì, nếu bạn muốn tìm hiểu vấn đề, hãy kiểm tra nguồn hoặc đọc nó. Mã chuyên nghiệp nên sử dụng thư viện cấp cao, tốt hơn nếu nó được phát triển bên trong Google! - Breno Salgado
@RaviWallau Bạn có thể liên kết với lớp học không? - Sébastien Tromp
@ SébastienTromp Đây là giải pháp hàng đầu cho câu hỏi này - ArrayUtils. - Ravi Wallau


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()]);
}

53



Đơn giản, nhưng không hiệu quả vì nó tạo một mảng cho ArrayList và sau đó tạo một mảng khác cho phương thức toArray. Nhưng vẫn hợp lệ vì nó đơn giản để đọc. - PhoneixS
áp dụng cho các chuỗi và các đối tượng (như câu hỏi muốn), nhưng không có phương thức addAll cho các kiểu chính (như ints) - joro


Một giải pháp 100% java cũ và không có  System.arraycopy (không có sẵ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;
}

37



đã được sửa lại cho File [], nhưng nó giống nhau. Cảm ơn vì giải pháp của bạn - ShadowFlame
Có lẽ khá kém hiệu quả. - JonasCz
Bạn có thể muốn thêm null kiểm tra. Và có thể đặt một số biến của bạn thành final. - Tripp Kinetics


Gần đây tôi đã gặp vấn đề với việc xoay bộ nhớ quá mức. Nếu một và / hoặc b được biết là thường trống rỗng, đây là một sự thích nghi khác của mã của silvertab (được mở rộng quá):

private static <T> T[] concat(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;
}

(Trong cả hai trường hợp, hành vi tái sử dụng mảng phải rõ ràng là JavaDoced!)


29



tuy nhiên điều này có nghĩa là bạn đang quay trở lại cùng một mảng và thay đổi một giá trị trên mảng được trả về thay đổi giá trị ở cùng vị trí của mảng đầu vào được trả về. - Lorenzo Boccaccia
Có - xem nhận xét ở cuối bài đăng của tôi về việc sử dụng lại mảng. Chi phí bảo trì áp đặt bởi giải pháp này là giá trị nó trong trường hợp cụ thể của chúng tôi, nhưng sao chép phòng thủ có lẽ nên được sử dụng trong hầu hết các trường hợp. - volley
Lorenzo / volley, bạn có thể giải thích phần nào trong mã khiến cho việc tái sử dụng mảng không? tôi đã nghĩ System.arraycopy sao chép nội dung của mảng? - Rosdi Kasim
Một người gọi thường mong đợi một cuộc gọi đến concat () để trả về một mảng mới được phân bổ. Nếu a hoặc b là null, concat () sẽ trả về một trong các mảng được truyền vào nó. Việc tái sử dụng này là điều có thể bất ngờ. (Yep, arraycopy chỉ sao chép. Việc tái sử dụng đến từ việc trả về a hoặc b trực tiếp.) - volley


Các Java chức năng thư viện có một lớp bao bọc mảng trang bị mảng với các phương thức 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 được mở, hãy gọi

String[] s = both.array();

26