ラベル Java の投稿を表示しています。 すべての投稿を表示
ラベル Java の投稿を表示しています。 すべての投稿を表示

2013年6月7日金曜日

シャフラーズ問題の回答 @sys1yagi編

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

 CodeIQで結城 浩さんが出題していた断片情報からデータを復元しよう!に挑戦したので解答をポストします。結果は満点でした。よかつた。今回はすごく簡単でした。30分程度で書き散らかしたのでソースは汚いです。
 

問題


 問題は以下の様なデータから、 aa = 10とかいった風にペアのデータを突き止めろ、というモノでした。てっきりデータ数が膨大だったりするのかと思ってたら1000件程度。サクーッといけました。
 10 22 24 = aa bb cc
 53 33 10 = dd ee aa
 24 33 53 = bb ee dd
 

解答ソース


 上記フォーマットのデータファイルを食わすと、ペアを解析してprintlnする感じのソースです。最初データを見たとき、「連立方程式っぽいな」と思ったんでそういう感じで解くようにしました。
 10 22 24 = aa bb cc
 53 33 10 = dd ee aa
 24 33 53 = bb ee dd
というデータがあった時、aaに着目すると、1行目ではaaの候補は10, 22, 24になります。
んで、2行目と比較すると53, 33, 10のうち10だけが1行目に存在してます。つー事でaa=10だ!という事。そんだけです。
 10 22 24 = aa
 53 33 10 = aa
 ↓重複する数字だけ取り出す
 10 = aa
 あとはデータを読み込んでkey,valueのリストを保持するShufflerクラスのリストを作って、名前の一覧を抽出し、名前に着目しながら1件ずつ特定していく感じ。特定された名前と数字はShufflerクラスにフィードバックして、Shuffler内でそれらの値をリストから削除する、という構造です。今見返すと実装大分汚いですが、大体そんな感じです。

package jp.dip.sys1.shufflers;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Main {
    static class Shuffler {
        private List<String> mIdList = new ArrayList<String>();
        private List<String> mNameList = new ArrayList<String>();

        public Shuffler(String[] ids, String[] names) {
            for (String id : ids) {
                this.mIdList.add(id);
            }
            for (String name : names) {
                this.mNameList.add(name);
            }
        }

        public List<String> getIdList() {
            return mIdList;
        }

        public List<String> getNameList() {
            return mNameList;
        }

        public void found(String id, String name) {
            mIdList.remove(id);
            mNameList.remove(name);
        }
        public User getUser(){
            if(mIdList.size() == 1 && mNameList.size() == 1){
                User user = new User(mIdList.get(0), mNameList.get(0));
                found(mIdList.get(0), mNameList.get(0));
                return user;
            }
            return null;
        } 
        @Override
        public String toString() {
            if(mIdList.isEmpty() && mNameList.isEmpty()){
                return null;
            }
            StringBuilder sb = new StringBuilder();
            for (String id : mIdList) {
                sb.append(id + " ");
            }
            sb.append("= ");
            for (String name : mNameList) {
                sb.append(name + " ");
            }
            return sb.toString();
        }
    }

    static class ShufflerFactory {
        public static Shuffler createShuffler(String line) {
            if (line == null) {
                return null;
            }
            String[] pair = line.split(" = ");
            String[] ids = pair[0].split(" ");
            String[] names = pair[1].split(" ");
            Shuffler shuffler = new Shuffler(ids, names);

            return shuffler;
        }
    }

    static class User {
        private String mId;
        private String mName;

        public User(String id, String name) {
            mId = id;
            mName = name;
        }

        public String getId() {
            return mId;
        }

        public String getName() {
            return mName;
        }

        @Override
        public String toString() {
            return mId + " = " + mName;
        }
        @Override
        public int hashCode() {
            return toString().hashCode();
        }
        @Override
        public boolean equals(Object obj) {
            return this.hashCode() == obj.hashCode();
        }
    }

    static class Matcher {
        private String mTargetName = null;
        private List<String> mCandidateIds = new ArrayList<String>();
        private User mUser = null;

        public Matcher(String targetName) {
            mTargetName = targetName;
        }
        public boolean identify(Shuffler shuffler) {
            for (String name : shuffler.getNameList()) {
                if (mTargetName.equals(name)) {
                    if (mCandidateIds.isEmpty()) {
                        for (String id : shuffler.getIdList()) {
                            mCandidateIds.add(id);
                        }
                    } else {
                        List<String> newCandidateIds = new ArrayList<String>();
                        for (String id : shuffler.getIdList()) {
                            if(mCandidateIds.contains(id)){
                                newCandidateIds.add(id);
                            }
                        }
                        mCandidateIds = newCandidateIds;
                    }
                }
            }
            if(mCandidateIds.size() == 1){
                mUser = new User(mCandidateIds.get(0), mTargetName);
                return true;
            }
            else{
                return false;
            }
        }

        public User getUser() {
            return mUser;
        }
    }

    List<Shuffler> loadShufflers(String path) {
        List<Shuffler> list = new ArrayList<Main.Shuffler>();
        FileInputStream fis = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        try {
            fis = new FileInputStream(path);
            isr = new InputStreamReader(fis);
            br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null) {
                Shuffler shuffler = ShufflerFactory.createShuffler(line);
                if (shuffler != null) {
                    list.add(shuffler);
                } else {
                    System.out.println("error.");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                br.close();
                isr.close();
                fis.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return list;
    }

    List<String> treatName(List<Shuffler> list) {
        List<String> names = new ArrayList<String>();
        for (Shuffler shuffler : list) {
            for (String name : shuffler.getNameList()) {
                if (!names.contains(name)) {
                    names.add(name);
                }
            }
        }
        System.out.println(names.size());
        return names;
    }

    public void matching(String path) {
        List<Shuffler> list = loadShufflers(path);
        List<User> users = new ArrayList<Main.User>();
        List<String> names = treatName(list);
        for (String name : names) {
            Matcher matcher = new Matcher(name);
            for (Shuffler shuffler : list) {
                if (matcher.identify(shuffler)) {
                    break;
                }
            }
            User user = matcher.getUser();
            if (user != null) {
                if(!users.contains(user)){
                    users.add(user);
                }
                for (Shuffler shuffler : list) {
                    shuffler.found(user.getId(), user.getName());
                }
            }
        }
        for(Shuffler shuffler : list){
            User user = shuffler.getUser();
            if(user != null){
                if(!users.contains(user)){
                    users.add(user);
                }
                for(Shuffler shuffler2 : list){
                    shuffler2.found(user.getId(), user.getName());
                }
            }
        }
        Collections.sort(users, new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return Integer.parseInt(o1.getId()) - Integer.parseInt(o2.getId());
            }
        });
        for (User user : users) {
            System.out.println(user);
        }
        List<String> remains = new ArrayList<String>();
        for(Shuffler shuffler : list){
           String remain = shuffler.toString();
           if(remain != null){
               if(!remains.contains(remain)){
                   remains.add(remain);
               }
           }
        }
        for(String remain : remains){
            System.out.println(remain);
        }
    }
    public static void main(String[] args) {
        Main main = new Main();
        //main.matching("shufflers/sample.txt");
        main.matching("shufflers/shufflers.txt");
    }
}

2013年5月24日金曜日

ピッグデータ問題の解答 @sys1yagi編

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
 先日CodeIQで結城 浩さんが出題していた《ピッグデータ》に負けないで! に挑戦したのでその解答をポストします。以下の内容はほぼ提出した解答そのままです。4/30の夜中に挑戦を開始して、翌日5/1の夕方辺りに解答を提出しました。
 
 結果は正解!「評価5 ベスト・ピッグデータ賞(結果が正しく技術メモも十分な解答)」だそうです。わーい。結城さんの解答も同じ考え方だったので二度わーい。

はじめに


 本書は、「ピッグデータ問題」のドキュメントである「problem.txt」、「pigdata.pdf」を読み、ピッグデータの仕様について理解している事を前提とした技術メモです。「ピッグデータ問題」の詳細については「《ピッグデータ》に負けないで! https://codeiq.jp/ace/yuki_hiroshi/q303」を参照して下さい。

利用言語、環境について


 言語はjavaを利用する事にしました。標準ライブラリにSHA-1によるハッシュ計算を行う事が出来るMessageDigestクラスが存在する点と、マルチスレッド処理が容易な点、実行速度がスクリプト言語より優位と考えられる点から選択しました。
 実行の環境は以下の通りです。

  • OS: OS X 10.8.3(Mountain Lion)
  • CPU: 1.8 GHz Intel Core i7 (4コア)
  • メモリ: 4GB
  • java: 1.8.0-ea (1.6系、1.7系でも問題なく動作は可能のはず。たまたま入っていたのでそのまま利用した)


  • getsign(107374182400, 16777216)の答え


    209679232

    処理概要


     getdataでは、SHA-1によるハッシュ計算のインプットとなるqがindexを10で除算した値である事から、qが変化するまでハッシュ値を保存する事によってハッシュ値計算の回数が減少する様にしています。getsignではExecutorServiceを利用しマルチスレッドによって同時に計算処理をする構造になっています。getsignで必要な指定count数のピッグデータの取得とソートについては、65536(16bit)個のlong配列を使って、getdataで得られた値の個数をカウントする方式としました。この処理方式に至った経緯の詳細については"処理詳細と工夫について"を参照して下さい。

    処理詳細と工夫について


    オンメモリの処理ではメモリが足りない問題


     当初getsignでは、与えられたcountの個数だけgetdataを実行し、結果をArrayListに格納していました。count個の値が出揃った後ArrayListをソートし、skipsに従ってシグネチャを計算しました。getsign(100, 10)の場合は問題無く動作しましたが、count個のデータをArrayListに格納するので、getsign(107374182400, 16777216)では当然メモリが足りなくなります。この方法ではgetsign(107374182400, 16777216)は計算出来ない事が分かりました。

    データを保存する場合2600GB必要になる


     オンメモリで大規模なgetdataの値を保持する事は困難である事から、外部記憶装 置にindexから得られるピッグデータを保存する事を考えました。後ほどソートが必要となる為、データは以下の様なリンクドリストの性質を持つ構造にしました。

    index(64bit), ピッグデータ(16bit), 前のindex(64bit), 次のindex(64bit)

     1データ辺り64+16+64+64=208bit=26byteとなり、これを107374182400個保存する場合、107374182400*24=2600GBの外部記憶装置が必要となります。2600GB=2.6TBは今ではそこまで大きなサイズとは言えませんが、2.6TBの読み書きとなると膨大な時間がかかる事が予想されます。その他、リンクドリストという構造上skipsとkに従ったシグネチャ計算を行う場合全データにシーケンシャルにアクセスする必要があったり、データサイズの大きさから実行可能な環境に大きな制約を与える事になります。この方式も見送る事にしました。

    ピッグデータが16bitである点に着目


     ピッグデータはpigdata.pdfの仕様から、符号なしの16bitの整数です。つまり0-65535の範囲の計65536種類という事になります。そこで、65536個の配列を作成し、getdataから返されたピッグデータを添字にして以下の様にgetdataから得られたピッグデータの取得回数を数え上げる事で省メモリ化出来ると考えました。

    int[] counter = new int[65536];
    int data = getdata(i);
    counter[data]++;
    

     また、この方式によりgetdataで得た一連の値のsortが不要となります。counterにアクセスする為の添字はgetdataで得られるピッグデータで、その添字でアクセスするcounterの値はgetdataで得られたピッグデータの回数を表します。添字を0から65535まで数え上げながら個数を足し合わせる事でソート済みのgetdataの配列と同等の情報が得られます。以下に例を示します。

    例:
    得られた値
      20536 6446 24555 213 9583 33333 24555
    

    counterの内部
     counter[213] = 1
     counter[6446] = 1
     counter[9583] = 1
     counter[20536] = 1
     counter[24555] = 2
     counter[33333] = 1
    

    ソート済み配列として扱う
      213 6466 9583 20536 24555 24555 33333
    

     この構造により、シグネチャを計算する為のskipsとkがアクセスする添字の位置のピッグデータを取得する事が出来ます。上記の例のデータを用いると、k=4の場合はcounter内の値を足し合わせて4を越えた時の値(>4)である24555が得られる値となります。このデータ構造の場合、各ピッグデータの元のindexデータは消失します。しかしシグネチャ計算においてはソート済みのピッグデータだけが必要となるので問題ないと判断しました。

    マルチスレッド化


     実行環境のPCはCPUのコアが4つあったので、getsignの計算をマルチスレッド化する事によって効率化を図りました。与えられた範囲のgetdataで得られた個数をカウントするCounterクラスを作成し、ExecutorServiceで任意の個数のスレッドを実行する様にしました。計算する値は1スレッド当たりcount/スレッド総数個とし、全てのスレッド計算が終わったあと、カウント用配列(65536個の配列)をマージする事でcount個のカウントデータが得られる様にしました。

    SHA-1がボトルネックとなる


     getdataのソート情報は省メモリで得られる様になりましたが、107374182400回SHA-1のハッシュ計算をするのは大きな負荷です。処理時間の殆どがSHA-1のハッシュ計算に使われてるので、この部分を最適化する事で高速化を図れると考えられます。処理概要にも記述した通り、getdataではSHA-1によるハッシュ計算のインプットとなるqがindexを10で除算した値である事から、index〜index+9の範囲でqは同じになります。そこでqが変化するまでハッシュ値を一時的に保存する事によってハッシュ値計算の回数を減らす様にしました。これによりSHA-1のハッシュ計算の回数が1/10となりました。前述した実行環境においては従来10時間の実行時間だったものが1時間まで短縮する事が出来ました。


    ソース


    package jp.dip.sys1.pigdata;
    
    import java.security.MessageDigest;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class Main {
        public final static int SIZE_OF_16 = 65536;
        public static int THREAD_COUNT = 1;
        static class Count {
            long[] mCount = new long[SIZE_OF_16];
    
            public long[] getCount() {
                return mCount;
            }
    
            public void merge(long[] src) {
                for (int i = 0; i < src.length; i++) {
                    mCount[i] += src[i];
                }
            }
        }
    
        static class Counter implements Callable<Count> {
            protected long mBegin;
            protected long mEnd;
            protected byte[] mDigest;
            protected long mPrevQ;
            protected MessageDigest mMessageDigest;
    
            public Counter(long begin, long end) {
                try {
                    mBegin = begin;
                    mEnd = end;
                    mPrevQ = -1;
                    mMessageDigest = MessageDigest.getInstance("SHA-1");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
            private byte[] toSHA1(String source) {
                mMessageDigest.update(source.getBytes());
                return mMessageDigest.digest();
            }
    
            public int getdata(long index) {
                long q = index / 10;
                long r = index % 10;
                if (mPrevQ != q) {
                    mDigest = toSHA1(String.valueOf(q));
                }
                mPrevQ = q;
                int upper16 = (0xff & mDigest[(int) r * 2]) << 8;
                int lower16 = 0xff & mDigest[(int) r * 2 + 1];
                return upper16 + lower16;
            }
    
            @Override
            public Count call() throws Exception {
                Count count = new Count();
                long[] countData = count.getCount();
                for (long i = this.mBegin; i < this.mEnd; i++) {
                    int data = getdata(i);
                    countData[data]++;
                }
                return count;
            }
        }
    
        public static long getsign(final long count, final long skips) {
            if (count < 1 || skips < 1) {
                return -1;
            }
            int threadCount = THREAD_COUNT;
            ExecutorService executor = Executors.newFixedThreadPool(threadCount);
            List<Future<Count>> futures = new ArrayList<Future<Count>>();
            for (int i = 0; i < threadCount; i++) {
                long begin = (count / threadCount) * i;
                long end = (count / threadCount) * (i + 1);
                if (i + 1 >= threadCount) {
                    end += count % threadCount;
                }
                futures.add(executor.submit(new Counter(begin, end)));
            }
            Count resultCount = new Count();
            for (Future<Count> future : futures) {
                try {
                    Count c = future.get();
                    resultCount.merge(c.getCount());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            long[] countData = resultCount.getCount();
            long result = 0;
            for (long k = 0; k < count; k += skips) {
                long index = 0;
                for (int l = 0; l < SIZE_OF_16; l++) {
                    index += countData[l];
                    if (index > k) {
                        result += l;
                        break;
                    }
                }
            }
            executor.shutdownNow();
            return result;
        }
    
        public static void main(String[] args) {
            long start = System.currentTimeMillis();
            System.out.println(getsign(107374182400L, 16777216L));
            System.out.println(System.currentTimeMillis() - start + "ms");
        }
    }
    

    おわりに


     おもろかった。

    2012年8月23日木曜日

    [Java] ヒープソート 遅い

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

    Javaでヒープソート。
    もう見なくてもヒープソートも実装できる様になったわー。
    この実装は再起を使っているので遅いらしい。
    次はHaskellでやる。

    2011年12月30日金曜日

    Chain Of ResponsibilityパターンによるFizzBuzz実装

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    出落ち感満載ですが、実装してみました。
    単純にGoFデザインパターンの一つであるChain Of Responsibilityパターンを使ってみたかっただけです。
    今まで実際に使った事が無かったので・・・。

    Chain Of Responsibilityパターンって?


    ggrks。
    Wikipedia:Chain of Responsibility パターン
    簡単に言うと責任の鎖。「ちょっと責任者に代わって。君じゃ埒があかん。」とこういう事ですね。


    実装


    ChainOfResponsibilityクラスのインナークラスとして全て書いています。
    Fizz, Buzz, FizzBuzz, Integersクラスは抽象クラスであるHandlerを継承しています。
    ChainOfResponsibility#fizzBuzz()の中でsetNextしてお互いを連結しています。

    Handler#fizzBuzz(int)で実行。inCharge(int)で自分がこの値を担当しているかどうかをbooleanで返します。担当であればpring(int)を実行して終了。担当じゃなかったら自分の次の人のHandler#fizzBuzz(int)を呼び出します。

    例えば65という値が入れられた場合はFizzBuzz->Fizz->Buzzまで行ってprintされます。

    なんとなくFizzBuzzクラスやIntegersクラスのinCharge(int)はFizzクラスとBuzzクラスを使って判定させてます。
    連結の仕方によって計算量が変わる気がしますがなんだか面倒なので考えるのをやめました。
    もっと短く出来ると思いますが面倒なので(ry

    public class ChainOfResponsibility {
        static public abstract class Handler{
            private Handler next = null;
           
            public Handler setNext(Handler next){
                this.next = next;
                return next;
            }
            public final void fizzBuzz(int value){
                if(inCharge(value)){
                    print(value);
                }else if(next != null){
                    next.fizzBuzz(value);
                }else{
                    throw new RuntimeException("Oops!");
                }
            }
            protected abstract boolean inCharge(int value);
            protected abstract void print(int value);
        }
        
        static class Integers extends Handler{
            Fizz fizz = new Fizz();
            Buzz buzz = new Buzz();
            @Override
            protected boolean inCharge(int value) {
                return !fizz.inCharge(value) && !buzz.inCharge(value);
            }
            @Override
            protected void print(int value) {
                System.out.println(value);
            }
        }
        static class Fizz extends Handler{
            @Override
            protected boolean inCharge(int value) {
                return value%3 == 0;
            }
            @Override
            protected void print(int value) {
                System.out.println("Fizz");
            }
        }
        static class Buzz extends Handler{
            @Override
            protected boolean inCharge(int value) {
                return value%5 == 0;
            }
            @Override
            protected void print(int value) {
                System.out.println("Buzz");
            }
        }
        
        static class FizzBuzz extends Handler{
            Fizz fizz = new Fizz();
            Buzz buzz = new Buzz();
            @Override
            protected boolean inCharge(int value) {
                return fizz.inCharge(value) && buzz.inCharge(value); 
            }
            @Override
            protected void print(int value) {
                System.out.println("FizzBuzz");
            }
        }
        
        public void fizzBuzz(){
            Handler integer = new Integers();
            Handler fizz = new Fizz();
            Handler buzz = new Buzz();
            Handler fizzBuzz = new FizzBuzz();
            
            fizzBuzz.setNext(fizz).setNext(buzz).setNext(integer);
            for(int i = 1; i <= 100; i++){
                fizzBuzz.fizzBuzz(i);
            }
        }
        
        public static void main(String[] args) {
            new ChainOfResponsibility().fizzBuzz();
        }
    }
    

    まとめ


    このパターンはいろいろ使えそうだなぁ。
    デザインパターンによるFizzBuzz実装をシリーズ化できるかなー。どうかな。

    2011年11月9日水曜日

    JavaによるErrorSortの実装

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
    また画期的なソートアルゴリズムが出ましたね。
    [Python]SleepSort、BogoSortに続く画期的なソートアルゴリズム、ErrorSortを考えた
    で、こういうネタソート、何か作れないかなーと思って考えていたら、「あっ、そうだ。エラーを使ってソートすればいいじゃないか」と思って実装してみた。

    面白いので早速Javaで実装してみました。


    ErrorSort


    ソートはintでやる事になるのでTをintに変換するインタフェースを設けました。
    try-cacthの部分で ToインタフェースのIntメソッドを呼び出してTのint表現をもらって配列を作り、
    インクリメントされていくindexで配列にアクセスしてIndexOutOfBoundsExceptionを起こさせています。

     public interface To<T> {
      public int Int(T t);
     }
     public static <T> List<T> errorSort(List<T> list, To<T> to){
      List<T> copy = new ArrayList<T>(list);
      List<T> result = new ArrayList<T>();
      int size = copy.size();
      int count = 0;
      int index = 0;
      WHILE:while(count < size){
       for(T t : copy){
        try{
         (new byte[to.Int(t)])[index] = 0;
         }catch(IndexOutOfBoundsException e){
         result.add(t);
         copy.remove(t);
         count++;
         continue WHILE;
        }
       }
       index++;
      }
      return result;
     }
    


    実行


    以下の様に使います。

     public static void main(String[] args){
      List<Integer> list = Arrays.asList(new Integer[]{2,4,60,3,23,44});
      list = errorSort(list, new To<Integer>(){
       @Override
       public int Int(Integer t) {
        return t;
       }
      });
     }
    

    結果
    2,3,4,23,44,60,


    結論


    まだまだいろんなソートアルゴリズムがありそうですね。

    2011年6月21日火曜日

    Java脳でもわかるObjective-C入門

    このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

    photo: Some rights reserved by yukiv


    Javaと比較しながら「へぇーObjective-Cってそういう感じか」と理解した気になろう。

    流れ




    軽くジャブ


    まずは以下のソースを眺める

    //SampleClass.h
    #import <Foundation/Foundation.h>
    #import "SampleClassDelegate.h"
    
    @interface SampleClass : NSObject <sampleclassdelegate>{
     int index;
     NSString *str;
    }
    @property(assign) int index;
    @property(nonatomic, retain) NSString *str;
    
    +(void) dump : (SampleClass*) instance;
    -(void) setData : (NSString*) str  str: (NSString*) number;
    -(void) setData : (NSString*) str  num: (NSInteger) number;
    -(NSString*) toString;
    
    @end
    
    //SampleClass.m
    #import "SampleClass.h"
    @implementation SampleClass
    @synthesize index;
    @synthesize str;
    +(void) dump:(SampleClass *)instance{
     NSLog(@"%@", [instance toString]);
    }
    -(void) setData : (NSString*) text  str: (NSString*) number{
     self.str = text;
     self.index = [number intValue];
    }
    -(void) setData : (NSString*) text  num: (NSInteger) number{
     self.str = text;
     self.index = number;
    }
    -(NSString*) toString{
     return [NSString stringWithFormat:@"%@:%d", self.str, self.index];
    }
    //delegate
    -(NSString*) toStringDao{
     return [NSString stringWithFormat:@"%@:%d%@", self.str, self.index, @"だお"];
    }
    @end
    
    さすがのSyntaxHighlighterさんもObjective-Cはサポートしてねーって事でC++でハイライト。
    ハイライトしなさ杉。
    うんざりする見た目だけど、これから説明する事を読めばスラスラ理解できるようになりますよ!



    クラスファイルの構成


    Javaなら"クラス名.java"なわけですが、Objective-CはC言語の上に乗ってる言語なので、
    その辺はCの仕組みに準拠します。
    C/C++同様にヘッダファイルと実装ファイルに分かれてます。分けなくてもいいけど分けた方がいいので分けてます。
    XCodeでクラスを新規作成した場合は以下の2ファイルが作成されます。
    "クラス名.h" //ヘッダ
    "クラス名.m" //実装ファイル

    まーCです。



    コメント

    全く何もかも同じ

    ■Java

    //コメント
    /* コメント */
    

    ■Objective-C

    //コメント
    /* コメント */
    


    import文


    Objective-CはC言語と違って#includeではなく#importを使うようになっています。
    違いは二重includeを自動的に防いでくれるだけです。#ifndefとかそういう制御がいらんという事です。
    Javaしか知らない人はこの辺は気にしなくていいです。#importって書いておけばいいんです。

    ■Java
    import java.lang.string;
    

    クラスパスが通っていれば読み込めます。


    ■Objective-C
    #import <UIKit/UIKit.h>
    #import "Moge.h"
    

    上の書き方は標準ライブラリから探す感じ(ほんとは違います)
    下の書き方はプロジェクトの中から探す感じっぽい雰囲気。多分



    変数の定義


    概ねJavaと同じです。



    ■Java
    int i = 10;
    

    ■Objective-C
    int i = 10;
    


    参照


    クラスオブジェクトは必ず参照型です。Javaと同じですね。
    書き方は"クラス名*"です。

    ■Java
    Object object;
    

    ■Objective-C
    NSObject *object;
    


    id型


    javaでいう処のObject型。
    どんなオブジェクトでも入れられる型です。生ポインタという認識で大体いいかもしれません。
    クリックイベントなどの引数でsenderをidで渡すといった形で使われます。

    ■Java
    String object1 = new String("あはは");
    Object object2 = object1;
    

    ■Objective-C
    NSObject *object1 = [[NSObject alloc] init];
    id object2 = object1;
    

    ちなみにidにプリミティブ型を突っ込むと、警告が出ます。
    Javaだとコンパイルエラーになりますね。



    クラス定義


    Javaと違って、Objective-Cではまずクラスを宣言します。その後実装コードを書きます。
    一般的には宣言はヘッダに、実装は実装ファイルに書いていきます。
    また、宣言と実装で宣言子が異なります。

    宣言


    ■Java
    宣言のみとかってねーよ。(ないよね?)


    ■Objective-C
    @interface Moge : NSObject{
    //メンバ変数宣言
    }
    //メソッド宣言
    @end
    
    キモイんですが、 @interface クラス名 : スーパークラス名で始まり
    { }の中にメンバ変数を宣言し、その後メソッドを宣言、最後に @end で閉じます。
    メンバやメソッドは宣言のみで初期化や実装をここで書くことは出来ません。
    この宣言を書いたヘッダファイル(.h)を実装ファイル(.m)でimportして実装を行います。


    実装


    ■Java
    public class Moge{
    
    }
    

    ■Objective-C
    #import "Moge.h"
    @implementation Moge
    //メソッドの実装などなど
    @end
    
    ヘッダをimportし、そこに宣言されたクラスを実装します。
    宣言子は@implementationです。
    ヘッダで宣言したメソッドの実装をガリガリ書きます。(実際の実装例はもうちょい下でやります)



    メソッドの宣言、実装


    メソッドには二種類あります。クラスメソッドとインスタンスメソッドです。
    Javaでいう所のstaticメソッドとメンバメソッドですね。一緒ですね。
    ヘッダのクラス宣言の所で各メソッドを宣言していきます。
    ちなみにヘッダで宣言したメソッドは全てpublicになります。
    privateなメソッドはmファイル内に実装だけいきなり書くことで実現する事が出来ます。


    ■宣言の仕方!


    まぁキモイんですが慣れです。
    -(void) moge : (NSString*) str isSave : (BOOL) isSave;
    
    そうです、":"で色々区切ってます。":"の後に引数を書いていくイメージですね。
    最初見たとき「えっ」ってなりました。意味不明ですよねこれ。
    つまりこういう事です

    -(戻り値の型) メソッド名 : (引数の型) 引数名 ラベル名 : (引数の型) 引数名

    さて気になるのが「ラベル名」です。これ実はいりません。なくてもビルド通ります。
    どういう時に使うかっていうとオーバーロード的な事をしたいときに使うんですね。
    Javaなら勝手にやってくれるってーのにねー。やれやれですね。

    -(void) moge : (NSString*) str isSave : (BOOL) isSave;
    -(void) moge : (NSString*) str num : (NSInteger) number;
    
    呼び出し側でラベル名を指定しながら引数をぶっこむ事でメソッドの呼び分けができるわけですね。
    オーバーロードの際はラベル名付けて無いとコンパイル時に怒られます。

    さて勘のいい方は気がついたかもしれません。
    メソッド宣言の先頭に"-"とかいって毛が生えていました。
    これ気のせいじゃないです。"-"には意味があります。
    先頭の記号はメソッドの種類を現しています。



    クラスメソッド


    Javaで言うところのstaticなメソッド。
    クラスをインスタンス化しなくても使えます。

    ■Java

    public static void moge(String str, Boolean isSave){}
    


    ■Objective-C

    +(void) moge : (NSString*) str isSave : (BOOL) isSave;
    
    先頭を"+"にするとクラスインスタンスである、という事になります。


    ■インスタンスメソッド


    いわゆる普通のメソッド。

    ■Java

    public void moge(String str, Boolean isSave){}
    


    ■Objective-C

    -(void) moge : (NSString*) str isSave : (BOOL) isSave;
    



    メッセージ式


    基本的に見た目がキモいだけです。色々と高尚な何かがあるみたいですけど、気にしなくてもいいです。

    ■Java

    Integer num = new Integer(120);
    num.toString();
    


    ■Objective-C

    NSNumber* num = [NSNumber numberWithInt:120];
    [num stringValue];
    

    「メッセージ式」でググると色々出てくるので興味ある方はアレして下さい。
    引数付きのコンストラクタも特殊です。ていうかコンストラクタは無いです。
    allocの後に初期化メソッドを呼びます。引数付きのコンストラクタは引数付きの初期化メソッドで実現するんですねー。
    上の例ではallocすらしてませんね、ファクトリメソッド的な物っぽいですね。多分そうです。



    プロパティ


    Javaでいうgetter/setterを隠蔽してくれる感じの仕組みです。
    いろんな設定ができますがココでは省きます。

    ■Java

    public void setIndex(int index){
      this.index = index;
    }
    public int getIndex(){
      return this.index;
    }
    

    ■Objective-C

    プロパティも宣言と実装的なものが分かれます。

    ・宣言
    これはクラス宣言の{}の後に書きます。
    @property(オプション的な何か) 名前;

    こういう感じです。
    @interface Moge : NSObject{
     NSInteger index;
    }
    @property(assign, nonatomic) NSInteger index;
    @end
    


    ・実装的な感じ
    @synthesize index;
    

    上記の例だと自動的にgetter/setterが見えない所で実装されています。
    色々と他にも使い方がありますがとりあえずこれだけでおkな気がします。
    これによって以下の様な書き方ができます。

    NSInteger a = self.index;
    self.index = 10;
    
    内部的にはgetter/setterメソッドが実行されています。

    詳細はプロパティの宣言と実装とか読んで下さい。



    セレクタ


    いわゆるコールバックです。関数ポインタみたいなもんです。
    Javaで言うとjava.lang.reflect.Methodだなぁというのが個人的なイメージ。

    ■Java

    public class Moge{
      //コールバック
      public void callback(String str){
        System.out.println(str);
      }
      public void caller(Object target, Method callback)throws Exception{
        callback.invoke(target, "あはは");
      }
      public static void main(String[] args){
        try{
          Moge moge = new Moge();
          //リフレクションで取り出す
          moge.caller(moge,Moge.class.getMethod("callback", String.class));
        }catch(Exception e){
          e.printStackTrace();
        }
      }
    }
    

    ■Objective-C

    Objective-Cでは@selectorでメソッドを取りだす事ができます。
    取り出したメソッドはSEL型という型になります。
    コールバックを実現するメソッドは引数にセレクタを受け取って結果をセレクタに返してやります。
    メディア再生のフレームワークとか使う時にセレクタをセットしてメディアの再生ステータスの変更を受け取ったり、
    ボタンにセットしてクリックの通知を受ける時などに使います。

    @implementation Moge
    //コールバック
    -(void) callback:(NSString*) str{
      NSLog(@"%@", str);
    }
    -(void) caller:(id) target callback:(SEL) callback{
      NSInvocation* invoker = [NSInvocation invocationWithMethodSignature:[target methodSignatureForSelector:callback]];
      NSString* string = @"あはは";
      [invoker setTarget:target];
      [invoker setSelector:callback];
      [invoker setArgument:&string atIndex:2];
      [invoker invoke];
    }
    
    -(void) exec{
      //@selectorでcallback:(NSString*) strを渡す
      [self caller:self callback:@selector(callback:)];
    }
    @end
    



    プロトコル


    iOSではデリゲートと呼ばれるものです。Javaでいう所のインタフェースにかなり近いです。
    ていうか一緒です。

    ■Java
    //宣言
    public interface Cat{
      public String getBreed();
    }
    //実装
    public class Mike implements Cat{
      @Override
      public String getBreed(){
        return "tortoiseshell cat";
      }
    }
    


    ■Objective-C
    //プロトコルの宣言
    @protocol Cat
    -(NSString*) getBreed;
    @end
    
    //クラスの宣言
    @interface Mike : NSObject < Cat >
    //書かなくてもいけるよ
    @end
    
    //クラスの実装
    @implementation Mike
    -(NSString*) getBreed{
      return @"tortoiseshell cat";
    }
    @end
    

    クラス宣言の最後に< プロトコル名達 > でプロトコルの実装を宣言できます。
    プロトコルは複数実装できます。Javaのインタフェースと同じっすね。
    プロトコルはiOSではデリゲートとして頻繁に登場します。
    例えばWebView(UIWebView)。
    AndroidではWebViewClientを継承してshouldOverrideUrlLoadingを実装しますが、
    iOSではUIWebViewDelegateというプロトコルを実装してセットしてあげる感じになります。
    その他アプリケーションのライフサイクルの通知もデリゲートです。



    カテゴリ


    カテゴリはちょっと異質で、javascriptのプロトタイプ拡張みたいな雰囲気を持っています。
    Javaには無い機能です。超便利です。

    ■Java
    m9(^Д^)

    ■Objective-C
    書き方はクラスと大体同じです。

    @interface 拡張するクラス (カテゴリ名)

    例えば全てのクラスの基底であるNSObjectに適当にメソッドを追加するなら
    @interface NSObject (Unko)
    -(void) buriburi;
    @end
    
    こんな感じでヘッダに書き、普通に実装します。
    ファイル名は判りやすいように"NSObject+Unko.h"という風に作られる事が多いです。それしか見た事ないす。
    拡張したNSObjectが使いたい人は"NSObject+Unko.h"をimportするだけです。
    それだけでいつでもburiburiメソッドを呼べます。凄いっすね。
    Objective-Cじゃなきゃできません。

    もしObjective-Cじゃなかったら、そんなのObjective-Cじゃないんです



    文字列リテラル


    Objective-Cでの文字列リテラルでは、先頭に@を付ける必要があります。
    @"文字列"はNSStringのリテラル表現となります。

    ■Java
    String str = "moge";
    Object str2 = "hoge";
    

    ■Objective-C

    NSString* str = @"moge";
    id str2 = @"hoge";
    



    ログ


    ログは重要すなぁ。Javaでいう標準出力なんですが、ほんとに標準出力なのか怪しいのでログという事にしました。

    ■Java
    System.out.println("わーい");
    


    ■Objective-C
    NSLog(@"わーい");
    



    ひと通り使ってみる感じ


    では最後にこれらの機能をひと通り使うクラスなどを載せてみます。

    SampleClassDelegate.h
    //プロトコル(デリゲート)
    #import <UIKit/UIKit.h>
    
    @protocol SampleClassDelegate
    -(NSString*) toStringDao;
    @end
    

    SampleClass.h
    #import <Foundation/Foundation.h>
    #import "SampleClassDelegate.h"
    
    @interface SampleClass : NSObject <SampleClassDelegate>{
     int index;
     NSString *str;
    }
    @property(assign) int index;
    @property(nonatomic, retain) NSString *str;
    
    +(void) dump : (SampleClass*) instance;
    -(void) setData : (NSString*) str  str: (NSString*) number;
    -(void) setData : (NSString*) str  num: (NSInteger) number;
    -(NSString*) toString;
    
    @end
    

    SampleClass.m
    #import "SampleClass.h"
    @implementation SampleClass
    @synthesize index;
    @synthesize str;
    
    +(void) dump:(SampleClass *)instance{
     NSLog(@"%@", [instance toString]);
    }
    
    -(void) setData : (NSString*) text  str: (NSString*) number{
     self.str = text;
     self.index = [number intValue];
    }
    
    -(void) setData : (NSString*) text  num: (NSInteger) number{
     self.str = text;
     self.index = number;
    }
    
    -(NSString*) toString{
     return [NSString stringWithFormat:@"%@:%d", self.str, self.index];
    }
    //delegate
    -(NSString*) toStringDao{
     return [NSString stringWithFormat:@"%@:%d%@", self.str, self.index, @"だお"];
    }
    
    @end
    

    SampleClass+Suffix.h
    //カテゴリ
    #import "SampleClass.h"
    @interface SampleClass (Suffix)
    -(void) toStringSuffix:(id) target callback:(SEL) callback;
    @end
    

    SampleClass+Suffix.m
    #import "SampleClass+Suffix.h"
    @implementation SampleClass (Suffix)
    -(void) toStringSuffix:(id) target callback:(SEL) callback{
     NSInvocation* invoker = [NSInvocation invocationWithMethodSignature:[target methodSignatureForSelector:callback]];
     NSString* string = [NSString stringWithFormat:@"%@%@", self.str, @"っていう"];
     [invoker setTarget:target];
     [invoker setSelector:callback];
     [invoker setArgument:&string atIndex:2];
     [invoker invoke];
    }
    @end
    

    User.h
    //SampleClassを利用するクラス
    #import <Foundation/Foundation.h>
    #import "SampleClass.h"
    #import "SampleClass+Suffix.h"
    @interface User : NSObject {
    }
    -(void) exec;
    @end
    

    User.m
    #import "User.h"
    @implementation User
    
    -(void) exec{
     SampleClass* sample = [[SampleClass alloc] init];
     id<SampleClassDelegate> delegate = sample;
     
     NSNumber* num = [NSNumber numberWithInt:120];
     [num stringValue];
     
     
     [sample setData:@"一回目" num:100];
     NSLog(@"%@", [sample toString]);
    
     [sample setData:@"二回目" str:@"50"];
     NSLog(@"object:%@", [sample toStringDao]);
     NSLog(@"delegate:%@", [delegate toStringDao]); 
     //セレクタ
     [sample toStringSuffix:self callback:@selector(callback:)];
     
    }
    -(void) callback :(NSString*) str{
     NSLog(@"callback:%@", str);
     [str release];
    }
    @end
    
    

    実行結果:
    一回目:100
    object:二回目:50だお
    delegate:二回目:50だお
    callback:二回目っていう


    まとめ

    ね、簡単でしょ?