String
Javaで2つの配列を連結する必要があります。
void f(String[] first, String[] second) {
String[] both = ???
}
これを行う最も簡単な方法は何ですか?
array1 + array2
連結を取得できなかったのはなぜでしょうか。
String
Javaで2つの配列を連結する必要があります。
void f(String[] first, String[] second) {
String[] both = ???
}
これを行う最も簡単な方法は何ですか?
array1 + array2
連結を取得できなかったのはなぜでしょうか。
回答:
古き良きApache Commons Langライブラリから1行のソリューションを見つけました。
ArrayUtils.addAll(T[], T...)
コード:
String[] both = ArrayUtils.addAll(first, second);
2つの配列を連結して結果を返す単純なメソッドを次に示します。
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;
}
プリミティブデータ型では機能せず、オブジェクト型でのみ機能することに注意してください。
次の少し複雑なバージョンは、オブジェクト配列とプリミティブ配列の両方で機能します。引数の型としてのT
代わりにT[]
を使用してこれを行います。
また、結果のコンポーネントタイプとして最も一般的なタイプを選択することで、2つの異なるタイプの配列を連結することもできます。
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;
}
次に例を示します。
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);
。意外にも、これまで見たことがない。@beaudetなぜ抑制されているのかを考えると、注釈はここで問題ないと思います。
任意の数の配列を連結するように拡張することさえできる完全に汎用的なバージョンを書くことが可能です。このバージョンではJava 6が必要です。Arrays.copyOf()
どちらのバージョンも、中間List
オブジェクトの作成を避けて使用しますSystem.arraycopy()
大きな配列のコピーを可能な限り高速にするためにします。
2つの配列の場合、次のようになります。
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;
}
そして、任意の数の配列(> = 1)の場合、次のようになります。
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
を置き換えるだけですbyte
(そしてを失う<T>
)。
ByteBuffer buffer = ByteBuffer.allocate(array1.length + array2.length); buffer.put(array1); buffer.put(array2); return buffer.array();
concat(ai, ad)
、ai
is Integer[]
とad
is Double[]
です。(この場合、型パラメーター<T>
は<? extends Number>
コンパイラーによって解決されます。)によって作成された配列Arrays.copyOf
は、最初の配列のコンポーネント型になりますInteger
(この例では)。関数が2番目の配列をコピーしよArrayStoreException
うとすると、がスローされます。解決策は、追加のClass<T> type
パラメーターを持つことです。
Stream
Java 8での使用:
String[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b))
.toArray(String[]::new);
または、次のように使用しflatMap
ます:
String[] both = Stream.of(a, b).flatMap(Stream::of)
.toArray(String[]::new);
ジェネリック型に対してこれを行うには、リフレクションを使用する必要があります。
@SuppressWarnings("unchecked")
T[] both = Stream.concat(Arrays.stream(a), Arrays.stream(b)).toArray(
size -> (T[]) Array.newInstance(a.getClass().getComponentType(), size));
.boxed()
タイプStream
ではなく、タイプである必要がありIntStream
ますStream.concat
。
a
とb
されint[]
、使用int[] both = IntStream.concat(Arrays.stream(a), Arrays.stream(b)).toArray();
System.arrayCopy
。しかし、特に遅くはありません。あなたはおそらくこれを行うには非常に多くの時間を巨大で配列本当に問題には、実行時間差のパフォーマンスに敏感なコンテキスト。
または最愛のグアバと:
String[] both = ObjectArrays.concat(first, second, String.class);
また、プリミティブ配列には次のバージョンがあります。
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)
2つの配列を2行のコードで追加できます。
String[] both = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, both, first.length, second.length);
これは高速で効率的なソリューションであり、プリミティブ型で機能し、関連する2つのメソッドがオーバーロードされます。
ArrayList、ストリームなどを含むソリューションは、有用な目的のために一時メモリを割り当てる必要があるため、避けてください。
for
大規模な配列のループは効率的ではないため、避ける必要があります。組み込みのメソッドは、非常に高速なブロックコピー機能を使用します。
Java APIの使用:
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()]);
}
100%古いJavaとなしの ソリューションSystem.arraycopy
(たとえば、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
チェックを追加することもできます。そして、おそらくいくつかの変数をに設定しますfinal
。
null
チェックは、NPEを表示するのではなく非表示にし、ローカル変数にfinalを使用しても、(まだ)メリットはありません。
私は最近、過度のメモリローテーションの問題と戦いました。aおよび/またはbが一般的に空であることがわかっている場合、silvertabのコードの別の適応を以下に示します(一般化されています)。
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;
}
編集:この投稿の以前のバージョンでは、このようなアレイの再利用は明確に文書化する必要があると述べていました。Maartenがコメントで指摘しているように、一般的にはifステートメントを削除する方がよいため、ドキュメントを用意する必要がなくなります。しかし、繰り返しになりますが、これらのifステートメントがそもそもこの特定の最適化の要点でした。この答えはここに残しておきますが、注意してください。
System.arraycopy
配列の内容をコピーすると思いましたか?
if
ステートメントを省略するだけで簡単に修正できます。
ArrayList<String> both = new ArrayList(Arrays.asList(first));
both.addAll(Arrays.asList(second));
both.toArray(new String[0]);
both.toArray(new String[both.size()])
。;)
ジェネリックが後付けされた、silvertabのソリューションを以下に示します。
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;
}
注:Java 6ソリューションについては、Joachimの回答を参照してください。警告を排除するだけでなく、また、短く、効率的で読みやすくなっています。
この方法を使用すると、サードパーティのクラスをインポートする必要がありません。
連結したい場合 String
2つの文字列配列を連結するためのサンプルコード
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;
}
連結したい場合 Int
2つの整数配列を連結するためのサンプルコード
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;
}
ここにメインメソッドがあります
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));
}
}
この方法でも使用できます。
このすでに長いリストに別のバージョンを追加したことを許してください。私はすべての答えを見て、署名にパラメーターが1つだけあるバージョンが本当に必要だと判断しました。また、予期しない入力があった場合の賢明な情報による早期の失敗から利益を得るために、いくつかの引数チェックを追加しました。
@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;
}
これをArraylistに変換して、addAllメソッドを使用してから、配列に変換してみることができます。
List list = new ArrayList(Arrays.asList(first));
list.addAll(Arrays.asList(second));
String[] both = list.toArray();
ここで、silvertabによって作成された疑似コードソリューションの作業コードの可能な実装。
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;
}
}
次はビルダーインターフェイスです。
注:Javaでは実行できないため、ビルダーが必要です。
new T[size]
ジェネリック型の消去のため:
public interface ArrayBuilderI<T> {
public T[] build(int size);
}
ここでは、インターフェースを実装してInteger
配列を構築する具体的なビルダーを示します。
public class IntegerArrayBuilder implements ArrayBuilderI<Integer> {
@Override
public Integer[] build(int size) {
return new Integer[size];
}
}
そして最後にアプリケーション/テスト:
@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]);
}
}
うわー!外部の依存関係に依存するいくつかの単純なものを含め、ここには多くの複雑な答えがあります。このようにするのはどうですか:
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]);
これは機能しますが、独自のエラーチェックを挿入する必要があります。
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();
}
}
おそらく最も効率的ではありませんが、Java独自のAPI以外には依存していません。
for
。それとループ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)
最初のfor
ループをスキップするために使用します。のサイズに比例して時間を節約しますarr1
。
これは文字列配列に変換された関数です:
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;
}
単純にどうですか
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());
}
}
そして、ただArray.concat(arr1, arr2)
。arr1
とarr2
が同じ型である限り、これにより、両方の配列を含む同じ型の別の配列が得られます。
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;
}
これは、Joachim SauerのconcatAllを少し改良したバージョンです。実行時に使用できる場合は、Java 6のSystem.arraycopyを使用してJava 5または6で動作します。この方法(IMHO)は、Android <9(System.arraycopyがない)で動作するため、Androidに最適ですが、可能であればより高速な方法を使用します。
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;
}
質問について考える別の方法。2つ以上の配列を連結するには、各配列のすべての要素をリストしてから、新しい配列を作成する必要があります。これは、a List<T>
を作成し、それを呼び出すように聞こえますtoArray
。他のいくつかの答えはを使用していますがArrayList
、それは問題ありません。しかし、私たち自身の実装はどうですか?それは難しくありません:
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);
}
上記はを使用するソリューションと同等であると思いますSystem.arraycopy
。しかし、これはそれ自身の美しさを持っていると思います。
どのように:
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()]);
}
Java独自のAPIのみを使用:
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]);
}
現在、このコードは最も効率的ではありませんが、標準のJavaクラスのみに依存しており、理解しやすくなっています。これは、任意の数のString [](ゼロ配列でも)で機能します。