2016-07-25 2 views
-3

重複したエントリをチェックするソート済み配列があります。問題は、重複配列が多次元配列であった場所を知る必要があり、2番目のエントリも削除する必要があることです。また、最終的には、値でいっぱいの配列を持たなければならないので、いくつかのダミーで重複を上書きするだけではうまくいきません。私はArray.utilsのトリックを読んだことがありますが、私はむしろ他のライブラリをインポートしていないので、Array.utilsは認識されませんので、インポートする必要があります。ソートされた配列の重複のチェック

私も少し関数を記述しようとした自分が

 Integer iIndex = 1; 
    while (keepRunning2){ 
     if(phoneNameNumber[iIndex][0].equals(phoneNameNumber[iIndex-1][0])){ 
      keepRunning = true; 
      tmp = new String[phoneNameNumber.length-1][2]; 
      int k = 0; 
      int t=0; 
      while(keepRunning){ 
       Log.wtf("running",String.valueOf(iIndex) + ":" + String.valueOf(k)); 
       tmp[t][0] = phoneNameNumber[k][0]; 
       tmp[t][1] = phoneNameNumber[k][1]; 
       if(k!=i-1){ 
        k++; 
        t++; 
       }else{ 
        k=k+2; 
        t++; 
       } 
       if(t==tmp.length-1) 
        keepRunning=false; 

      } 
      phoneNameNumber = null; 
      phoneNameNumber = new String[tmp.length][2]; 
      phoneNameNumber = tmp; 
     } 
     if(!(iIndex < phoneNameNumber.length-1)){ 
      keepRunning2 = false; 
     } 
     iIndex++; 
    } 

Buuuut ...最初のそれは、これを完了するために、10年のようにかかり、これがアプリのUIのためであるとして、あなたはそれがあるべきことがわかりますalittle faster +これをすべて処理した後、何らかの理由でkeeppRunning2がfalseに設定されることはありません。

誰かが解決策を知っていますか?

、データがどこから来るSBたいがわかっている場合:配列内の重複がある理由HERESに活動

import android.app.ActionBar; 
import android.app.Activity; 
import android.app.DialogFragment; 
import android.content.ContentResolver; 
import android.content.Intent; 
import android.database.Cursor; 
import android.os.AsyncTask; 
import android.os.Bundle; 
import android.provider.ContactsContract; 
import android.support.v7.app.AppCompatActivity; 
import android.util.Log; 
import android.view.Menu; 
import android.view.MenuItem; 
import android.view.View; 
import android.widget.AdapterView; 
import android.widget.ArrayAdapter; 
import android.widget.Button; 
import android.widget.CheckBox; 
import android.widget.CheckedTextView; 
import android.widget.EditText; 
import android.widget.LinearLayout; 
import android.widget.RadioGroup; 
import android.widget.Spinner; 
import android.widget.TextView; 
import android.widget.Toast; 

import java.sql.Array; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.Comparator; 
import java.util.List; 
import java.util.concurrent.ExecutionException; 

/** 
* Created by **** on 19.07.2016. 
*/ 
public class newChatActivity extends Activity implements  View.OnClickListener,AdapterView.OnItemSelectedListener{ 

Button bCreateChat; 
Button bCancelChat; 
EditText tchat_name; 
//EditText tmembers; 
CheckBox cBoxPrivateChat; 
Spinner memberSpinner; 
String members = ""; 
String lastContactName = ""; 
List<String> nameList,number; 
String[][]phoneNameNumber,tmp; 
String[] sortedNames; 
Integer i = 0; 
Integer memberCount = 0; 
Boolean keepRunning = true; 
Boolean keepRunning2 = true; 

private static final String[] PROJECTION = new String[] { 
     ContactsContract.CommonDataKinds.Phone.CONTACT_ID, 
     ContactsContract.Contacts.DISPLAY_NAME, 
     ContactsContract.CommonDataKinds.Phone.NUMBER 
}; 



@Override 
public void onCreate(Bundle savedInstanceState){ 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.new_chat); 
    setTitle("Add New Chat"); 

    bCreateChat = (Button) findViewById(R.id.bCreateChat); 
    bCancelChat = (Button) findViewById(R.id.bChatCance); 
    tchat_name = (EditText) findViewById(R.id.tchat_name); 
    cBoxPrivateChat = (CheckBox) findViewById(R.id.cBoxprivChat); 
    memberSpinner = (Spinner) findViewById(R.id.memberSpinner); 


    bCreateChat.setOnClickListener(this); 
    bCancelChat.setOnClickListener(this); 

    loadAllContacts(); 
    sortedNames = new String[phoneNameNumber.length]; 
    Integer iIndex = 1; 
    while (keepRunning2){ 
     if(phoneNameNumber[iIndex][0].equals(phoneNameNumber[iIndex-1][0])){ 
      keepRunning = true; 
      tmp = new String[phoneNameNumber.length-1][2]; 
      int k = 0; 
      int t=0; 
      while(keepRunning){ 
       Log.wtf("running",String.valueOf(iIndex) + ":" + String.valueOf(k)); 
       tmp[t][0] = phoneNameNumber[k][0]; 
       tmp[t][1] = phoneNameNumber[k][1]; 
       if(k!=i-1){ 
        k++; 
        t++; 
       }else{ 
        k=k+2; 
        t++; 
       } 
       if(t==tmp.length-1) 
        keepRunning=false; 


      } 
      phoneNameNumber = null; 
      phoneNameNumber = new String[tmp.length][2]; 
      phoneNameNumber = tmp; 
     } 
     if(!(iIndex < phoneNameNumber.length-1)){ 
      keepRunning2 = false; 
     } 
     iIndex++; 
    } 

    for(int i = 0;i<phoneNameNumber.length;i++){ 
     sortedNames[i] = phoneNameNumber[i][0]; 
    } 
    ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,sortedNames); 
    spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 
    memberSpinner.setAdapter(spinnerAdapter); 
    memberSpinner.setOnItemSelectedListener(this); 



} 


@Override 
public void onClick(View v) { 
    switch (v.getId()){ 
     case R.id.bCreateChat: 
       if (!tchat_name.getText().toString().isEmpty()&&!members.isEmpty()) { 
        if(cBoxPrivateChat.isChecked()){ 
         createChat("name=" + tchat_name.getText().toString() + "&members=" + members + ScrollingActivity.Identifier + "#"); 
        } 
       } else{ 
        Toast.makeText(this,"Keine Daten Eingegeben",Toast.LENGTH_SHORT).show(); 
       } 
      break; 
     case R.id.bChatCance: 
      this.finish(); 
      break; 
     default: 
      if(v.getId() > 2000000){ 
       members = members.replace("#" + number.get(v.getId() - 2000000) + "#",""); 
       TextView delText = (TextView) findViewById(v.getId()); 
       delText.setVisibility(View.GONE); 
       memberCount = memberCount - 1; 
       if(memberCount <= 1){ 
        cBoxPrivateChat.setEnabled(true); 
       } 
      } 
    } 
} 

public void createChat(String Data){ 
    try { 
     Boolean b = new createNewCHat().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,ScrollingActivity.ServerLocation + "/alterChat.php", Data).get(); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } catch (ExecutionException e) { 
     e.printStackTrace(); 
    } 
    Intent intent = new Intent(getApplicationContext(), ScrollingActivity.class); 
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
    intent.putExtra("EXIT", true); 
    startActivity(intent); 
} 

@Override 
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 
    if(position != 0 && !cBoxPrivateChat.isChecked()) { 
     members = members + "#" + number.get(position) + "#"; 
     TextView MemberText = new TextView(this); 
     MemberText.setId(2000000 + position); 
     MemberText.setText(phoneNameNumber[position][0] + " " + phoneNameNumber[position][1]); 
     LinearLayout memberLayout = (LinearLayout) findViewById(R.id.membersLayout); 
     RadioGroup.LayoutParams params = new RadioGroup.LayoutParams(RadioGroup.LayoutParams.MATCH_PARENT, 100); 
     memberLayout.addView(MemberText, params); 
     MemberText.setOnClickListener(this); 
     memberCount++; 
     if(memberCount > 1){ 
      cBoxPrivateChat.setEnabled(false); 
      cBoxPrivateChat.setChecked(false); 
     } 
    } else if(position != 0 && cBoxPrivateChat.isChecked() && members.equals("")){ 
     members = members + "#" + number.get(position) + "#"; 
     TextView MemberText = new TextView(this); 
     MemberText.setId(2000000 + position); 
     MemberText.setText(phoneNameNumber[position][0] + " " + phoneNameNumber[position][1]); 
     LinearLayout memberLayout = (LinearLayout) findViewById(R.id.membersLayout); 
     RadioGroup.LayoutParams params = new RadioGroup.LayoutParams(RadioGroup.LayoutParams.MATCH_PARENT, 100); 
     memberLayout.addView(MemberText, params); 
     MemberText.setOnClickListener(this); 
     memberCount++; 
    } 
} 

@Override 
public void onNothingSelected(AdapterView<?> parent) { 

} 


public void loadAllContacts(){ 
    nameList = new ArrayList<String>(); 
    number = new ArrayList<String>(); 
    ContentResolver cr = getContentResolver(); 
    Cursor cursor = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, null, null, null); 
    if (cursor != null) { 
     try { 
      final int nameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); 
      final int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER); 


      nameList.add(0,"Select chat members"); 
      number.add(0,""); 

      while (cursor.moveToNext()) { 
       if(!cursor.getString(nameIndex).equals(lastContactName)){ 
        nameList.add(cursor.getString(nameIndex)); 
        number.add(cursor.getString(numberIndex)); 
        lastContactName = cursor.getString(nameIndex); 
       } 
      } 
     } finally { 
      cursor.close(); 

     } 
     phoneNameNumber = new String[nameList.size()][2]; 

     for(int i = 0; i<nameList.size();i++){ 
      phoneNameNumber[i][0] = nameList.get(i); 
      phoneNameNumber[i][1] = number.get(i); 
     } 

     Arrays.sort(phoneNameNumber, new Comparator<String[]>() { 
      @Override 
      public int compare(final String[] entry1, final String[] entry2) { 
       final String time1 = entry1[0]; 
       final String time2 = entry2[0]; 
       return time1.compareTo(time2); 
      } 
     }); 

    } 

} 
} 

はああとところで誰かが知っているのですか?なぜなら、私は単純にスピンナー上のリスト名を使用すると、重複はありません。

+0

while(keepRunning2){':' keepRunning2'が定義されていませんでした。 (そして、私はそれを想像することができますが、 'phoneNameNumber'も定義されていません。あなたはいくつかの例の値で偽のものを提供していて、削除したいと思います。ところで、もし私がこれに答えることさえしようとするならば、まず要素を消去できるリスト/マップ/セットではなく、なぜ配列を使うのかをまず知る必要があります。 – Ped7g

+0

プログラミング時に考慮すべき最も重要なことは、データの構造です。データの構造の例を教えてください。 –

+0

よく配列の仕組みを完全に理解していないので、私は 'phone name number'を3回割り当てます。最初の配列は古い配列を消去するためです(古いデータがまだそこに残っているのはなぜですか)。配列を初期化し、次に値を代入します。 実際に私は1週間学習していますか?いいえ 私はチュートリアルも講義も使用していません。少し知識があり、私が知っていたことからアプリを構築することに決めました。今度は学習を拡大しようとしています。 また、 – BAAAZINGA

答えて

0

ので、おそらくこのようないくつかの機能を求めていました:

(私はあなたのコードは、他のディメンションを使用しない理由も...「私は、重複があった場所を知る必要があり、」理解していませんでした?どのような方法で、唯一の[0]と比較されています...ので、私は私のコードでは、あまりにも、多分それはあなたが望んでいないことを模倣?)私はあなたのアイデアを与えるためにそれを少しコメントしようとした

import java.util.Arrays; // Maybe all of your problem is that you had typo in import? 
// This is language standard stuff, available everywhere IMHO 

    /** 
    * Removes duplicates and nulls from sorted array, first item (sortedArray[i][0]) is considered 
    * for duplicates search, rest of values sortedArray[i][1..n] is not considered at all. 
    * 
    * @param sortedArray 
    *   sorted 2D String array, can have any 1 <= length in second dimension. 
    * @return items with duplicate in [i][0] or null will be removed from result array. 
    */ 
    public static String[][] removeDuplicateOrNullItems(final String[][] sortedArray) { 
     // validate input 
     if (null == sortedArray || sortedArray.length <= 1) { 
      return sortedArray; // garbage in, garbage out 
     } 
     // search for first duplicate (if any) 
     int i = 0; 
     while (++i < sortedArray.length) { 
      if (null == sortedArray[i][0] || sortedArray[i][0].equals(sortedArray[i - 1][0])) { 
       break; 
      } 
     } 
     if (i == sortedArray.length) { 
      return sortedArray; // no duplicate found, return input 
     } 
     // First duplicate is at index i, now compact the rest of array content (overwriting dupes) 
     int newi = i; // will serve both as count of unique values, and compact-writing index 
     while (++i < sortedArray.length) { 
      if (null == sortedArray[i][0] || sortedArray[i][0].equals(sortedArray[i - 1][0])) { 
       continue; // another duplicate, skip this one 
      } 
      sortedArray[newi++] = sortedArray[i]; // valid value, compact it 
     } 
     return Arrays.copyOf(sortedArray, newi); // return the compacted result 
    } 

// demo how to call it: 

     String[][] in = {{"A1", "A2"}, {"A1", "dupA"}, {null, null}, {"B1", "B2"}, {"B1", "dupB1"}, {"B1", "dupB2"}, {"C1", "C2"}}; 
//  String[][] in = {{"A1", "A2", "A3"}, {"B1", "B2", "B3"}}; 
//  String[][] in = null; 

     String[][] out = removeDuplicateOrNullItems(in); 

何をしているのですか...デバッガを使用して行単位でステップ実行することは、何度か役立つかもしれませんが。あなたが本当にjava.util.Arraysを回避したい場合


は、最後return Arrays.copyOf...はこの置き換えてください:

final String[][] result = new String[newi][sortedArray[0].length]; 
    for (i = 0; i < newi; ++i) result[i] = sortedArray[i]; 
    return result; 

が追加さ:
3それらについての割り当てを:

私はあなたがすでにPROGについて知っているかわからない
 phoneNameNumber = null; 
     phoneNameNumber = new String[tmp.length][2]; 
     phoneNameNumber = tmp; 

これは難しいかもしれません。

phoneNameNumberString[][]タイプです。これは、配列が格納されているメモリに配置するアドレス(ポインタ)の一種です(次元数は重要ではありません)。

だから、最初に、新しい割り当てられたスペースに、nullにそれを設定し、そして最後にtmpが既に指している空間に(tmpと同じ配列を共有します)。 2番目の行は新しいメモリを割り当てても役に立たず、3番目の行ですぐに上書きされ、新たに割り当てられたメモリはGC(ガベージコレクタ)で使用できるようになります。

tmpの値をメモリ内の新しく割り当てられた領域(実コピー)にコピーする場合は、forループを実行し、phoneNameNumber[i][j] = tmp[i][j];を割り当てます。これはと共有し、String自体のポインタはJavaでOKです。Stringは不変です(値を変更できないため、1つの文字列のアドレスを保持すると、コードの他の部分がそのポインタで動作している場合でも変更されます)。あなたはそれをコピーするだけphoneNameNumber[i] = tmp[i];を行いたい場合

、それは新しいphoneNameNumberで同じ(値はtmpと同じになります一見になります。しかし、違いは、今、また、2つの文字列の内側を共有するphoneNameNumbertmpがありますそのようなコピーの後でtmp[0][1] = null;を実行すると、phoneNameNumber[0][1]には突然nullが含まれます(文字列へのポインタを格納するメモリ領域が共有されているため、両方の変数にnullポインタが書き込まれるため)

または、アレイ使用java.util.Arrays.copy***の方法をコピーする(真のコピー)。

nullに設定する最初の行は、実際にJavaプログラムで使用されることがあります。これは、GCのメモリーを「リリースされる」とマークするためです。あなただけの直接のtmp(GCの準備が古い値=を失うこと)を割り当てます場合は、すぐに別のポインタでphoneNameNumberを上書きするように、これは無意味です

は、GCはまた、元のメモリを収集します。実際にこのような場合は、中間のステップで古い値をnullにすると、2つの代入を1つではなく解決するため、JVM + GCが犠牲になり、結果は同じになります。

ご覧のとおり、元のアレイの一部をコピーして共有するかどうかは、どちらか簡単な方を気にしません。その方法は元の入力を変更する方法で書かれています。あなたはそのままそれを維持(と異なる配列で結果を受け取る)したい場合は、入力配列のコピーでそれを呼び出す必要があります、コピーしてそれを台無しにしてみましょう:
String[][] out = removeDuplicateOrNullItems(Arrays.copyOf(in, in.length));
EDIT:BUG ^^このcopyOfまたはin.clone()はディープコピーではなく、すべてのディメンションレベルでコピー を作成しますが、上位の配列のコピーを作成し、その間のすべてを共有します。

コードで例:

// INIT 

String[][] in = {{"A1", "A2"}, {"B1", "B2"}}; 
// in is array of 2 references (pointers) at String[] value. 
// Let's say [email protected] = [@r1, @r2] (at address @adr1 in memory) 
// @r1 = array of two references to Strings [@"A1", @"A2"], @r2 = [@"B1", @"B2"] 
String[][] inCopy = in.clone(); 
// inCopy has copy of top level array (content of [email protected]) at address @adr2 
// [email protected] = [@r1, @r2]; so this is in new memory, 
// but the values are old @r1 and @r2, sharing the second level arrays with "in" 

// modification CASE 1 

String[] x = {"X1", "X2"}; // [email protected] = [@"X1", @"X2"] 
in[0] = x; 
// [email protected] = [[email protected], @r2], so in = [[X1, X2], [B1, B2]] 
// [email protected] = [@r1, @r2], so inCopy = [[A1, A2], [B1, B2]] 

// modification CASE 2 (on deeper level, where clone/copyOf didn't create copy) 

in[1][0] = "X"; // @r2[0] modified to contain @"X"; 
// [email protected] = [[email protected], @r2], @r2 = [@"X", @"B2"], in = [[X1, X2], [X, B2]] 
// [email protected] = [@r1, @r2] (same @r2!), so inCopy = [[A1, A2], [X, B2]] 

あなたが見ることができるように、第二のケースでは、InCopyの中の修飾によって修飾されている[1] [0]。

String[][] in = {{"A1", "A2"}, {"B1", "B2"}}; 
String[][] inCopy = new String[in.length][in[0].length]; 
// inner arrays of inCopy are cloned to new memory, not shared with "in" 
for (int i = 0; i < in.length; ++i) { 
    inCopy[i] = in[i].clone(); 
} 

をしかし、あなたはそれがあなたの仮定の主要な間違いを作成することは難しいことではありませんことを、警告としてこれを取ることがあります。これを防ぐには、深さごとに多次元配列をコピーする必要があります。 :/常にあなたのコードをテストしてください:D(ユニットテストによる自動テストはそのような場合には完璧です)。また、最初の良いバージョンを書いた後は、デバッガをステップバイステップでステップアップすることを傷つけることはなく、意図したとおりに動作することを確認するだけでなく、多くのことを学ぶのに役立ちます。


通常、あなたがあなた自身のメソッドを作成しているとき、それは不変の値として入力に作用し、それらを修正しないために少し良いでしょう(理想的な世界では、私の方法は非常に最初に配列のコピーを作成しますそれの)。これにより、APIを呼び出して修正された配列だけを新しいコピーとして返し、入力された配列は変更しないと思ったときに、後で不測のバグが少なくなります。 (JavaのクラスDateは、私がUIのために保存された値を尋ねてきたときに私が気づかずに私を捕まえていることをうかがいます。例えば、日付が14日で失効するかどうかを調べるためにいくつかの計算を行います。 (私は元のメモリを計算してコピーしませんでした)。Stringは変更不可能なので、文字列を新しい値に変更すると、新しいポインタを受け取った可能性があります。それはまだ古いStringを見ています。=急いでいるプログラマーにとってはあまり驚くべきことではありません。

私は何を話しているのか理解していない場合は、どのようにコンピュータのメモリが動作するか、メモリの割り当てとは何か、Javaの場合、変数の格納方法found something here, looks like free lecture notes from uni course。プログラミングに慣れていない場合、q最初は混乱していましたが、コンピュータの仕組みやプログラミングに関する初期の書籍(1970-1990時代)などの子供向けの書籍を検索してください。JVMについて詳しく知りたいときには、 。

+0

おかげですごく助かりました。私は正直にそれを学びました(私の脳は記憶とコピーに変わりました。あなたは不変の値をどういう意味ですか? – BAAAZINGA

+0

簡潔に言えば、変更不可能なクラス - > 'String s =" hello ";のように値を使ってインスタンス化すると、Java Stringの特定のインスタンスはこれまでどおり変更されません。その文字列の一部の文字を変更すると、内部的に新しいメモリにコピーされ、変更され、Stringの新しいインスタンスが作成されます(凍結されます)。したがって、Stringで多くの変更を行うと、実際にかなりのメモリをコピーしているので、Javaで 'StringBuilder'が存在するのです。なぜC++の 'std :: string'をJavaの' String'に対してベンチマークするべきでないのですか?なぜなら、それは飛行機と車を比較するのと同じだからです。 – Ped7g

+0

@BAAAZINGA:immutables(String/BigDecimal/...)を使用すると、プログラマのエラー(古い値を予期していたり​​、古い値を期待したり、別の方法で使用するなど)が発生しにくくなり、ソースを読みやすく。マルチスレッド版を作成しやすくなりました。余分なコストは、これらのすべてのコピーオンライト操作のパフォーマンスです(避けられない場合)。これは通常、奇妙なバグを狩るコストと比較して、わずかなコストです。とにかく、インターネットを使用してください。 (私はコンピュータ(紙+ペン、週に4時間のHW)とインターネットなし(書籍のみ)のコードを学習していました。おそらく、より良い、少ない気晴らし。 :D – Ped7g

0

有用なライブラリがあるので、Javaは強力な言語です。 ArrayListを使用することをお勧めします。効率、性能、複雑さを向上させるためです。

これらのライブラリを使用せずにこの種の問題を解決したい場合は、これが本当に最高です。

// I don't know what's your phoneNameNumber, but I assume it is 2D string array: 
String[][] phoneNameNumber = new String[][]; 
String duplicatedIndicesString = ""; 

// Loop through phoneNameNumber 
for (int i=0; i<phoneNameNumber.length; i++) { 
    // Find duplicates 
    for (int i2=0; i<phoneNameNumber.length; i2++) { 
     boolean duplicated = true; 

     for (int j=0; j<phoneNameNumber[i2].length; j++) { 
      if (phoneNameNumber[i][j].equals(phoneNameNumber[i2].j) == false) { 
       // Not duplicate for this i2 
       duplicated = false; 
       break; 
      } 
     } 

     if(duplicated == true) { 
      duplicatedIndicesString += i2; 
      if (i2!=phoneNameNumber.length-1) { 
       // Store duplicated index for later dynamic allocation.. 
       duplicatedIndicesString += ","; 
      } 
     } 
    } 
} 

// Now let's copy results to a new array. 
String[] duplicatedIndicesStringArray = duplicatedIndicesString.split(","); 
String[][] resultPhoneNameNumber = new String[phoneNameNumber.length - dupicatedIndices.length][2]; 
int resultIndex = 0; 

for (int i=0; i<phoneNameNumber.length; i++) { 
    boolean duplicated = false; 

    for (j=0; j<duplicatedIndicesStringArray.length; j++) { 
     if (Integer.parseInt(duplicatedIndicesStringArray[j]) == i) { 
      duplicated = true; 
      break; 
     } 
    } 

    if (duplicated == false) { 
     resultPhoneNameNumber[resultIndex] = phoneNameNumber[i]; 
     resultIndex++; 
    } 
} 

これは私がしばらく書いた醜いコードでしょう。私はあまりにも多くの朝のコーヒーを取ったので、それをやった。コードする前に考えてみてください。

関連する問題