質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.34%

  • Java

    16774questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

2つの配列の同インデックス要素を演算する for ループの Stream への書き直し方

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,825

JavaTea

score 23

前提・実現したいこと

最近 Java を学び始め、Java8 では多くのループ構造を Stream API で実装可能であり、またこれからは可能な限りそうするべきとも伺いました。
私としても可能な限りの Stream の活用を心掛けたく思うのですが、2つの配列の同インデックス同士を演算していくような for ループの Stream での上手い書き方を思いつけずにおります。
下記のような for ループを上手く Stream 化する方法にお知恵をお貸しいただけましたら幸いです。

該当のソースコード

public static void main(String[] args) {

        // main の引数から配列のサイズを指定
        int limit = Integer.parseInt(args[0]);

        // 1 ずつインクリメントする int 配列を作成。 limit が 5 なら {1, 2, 3, 4, 5}
        int[] arr1 = Stream.iterate(1, i -> i + 1).limit(limit).mapToInt(Integer::intValue).toArray();

        // 1 から 10 倍していく int 配列を作成。 limit が 5 なら {1, 10, 100, 1000, 10000}
        int[] arr2 =Stream.iterate(1, i -> i * 10).limit(limit).mapToInt(Integer::intValue).toArray();

// 配列 arr1, arr2 は for ループ内の動きを模式的に表すためのサンプルとなります。
//説明の足りませんでした点を補いますと、任意の値を内容に持つ2つの配列で
//同インデックス同士を対応させていくようなループ処理を模索しております。

        // arr1 の各要素に arr2 の対応インデックスを掛け合わせていく、このような for ループを Stream 化したい
        for(int i =0; i < arr1.length; i++){
            arr1[i] *= arr2[i];
            System.out.println(arr1[i]); // 1, 20, 300, 4000, 50000
        }

        // Arrays.stream(arr1).map(arr -> ...?
}

試したこと

Arrays.stream(arr1).map(arr -> ...?

から map メソッドを用いて実装していくべきかと思いましたが、インデックス同士の対応をどのように書いていくべきか思いつけずにおります。
よい書き方ございましたら、ご助言よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

関数型プログラミングが十分に考慮された言語では、二つのストリームをくっつけて一つのストリームにすることをzipという関数で実装しています。たとえば、Scalaであれば、下記のようになります。

object SampleZipScala {
  def main(args: Array[String]): Unit = {
    val limit = args(0).toInt
    val st1 = Stream.iterate(1L)(i => i + 1)
    var st2 = Stream.iterate(1L)(i => i * 10)
    (st1 zip st2).take(limit).foreach(x => Console.println(x._1 * x._2))
  }
}


※ Int(32bit)だと小さすぎるためLong(64bit)にしています。

Scala以外でもHaskellを始め、C#(というよりLINQ)、Python、Rubyなどよくできていると言われる言語では標準で採用されています。しかし、Java 8ではzip関数は採用されていません。現在開発中の Java 9でもまだ採用はされていないようです。Java標準のStreamで将来使えるようになるのかは不明です。

では、どうするかですが、選択肢は4つほどです。

① for文を使う。
一番確実です。Streamを無理に使うのは諦めましょう。Javaを使い続ける限りはこれが一番いいのではないかと思います。

import java.util.stream.LongStream;
import java.util.PrimitiveIterator;

class SampleZipJavaFor {
  public static void main(String[] args) {
    final int limit = Integer.parseInt(args[0]);
    final LongStream st1 = LongStream.iterate(1L, i -> i + 1);
    final LongStream st2 = LongStream.iterate(1L, i -> i * 10);
    final PrimitiveIterator.OfLong it1 = st1.iterator();
    final PrimitiveIterator.OfLong it2 = st2.iterator();
    for (int i = 0; i < limit; i++) {
      System.out.println(it1.nextLong() * it2.nextLong());
    }
  }
}

② 片方だけSreamにして、もう一方をIteratorにする。
for文なんて使いたくないというのであれば、下記のような書き方もあります。が、余り綺麗とは言えません。

import java.util.stream.LongStream;
import java.util.PrimitiveIterator;

class SampleZipJavaForEach {
  public static void main(String[] args) {
    final int limit = Integer.parseInt(args[0]);
    final LongStream st1 = LongStream.iterate(1L, i -> i + 1);
    final LongStream st2 = LongStream.iterate(1L, i -> i * 10);
    final PrimitiveIterator.OfLong it = st2.iterator();
    st1.limit(limit).forEach(i -> {
      System.out.println(i * it.nextLong());
    });
  }
}

③ zipを実装する、または、zipが実装されたライブラリを使う。
functional programming - Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip) - Stack Overflowに記載の回答など、"Java Stream zip"というキーワードで検索すると色々とサンプルやライブラリが出てきますので、それらのどれかを使うという手段もあります。

④ Javaを捨ててScalaを使う。
このように、JavaのStreamは中途半端です。Streamのような処理のような関数型プログラミングを中心にプログラミングをしたい場合はScalaを使うことをお勧めします。JavaVM環境に縛られていてもScalaはできますし、Javaの遺産との連携もできます。

⑤ JavaVM毎捨てる。
選択肢は自由です。C#も良し、Haskellも良し、PythonだってRubyだって、何だって使えます。Javaの言語設計は今となっては古く、多くの問題をはらんでいると言われています。どうしてもJavaでなければならない事情がなければ、Javaを捨てることをお勧めします。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/05 20:09

    ありがとうございます。現行の Java8 標準の仕様だけでは、私が考えていたような動作はそもそも無理筋なのですね。
    Java8 についての情報を調べて回る中、 for 文禁止などという話も見かけ何とか for 文を使わずに実現できないかと悩んでおりましたが、そもそも Java8 で私の考えていたことを実現しようとするのが無理筋であり、仕様的に無理な実装をなんとか実現出来ないかと無駄に悩んでいたと分かり大変助かりました。
    Java の学習はまだ取り掛かったばかりで捨てるのは流石に早すぎな気もしますので、Java の仕様で実現できそうな範囲でループ構造を Stream に書き換えていくことにします。

    キャンセル

+1

上記のような for ループ同士だとこのようなソースが考えられます

import java.util.*;
import java.io.*;
import java.util.stream.*;

import java.util.concurrent.atomic.AtomicInteger;
class DK{


public static void main(String[] args){

int limit=5;

int[] arr1 = Stream.iterate(1, i -> i + 1).limit(limit).mapToInt(Integer::intValue).toArray();

// 1 から 10 倍していく int 配列を作成。 limit が 5 なら {1, 10, 100, 1000, 10000} 
int[] arr2 =Stream.iterate(1, i -> i * 10).limit(limit).mapToInt(Integer::intValue).toArray();

AtomicInteger i2 = new AtomicInteger();


int[] arr3 =Stream.iterate(1, i -> i*10).limit(limit).mapToInt(Integer::intValue).map(s->s*i2.incrementAndGet()).toArray();

for(int ar:arr1){

System.out.println(ar);
}


for(int ar:arr2){

System.out.println(ar);
}


for(int ar:arr3){

System.out.println(ar);
}

}

}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/05 19:51

    ご回答誠にありがとうございます。AtomicInteger や incrementAndGet の情報大変参考になりました。

    ご教示頂きましたコードで私の例示コードの欲する出力は確かに満たされておりますので、tetratail 様に対しては私の説明力の至らなさをお詫びするほかないのですが、例示コードの arr1, arr2 は for ループ内の動作を分かりやすくするためのサンプル用途として、当座に作成しただけの配列となります。

    説明の至りませんでした点を補足させて頂きますと、規則的な値を配置した配列に限らず、任意の値を持つ配列同士でも2つの配列の同インデックス同士が対応していく演算のロジックが書けないものかと悩んでいる次第です。
    私の説明の至らなさをお詫びするとともに、ご回答重ねてお礼申し上げます。ありがとうございました。

    コード内で説明の足りていない点につきましては速やかに補足し改善いたします。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Java

    16774questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。