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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Java

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

インターフェース

インターフェイスという用語はハードウェア・ソフトウェアの両方に使うことができます。 一般的に、インターフェイスは内部処理の詳細を見せないように設定されます。オブジェクト指向プログラミングにおいて、インターフェイスはabstractクラスとして定義されます。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

6回答

2396閲覧

2次元配列の読み取りアクセスのみのインターフェースを返す方法とはどうするのでしょうか?

gyro16

総合スコア89

Java

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

インターフェース

インターフェイスという用語はハードウェア・ソフトウェアの両方に使うことができます。 一般的に、インターフェイスは内部処理の詳細を見せないように設定されます。オブジェクト指向プログラミングにおいて、インターフェイスはabstractクラスとして定義されます。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2016/07/09 01:21

編集2016/07/09 03:34

###前提・実現したいこと
private Card[][] table_ = new Card[4][13];
カプセル化された七並べの2次元配列

これを読み取って返すのに、「読み取りアクセスのみのインターフェースを返す」方法が書籍では割愛するがあるとありました。

(1)配列をコピーArrayCopy()してその配列を返す、やり方が書いてありましたが、
この方法だと使い捨てオブジェクトを毎回生成していつかGC、ガーベジコレクションが発生する可能性があるので、回避するには「読み取りアクセスのみをインターフェースを返す」方法があるとありました。

2次元配列の読み取りアクセスのみのインターフェースを返す方法とはどうやるのでしょうか?

###発生している問題・エラーメッセージ

エラーメッセージ

###該当のソースコード

ここにご自身が実行したソースコードを書いてください

###試したこと
課題に対してアプローチしたことを記載してください

###補足情報(言語/FW/ツール等のバージョンなど)
うーんと説明が足りなかったようです。配列の一要素を取り出すのではなく、2次元配列そのものを取り出す場合なのです。
その時点でのテーブルtable_(プレイフィールド)の2次元配列を読み取って2次元配列を返すのでCard[][]を返す場合の「読み取りアクセスのみのインターフェースを返す」ということです。return table_;は好ましくないという趣旨で書かれています。”カプセル化”された配列の参照渡しはしないという意味だと思うのですがどうすればいいのでしょうか?

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

Java

1public class TableD implements Table{ 2 3 private Card[][] table_ = new Card[Card.SUIT_NUM][Card.CARD_NUM]; 4 5 boolean copyFlg = false; 6 7 public Card[][] getCards(){ 8 if(copyFlg == false){ 9 System.arraycopy(table_[suit], 0, table[suit], 0, Card.CARD_NUM); 10 copyFlg == true; 11 } else { 12 for(int i = 0; i < Card.SUIT_NUM; i++){ 13 for(int j = 0; j < Card.CARD_NUM; j++){ 14 table[i][j] = table_[i][j]; 15 } 16 } 17 } 18 return table; 19 } 20}

インターフェースの方は分からないので、コピー配列を再利用する方法。

投稿2016/07/09 06:00

gyro16

総合スコア89

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

元の文献では、UnmodifiableCollection をつかうことを意図していると思います。

参考情報:

...
Java では final の配列の要素は可変です。
...
Collections#unmodifiableList

引数で渡された List と同じ要素を持つ、変更不可の List を返すメソッドです。下記の例では Arrays#asList で一時的な List を作り、それを引数にして Collections#unmodifiableList を呼び出し、変更不可の List を取得しています。

...

投稿2016/07/09 05:49

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

既にある回答の焼き直しになりますが,アプローチとしては2つあります.

A. イミュータブルなリストを返す
B. 配列の一要素のみを参照できるメソッドを提供する.

これ以外の方法はありません.

【A】 java.util.Collections.unmodifiableListメソッドで,イミュータブルなリストは作れるそうです.プリミティブの配列は無理なのでリストで妥協してください.また,そのまま何も考えずに書いてしまうとメソッドを呼び出すたびにリストが作られてしまって意味が無いので,変更を加えたあとの1回目の呼び出しのみCollections.unmodifiableList()で生成するようにし,2回目以降はメンバ変数に保存しておいたものをreturnすればよいでしょう.変更を加えた時にtableChanged_の値も変更するようにしてください.

java

1public List<List<Card>> getTable() 2{ 3 if (tableChanged_) { 4 tableCache_ = Collections.unmodifiableList( 5 Stream 6 .of(table_) 7 .map(row -> Collections.unmodifiableList(Arrays.asList(row))) 8 .collect(Collectors.toList()) 9 ); 10 tableChanged_ = false; 11 } 12 return tableCache_; 13}

【B】 既存の回答の通り,ビュー用のメソッドを作る方法です.

投稿2016/07/09 05:49

編集2016/07/09 06:05
mpyw

総合スコア5223

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

java

1public class TableD implements Table{ 2 3 private Card[][] table_ = new Card[Card.SUIT_NUM][Card.CARD_NUM]; 4 5 boolean copyFlg = false; 6 7 public Card[][] getCards(){ 8 if(copyFlg == false){ 9 System.arraycopy(table_[suit], 0, table[suit], 0, Card.CARD_NUM); 10 copyFlg == true; 11 } else { 12 for(int i = 0; i < Card.SUIT_NUM; i++){ 13 for(int j = 0; j < Card.CARD_NUM; j++){ 14 table[i][j] = table_[i][j]; 15 } 16 } 17 } 18 return table; 19 } 20}

インターフェースの利用はできませんでしたが、
ガーベジコクレクションを回避すべく、コピー配列を再利用しました。
フラグでコピーされているか識別しました。

投稿2016/07/09 05:43

gyro16

総合スコア89

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mpyw

2016/07/09 06:06 編集

tableが以下のいずれかを満たしていなければ「読み取り専用」になりません. (1) メソッドの呼び出しごとに新規生成される (2) イミュータブルである tableを宣言している場所が見当たらないので何ともいえないのですが,もし呼び出しごとに新規生成しないのであれば,リターンされた値に変更を加えたとき,次のgetCardsの呼び出しでその変更が無効化されてしまう問題点があります.新規生成するのであれば,そもそもarraycopyとやってることが同じで実行スピードもメモリ効率も悪いです. そこで,私やkatoyさんが提案するように,「リターンされた値に変更」することができなくなるように,イミュータブルなリストを返す方法をおすすめします.
guest

0

ベストアンサー

その記述は、

java

1interface ReadableMatrix { 2 Card get(int x, int y); 3}

なるインターフェイスを作ってその実装を返しなと言っています。
Java8ならわざわざ自分でこのインターフェイスを書かなくてもBiFunction<Integer,Integer,Card>が使えます。

だからJava8なら、BiFunction<Integer,Integer,Card>を返すことにしておいて

return (int x, int y) -> table_[x][y];

ですかね。
それとももっと古いJavaですか?

投稿2016/07/09 02:22

yuba

総合スコア5568

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gyro16

2017/01/04 03:29 編集

`interface ReadableMatrix { Card getCard(int suit, int number); } ` でこれを実装し、 ` FantanTable implements ReadableMatrix{ public Card getCard(int suit, int number){ Card card = table_[suit-1][number-1]; return card; } } ` これでできます。ありがとうございました。
guest

0

これはいわゆる「ビュー」を作るということですね。

2次元配列へのアクセスはtable_[i][j]なので、
それをメソッドでできるようにすれば良いですね。

lang

1Card getCard(int i, int j);

というメソッドだけを持ったインターフェイスを定義して、
その実装クラスにtable_を持たせるようにします。

インターフェイスがinterfaceでなく広義のを指しているなら、
クラス単体でも読み取りアクセスは実現できますね。

投稿2016/07/09 02:21

argius

総合スコア9388

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gyro16

2016/07/09 03:33

ご返答ありがとうございます。 うーんと説明が足りなかったようです。配列の一要素を取り出すのではなく、2次元配列そのものを取り出す場合なのです。 その時点でのテーブルtable_(プレイフィールド)の2次元配列を読み取って2次元配列を返すのでCard[][]を返す場合の「読み取りアクセスのみのインターフェースを返す」ということです。return table_;は好ましくないという趣旨で書かれています。”カプセル化”された配列の参照渡しはしないという意味だと思うのですがどうすればいいのでしょうか?
argius

2016/07/09 03:54

Javaで、コピー以外でかつ2次元配列そのものを読み取り専用にする方法というのは 聞いたことがありません。 List<List<Card>>型を使ってできないこともありませんが、 明示的な読み取り専用にならないので View方式のほうが良い気がします。 ビューがCardTableViewという名前のインターフェイスだとしたら、 CardTableView view = createView(table_); // createViewはどこかで定義 Card card = view.get(3, 5); // table_[3][5]と同等、書き込みはできない のようにできるので、 実質は2次元配列の読み取り専用と同じことになります。 1つの要素を取り出すだけでは足りなければ、 その操作をインターフェイスに追加すれば良いです。
gyro16

2016/07/09 04:59

pubilc Card[][] getCards(){ //ここで返したい、本にはSystem.arraycopy()コピーした配列tableをreturnしています。 }これを「インターフェースで返す」にはどうしたらいいでしょうか? }
argius

2016/07/09 05:06

ここまでに書いたことで納得いただけないのでしたら、 ちょっとこれ以上は回答のしようがありませんね。 本当に pubilc Card[][] getCards() のままでインターフェイスにするなんて方法があるんでしょうか? 私も答えが知りたいです。 力及ばず申し訳ありません。
gyro16

2016/07/09 05:11

七並べのテーブルなので、ゲームが進行するとtable_は更新されていきますが、table_をコピーしてその配列table一個だけを作ってgetCards()の時だけ、table_からtableに更新、追加格納してtableを返す。再利用できるコピーした配列tableはどのように書けばいいでしょうか?
argius

2016/07/09 05:31

> table_からtableに更新 table_はフィールドだとして、 tableは何を表していますか? 更新はただ移し替えれば良いのでは?
gyro16

2016/07/09 05:39

レスありがとうございました。 理解は深まりました。
退会済みユーザー

退会済みユーザー

2016/07/09 13:40

皆さんもおっしゃっていますが、Javaの配列は要素を書き換え可能だということを理解していただく必要があります。 次のようなコードを書くと、配列cards[i][j]の内容が置き換わる、つまり、table_[i][j]の内容が置き換わることになります。すると、カードゲームが正常に動作できなくなります。 Card[][] cards = x.getCards(); cards[i][j] = new Card(...); その回避策として、getCards()が呼び出されるたびに、table_と同じ内容の新しい配列を作成しコピーして返せばよいのです。コピーですから、上のコードを実行しても、table_そのものには影響がありません。しかし、getCards()を呼び出すたびに、新しい配列を作成するため、GCが頻発しやすくなるデメリットがあります。 結局のところ、「読み取りアクセスのみのインターフェースを返す」ということは、「配列以外のモノを返しましょう」ということになるわけです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問