顯示具有 java 標籤的文章。 顯示所有文章
顯示具有 java 標籤的文章。 顯示所有文章

When to use Java String, StringBuffer, or StringBuilder?

在 Java 開發中,處理文字字串是最常見的操作之一。然而,許多初學者仍容易混淆 StringStringBufferStringBuilder 的使用時機。

選擇錯誤的工具不僅會影響程式的執行效率(Performance),在多執行緒環境下更可能導致難以追蹤的 Bug。為了幫助大家徹底釐清這三者的差異,以下整理了一份綜合比較表,從底層架構到實際應用一目了然。

比較特性 String StringBuffer StringBuilder
可變性 (Mutability) 不可變 (Immutable) 可變 (Mutable) 可變 (Mutable)
執行緒安全 安全 安全 (synchronized) 不安全
效能速度 最慢 居中 最快
修改方式 產生新物件 原地修改 (Buffer) 原地修改 (Buffer)
主要操作方法 concat(), split(), substring(), + append(), insert(), delete(), reverse() append(), insert(), delete(), reverse()
方法傳回值 傳回新 String 傳回 this 傳回 this
儲存位置 字串池 / Heap Heap Heap
引入版本 Java 1.0 Java 1.0 Java 1.5

總結與建議

總結來說,這三者的選擇邏輯非常清晰:

  • 如果處理的是少量數據,或者字串內容不會改變,請直接使用 String
  • 如果須進行大量字串拼接(例如迴圈中組裝 SQL 或 JSON),且僅在單一執行緒中運行,StringBuilder 是最佳選擇。
  • 只有在明確需要多執行緒共享同一個可變字串時,才考慮使用 StringBuffer

掌握以上細節,不僅能讓程式碼跑得更快,也能展現 Java 記憶體管理與並行控制的專業理解。希望這份表格對您的開發工作有所幫助!

quick ways to initialize a list of numbers or strings in java

Java 快速建立整數與字串清單的寫法

在 Java 中,建立 List<Integer>List<String> 是非常常見的需求。 以下依照不同 JDK 版本,整理出幾種快速建立可修改(modifiable) 清單的寫法,可依個人習慣彈性使用。

1️⃣ JDK 5(2004)起支援的寫法

自 JDK 5 起,Java 引入了 泛型(Generics)自動封裝(Autoboxing),因此可以直接將 int 自動轉為 Integer


  Integer[] intArray = {1, 2, 3};
  String[]  strArray = {"A", "B", "C"};

  List<Integer> ints =
    new ArrayList<Integer>(Arrays.asList(intArray));

  List<String> strings =
    new ArrayList<String>(Arrays.asList(strArray));
  
✅ 適用於舊版系統(JDK 5 / 6)
✅ 清楚明確,但型別宣告較冗長

2️⃣ JDK 7(2011)起支援的寫法(鑽石運算子)

JDK 7 引入了 鑽石運算子(<>, 可讓編譯器自動推斷泛型型別,使程式碼更精簡。


  List<Integer> ints =
    new ArrayList<>(Arrays.asList(1, 2, 3));

  List<String> strings =
    new ArrayList<>(Arrays.asList("A", "B"));
  
✅ 語法更簡潔
✅ 仍然回傳可修改的 ArrayList

3️⃣ Java 9(2017)起支援的寫法(搭配 List.of

Java 9 引入了 List.of() 作為集合工廠方法, 可快速建立不可修改的清單。 若實務上仍需要可修改的清單,可再包裝成 ArrayList


  List<Integer> ints =
    new ArrayList<>(List.of(1, 2, 3));

  List<String> strings =
    new ArrayList<>(List.of("A", "B"));
  
✅ 語意清楚、現代化寫法
⚠️ List.of() 本身不可修改,需額外包裝

✅ 小結

  • JDK 5+:可使用泛型與自動封裝
  • JDK 7+:可使用鑽石運算子,讓程式碼更乾淨
  • Java 9+:可搭配 List.of() 撰寫更語意化的程式碼

實務上建議:
👉 需要可修改清單 ⇒ 使用 new ArrayList<>(...)
👉 元素固定不變 ⇒ 直接使用 List.of()

Linked Lists from C to Java

C Pointer Concepts in Java」一文提到 Java 沒有指標型別 (pointer type) ,但有參照型別 (reference type) 的設計。在遇到須要處理鏈結清單 (linked list)、圖形 (graph) 等資料結構時,Java 如何透過參照型別,仍能達成類似的效果。本文將以「鏈結清單」為例,分別用 C 與 Java 實作,說明兩者的差異與對應。


🔹 C 語言:使用指標建立鏈結清單

/*
   1 -> 2 -> 3 NULL
*/
#include <stdio.h>
#include <stdlib.h>

// 定義節點結構
typedef struct Node {
    int data;  // 資料
    struct Node* next;  // 下一節點指標
} Node;

int main() {
    // 建立三個節點
    Node* head = (Node*)malloc(sizeof(Node));
    Node* second = (Node*)malloc(sizeof(Node));
    Node* third = (Node*)malloc(sizeof(Node));

    // 給值與連結
    head->data = 1;
    head->next = second;

    second->data = 2;
    second->next = third;

    third->data = 3;
    third->next = NULL;

    // 印出鏈結清單
    Node* current = head;
    while (current != NULL) {
        printf("%d -> ", current->data);
        current = current->next;
    }
    printf("NULL\n");

    return 0;
}
    其中,
  • Node* 是指標變數,指向記憶體中的節點。
  • 使用 malloc 配置記憶體,並用 -> 操作指標指向的內容。
  • 每個節點透過 next 指向下一個節點,形成鏈結。

🔹 Java 語言:使用參照建立鏈結清單

/*
   1 -> 2 -> 3 null
*/

// 定義節點結構
class Node {
    int data; // 資料
    Node next;  // 下一節點參照

    Node(int data) {
        this.data = data;
        this.next = null;
    }
}

public class LinkedListDemo {
    public static void main(String[] args) {
        // 建立三個節點
        Node head = new Node(1);
        Node second = new Node(2);
        Node third = new Node(3);

        // 給值與連結
        head.next = second;
        second.next = third;

        // 印出鏈結清單
        Node current = head;
        while (current != null) {
            System.out.print(current.data + " -> ");
            current = current.next;
        }
        System.out.println("null");
    }
}
    其中,
  • Node 是一個類別,變數如 headsecond 是參照變數,指向物件。
  • 使用 new 建立物件,並透過 . 操作物件的屬性。
  • 雖然語法上沒有 *&,但物件的參照本質上就像 C 的指標。

🔄 對照總結:C 指標 vs Java 參照

概念C 語言Java 語言
記憶體操作明確使用 *&隱含於物件參照
記憶體配置mallocnew
節點連結指標指向下一節點物件參照指向下一節點
安全性容易出現記憶體錯誤自動垃圾回收記憶體管理 (GC)

以上說明 Java 雖然沒有指標型別,但仍能透過「參照型別」的設計,實現如鏈結清單這類需要動態記憶體與節點連結的資料結構。希望對於從 C 過渡到 Java 的學習者有幫助。

How to interpret the caused by sections of a Java stack trace?

Java執行出錯丟出例外時,常會列印一串 Caused by 訊息,其格式為
   java.lang.Exception: Exception in xxx
       at ......... (....java: ..)
       .......
       at ......... (....java: ..)
   Caused by: java.lang.Exception: Exception in yyy
       at ......... (....java: ..)
       .......
       at ......... (....java: ..)
   Caused by: java.lang.Exception: Exception in zzz
       at ......... (....java: ..)
       .......
       at ......... (....java: ..)

這表示先有 zzz 錯誤,然後造成 yyy 錯誤,然後造成 xxx 錯誤。因此,最初錯誤原因為最後Caused by 指出的 zzz 錯誤。至於每個錯誤後面都會跟著很多 at,印出丟出例外當時的方法堆疊內容,越後面的 at 程式碼越早執行。


 public class CausedByExample {
    public static void main(String[] args) {
        try {
            method1();  // line 4
        } catch (Exception e) {
            // 此行指令表明 執行方法main出現例外 將列印丟出例外時的堆疊記錄內容
            e.printStackTrace();
        }
    }

    public static void method1() throws Exception {
        try {
            method2();  // line 13
        } catch (Exception e) {
            // 此行指令表明 執行方法1出現例外 是由 執行方法2的例外e造成,將列印
            // java.lang.Exception: Exception in method1
            //  逐層列印丟出方法1例外時的堆疊記錄內容
            throw new Exception("Exception in method1", e);  // line 18
            //  public Exception(String message, Throwable cause) 
            //  產生新例外,包含例外說明字串 message,及造成本例外的原因 cause
        }
    }

    public static void method2() throws Exception {
        // 此行指令表明 執行方法2出現例外,將列印
        // java.lang.Exception: Exception in method2
        //  逐層列印丟出方法2例外時的堆疊記錄內容
        throw new Exception("Exception in method2");  // line 26
    }
}

Output:

上面程式在method2產生例外,由method1接收,再包裝成原因產生新例外,由main接收,列印e.printStackTrace。其列印內容說明,Exception in method2 造成 Exception in method1

java.lang.Exception: Exception in method1
	at CausedByExample.method1(CausedByExample.java:18)
	at CausedByExample.main(CausedByExample.java:4)
Caused by: java.lang.Exception: Exception in method2
	at CausedByExample.method2(CausedByExample.java:26)
	at CausedByExample.method1(CausedByExample.java:13)
	... 1 more

how to solve the tower of hanoi by recursion versus simulated call stack?


/*
   TowerOfHanoi.java  遞迴版 及 模擬呼叫堆疊版 求解河內塔 

> java TowerOfHanoi
Hanoi Tower by Implicit Call Stack 遞迴版
A:[3, 2, 1], B:[], C:[]		1: Move disk 1 from A to C
        A:[3, 2], B:[], C:[1]		2: Move disk 2 from A to B
        A:[3], B:[2], C:[1]		3: Move disk 1 from C to B
A:[3], B:[2, 1], C:[]		4: Move disk 3 from A to C
A:[], B:[2, 1], C:[3]		5: Move disk 1 from B to A
        A:[1], B:[2], C:[3]		6: Move disk 2 from B to C
        A:[1], B:[], C:[3, 2]		7: Move disk 1 from A to C
A:[], B:[], C:[3, 2, 1]

Hanoi Tower by Explicit Stack 模擬呼叫堆疊版
A:[3, 2, 1], B:[], C:[]		1: Move disk 1 from A to C
        A:[3, 2], B:[], C:[1]		2: Move disk 2 from A to B
        A:[3], B:[2], C:[1]		3: Move disk 1 from C to B
A:[3], B:[2, 1], C:[]		4: Move disk 3 from A to C
A:[], B:[2, 1], C:[3]		5: Move disk 1 from B to A
        A:[1], B:[2], C:[3]		6: Move disk 2 from B to C
        A:[1], B:[], C:[3, 2]		7: Move disk 1 from A to C
A:[], B:[], C:[3, 2, 1]
*/
import java.util.Stack;

public class TowerOfHanoi 
{
    static Stack stackA = new Stack<>();  // 柱A
    static Stack stackB = new Stack<>();  // 柱B
    static Stack stackC = new Stack<>();  // 柱C
    static int nDisks = 5; // Number of disks 盤數
    static int count = 0;  // 步數
    static boolean printGoal = false;  // 列印目標否
    static boolean printOperation = true;  // 列印步驟否
    static boolean printStack = false;  // 列印模擬呼叫堆疊否

    // 印n格空白
    public static void printSpaces(int n)
    {
        for(int i=0; i <= n -1; i++) System.out.print(" ");
    }
    
    // 印柱A,柱B,柱C堆疊,前面n層內縮
    public static void printStacks(int n)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("A:" + stackA);
        sb.append(", B:" + stackB);
        sb.append(", C:" + stackC);

        System.out.println();
        printSpaces(n*8);  // 每層內縮8格
        System.out.print(sb.toString());
    }
    
    // 搬移柱from頂一個盤子到柱to
    public static void transfer(char from, char to)
    {
        if(from=='A' && to=='B') stackB.push(stackA.pop());
        if(from=='A' && to=='C') stackC.push(stackA.pop());
        if(from=='B' && to=='A') stackA.push(stackB.pop());
        if(from=='B' && to=='C') stackC.push(stackB.pop());
        if(from=='C' && to=='A') stackA.push(stackC.pop());
        if(from=='C' && to=='B') stackB.push(stackC.pop());
    }
    
    // 遞迴版解河內塔,將n個盤子從柱sourc,搬到柱target,透過柱auxiliary
    public static void solveHanoi(int n, char source, char auxiliary, char target) 
    {        
        if(printGoal) 
        {
            System.out.println();
            printSpaces((nDisks - n)*8);
            System.out.print(String.format("hanoi(n:%d,s:%c -> t:%c)",n,source,target));
        }

        if (n == 1) 
        {
            if(printOperation) 
                System.out.print(String.format("\t\t%d: Move disk 1 from %c to %c", ++count, source, target));

            transfer(source, target);            
        } 
        else 
        {
            solveHanoi(n - 1, source, target, auxiliary);

            printStacks(nDisks - n);            
            if(printOperation) 
                System.out.print(String.format("\t\t%d: Move disk %d from %c to %c", ++count, n, source, target));
            transfer(source, target);
            printStacks(nDisks - n);

            solveHanoi(n - 1, auxiliary, source, target);
        }
    }

    // 模擬呼叫記錄    
    static class HanoiCallRecord
    {
        int num;
        char source;
        char auxiliary;
        char target;
        int stage; // 0 for moving n-1 disks from source to auxiliary rods; 
                   // 1 for moving the disk n from source to target rods
                   //   and moving n-1 disks from auxiliary to target rods
                   // 2 for backtracking to the previous call record
        
        // 建構子
        public HanoiCallRecord(int num, char source, char auxiliary, char target)
        {
            this.num = num;
            this.source = source;
            this.auxiliary = auxiliary;
            this.target = target;
            this.stage = 0;  // 預設從階段0開始
        }
        
        // 列印呼叫記錄
        public String toString()
        {
            return String.format("(n:%d,s:%c,a:%c,t:%c,s:%d)",
                    num, source, auxiliary, target, stage);
        }
    }
    
    // 模擬呼叫堆疊,解河內塔,將n個盤子從柱sourc,搬到柱target,透過柱auxiliary
    public static void hanoiUsingStacks(int num, char src, char aux, char tgt) 
    {
        Stack stack = new Stack<>();
        
        HanoiCallRecord initial = new HanoiCallRecord(num, src, aux, tgt);
        stack.push(initial);  // 壓入第1層呼叫記錄

        while (stack.isEmpty()==false) 
        {
            if(printStack) System.out.print("\n" + stack);
            
            HanoiCallRecord current = stack.peek();  // 檢視本層呼叫記錄
            int n = current.num;
            char source = current.source;
            char auxiliary = current.auxiliary;
            char target = current.target;
            int stage = current.stage;
            
           if (n == 1) // 執行特別任務,然後退回上一層任務
           {
                if(printOperation) 
                    System.out.print(String.format("\t\t%d: Move disk 1 from %c to %c", ++count, source, target));

                transfer(source, target);
                stack.pop();  // 彈出本層呼叫記錄,
            } 
            else if(stage == 0) // 階段0, 執行本層第0階段任務
            {
                // solveHanoi(n - 1, source, target, auxiliary);
                HanoiCallRecord next = new HanoiCallRecord(n - 1, source, target, auxiliary);
                stack.push(next); // 壓入下層呼叫記錄
                current.stage++;  // 更新本層呼叫記錄的階段欄位
            }
            else if(stage == 1) // 階段1, 執行本層第1階段任務
            {
                printStacks(nDisks - n);            
                if(printOperation)
                    System.out.print(String.format("\t\t%d: Move disk %d from %c to %c", ++count, n, source, target));
                transfer(source, target);
                printStacks(nDisks - n);

                // solveHanoi(n - 1, auxiliary, source, target);
                HanoiCallRecord next = new HanoiCallRecord(n - 1, auxiliary, source, target);
                stack.push(next);  // 壓入下層呼叫記錄
                current.stage++; // 更新本層呼叫記錄的階段欄位
            }
            else if(current.stage == 2) // 階段2,本層任務完成,退回上一層任務
            {
                stack.pop();  // 彈出本層呼叫記錄,
            }
        }
    } 

    // 測試主程式
    public static void main(String[] args) 
    {
        nDisks = 3; // Number of disks
        count = 0;
        for(int i=nDisks; i >= 1; i--) stackA.push(i);
        
        System.out.print("Hanoi Tower by Implicit Call Stack 遞迴版");

        printStacks(0);
        solveHanoi(nDisks, 'A', 'B', 'C');
        printStacks(0);
        
        // ===================================
    
        count = 0;
        stackA.clear();
        stackB.clear();
        stackC.clear();
        for(int i=nDisks; i >= 1; i--) stackA.push(i);
                    
        System.out.print("\n\nHanoi Tower by Explicit Stack 模擬呼叫堆疊版");
        
        printStacks(0);
        hanoiUsingStacks(nDisks, 'A', 'B', 'C');
        printStacks(0);
    }
}    

how to query OSM data in Java?

開放街圖OSM的公共圖資可以透過overpass-turbo介面查詢獲得。
若想利用Java程式碼查詢,可參考以下查詢範例。
其中,查詢指令可參考overpass-turbo範例指令,測試成功再代入程式。


/*
  QueryOSM.java
        展示如何用retrofit套件,同步及非同步(適用於Android),存取如下OSM圖資服務
                http://overpass-api.de/api/interpreter?data=xxx

        // 建立服務連線客戶端及請求內容
        OverpassService requestClient = OverpassServiceProvider.get();
        String request = composeRequest();
        
        // 非同步查詢圖資,適用於手機平板Android平台
        asyncRequest(requestClient, request);
        
        // 同步查詢圖資,適用於桌機Application應用
        OverpassQueryResult result = syncRequest(requestClient, request);
        postProcess(result);
 
執行步驟:
  javac QueryOSM.java
  java QueryOSM
run:
[out:json][timeout:25];
(
node[tourism](25.1735, 121.446, 25.1775, 121.455);
relation[!highway][type!='route'][!boundary](25.1735, 121.446, 25.1775, 121.455);
);
out center;
end of asyncRequest()
21 elements...
1. id:4326372453, type:node, lat:25.1761808, lon:121.4490491, name:五虎碑, wheelchair:limited
2. id:4384988658, type:node, lat:25.1750071, lon:121.4522078, name:文錙藝術中心, wheelchair:yes
3. id:4492221396, type:node, lat:25.1736267, lon:121.4473809, name:三化牆
4. id:4492221397, type:node, lat:25.1744069, lon:121.4474037, name:地球村雕像, wheelchair:limited
5. id:4492221399, type:node, lat:25.1735175, lon:121.4484658, name:淡江大學花牆
6. id:4492221425, type:node, lat:25.1749949, lon:121.450678, name:閱讀的少女, wheelchair:yes
7. id:4502075211, type:node, lat:25.1751384, lon:121.4523232, name:旅者
8. id:4502075212, type:node, lat:25.1739938, lon:121.4505047, name:李雙澤紀念碑, wheelchair:yes
9. id:4502075213, type:node, lat:25.1761919, lon:121.4499374, name:福園金鷹銅雕, wheelchair:no
10. id:4502075222, type:node, lat:25.1738907, lon:121.4475716, name:驚聲銅像, wheelchair:limited
11. id:4507662408, type:node, lat:25.1741082, lon:121.4474671, name:溫馨, wheelchair:yes
12. id:5012978611, type:node, lat:25.1741784, lon:121.4507282, name:黃河母親, wheelchair:no
13. id:5072580167, type:node, lat:25.1769149, lon:121.4495309
14. id:5130535622, type:node, lat:25.1757202, lon:121.4496844, name:會文館, wheelchair:yes
15. id:5132288341, type:node, lat:25.1741586, lon:121.4508061
16. id:5132288342, type:node, lat:25.174208, lon:121.4475417
17. id:6050843218, type:node, lat:25.1770813, lon:121.449821
18. id:8991981256, type:node, lat:25.1750049, lon:121.4480033, name:淡江願景牆, wheelchair:yes
19. id:3974590, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:操場
20. id:3983402, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:松濤廣場
21. id:7530081, type:relation, lat:0.0, lon:0.0, type:multipolygon

Response{protocol=http/1.1, code=200, message=OK, url=http://overpass-api.de/api/interpreter?data=%5Bout%3Ajson%5D%5Btimeout%3A25%5D%3B%0A%28%0Anode%5Btourism%5D%2825.1735%2C%20121.446%2C%2025.1775%2C%20121.455%29%3B%0Arelation%5B%21highway%5D%5Btype%21%3D%27route%27%5D%5B%21boundary%5D%2825.1735%2C%20121.446%2C%2025.1775%2C%20121.455%29%3B%0A%29%3B%0Aout%20center%3B}
end of syncRequest()
21 elements...
1. id:4326372453, type:node, lat:25.1761808, lon:121.4490491, name:五虎碑, wheelchair:limited
2. id:4384988658, type:node, lat:25.1750071, lon:121.4522078, name:文錙藝術中心, wheelchair:yes
3. id:4492221396, type:node, lat:25.1736267, lon:121.4473809, name:三化牆
4. id:4492221397, type:node, lat:25.1744069, lon:121.4474037, name:地球村雕像, wheelchair:limited
5. id:4492221399, type:node, lat:25.1735175, lon:121.4484658, name:淡江大學花牆
6. id:4492221425, type:node, lat:25.1749949, lon:121.450678, name:閱讀的少女, wheelchair:yes
7. id:4502075211, type:node, lat:25.1751384, lon:121.4523232, name:旅者
8. id:4502075212, type:node, lat:25.1739938, lon:121.4505047, name:李雙澤紀念碑, wheelchair:yes
9. id:4502075213, type:node, lat:25.1761919, lon:121.4499374, name:福園金鷹銅雕, wheelchair:no
10. id:4502075222, type:node, lat:25.1738907, lon:121.4475716, name:驚聲銅像, wheelchair:limited
11. id:4507662408, type:node, lat:25.1741082, lon:121.4474671, name:溫馨, wheelchair:yes
12. id:5012978611, type:node, lat:25.1741784, lon:121.4507282, name:黃河母親, wheelchair:no
13. id:5072580167, type:node, lat:25.1769149, lon:121.4495309
14. id:5130535622, type:node, lat:25.1757202, lon:121.4496844, name:會文館, wheelchair:yes
15. id:5132288341, type:node, lat:25.1741586, lon:121.4508061
16. id:5132288342, type:node, lat:25.174208, lon:121.4475417
17. id:6050843218, type:node, lat:25.1770813, lon:121.449821
18. id:8991981256, type:node, lat:25.1750049, lon:121.4480033, name:淡江願景牆, wheelchair:yes
19. id:3974590, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:操場
20. id:3983402, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:松濤廣場
21. id:7530081, type:relation, lat:0.0, lon:0.0, type:multipolygon

end of main()
BUILD SUCCESSFUL (total time: 1 minute 2 seconds)

  參考:
  a. Retrofit 2 – Synchronous and asynchronous call example
        https://howtodoinjava.com/retrofit2/retrofit-sync-async-calls/
  b. https://github.com/zsoltk/overpasser
        hu.supercluster.overpasser.adapter
            OverpassQueryResult
            OverpassQueryResult.Element
            OverpassQueryResult.Element.Tags
            OverpassService
  c. 引用函數庫
      compile/run: 
        okhttp-3.14.9.jar
        okio-1.17.2.jar
        converter-gson-2.10.2.jar
        gson-2.8.5.jar
        retrofit-2.10.2.jar
      compile/run tests:
        byte-buddy-1.11.3.jar
        byte-buddy-agent-1.11.3.jar
        mockito-core-3.11.2.jar
        objenesis-3.2.jar
 */
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

import hu.supercluster.overpasser.library.output.OutputFormat;
import hu.supercluster.overpasser.library.output.OutputModificator;
import hu.supercluster.overpasser.library.output.OutputOrder;
import hu.supercluster.overpasser.library.output.OutputVerbosity;
import hu.supercluster.overpasser.library.query.OverpassQuery;
 
import hu.supercluster.overpasser.adapter.OverpassQueryResult;
import hu.supercluster.overpasser.adapter.OverpassQueryResult.Element;
import hu.supercluster.overpasser.adapter.OverpassQueryResult.Element.Tags;
import hu.supercluster.overpasser.adapter.OverpassService;
import hu.supercluster.overpasser.adapter.OverpassServiceProvider;

import java.lang.reflect.Field;
import java.util.List;
/**
 *
 * @author seke
 */

public class QueryOSM {
    
    // 可利用下址測試查詢指令
    //     http://overpass-turbo.eu/
    public static String composeRequest()
    {
/* 查詢範例1: 
        在(47.48047027491862,19.039797484874725,47.51331674014172,19.07404761761427)範圍內
        列出所有非私人停車場
        
    A. 查詢指令
    ["out":"json"]["timeout":"30"];
    (
        node
            ["amenity"="parking"]
            ["access"!="private"]
            (47.48047027491862,19.039797484874725,47.51331674014172,19.07404761761427);
            <;
    );
    out body center qt 100;

    B. 組合查詢指令方法
      String query = new OverpassQuery()
        .format(OutputFormat.JSON)
        .timeout(30)
        .filterQuery()
            .node()
            .amenity("parking")
            .tagNot("access", "private")
            .boundingBox(
                47.48047027491862, 19.039797484874725,
                47.51331674014172, 19.07404761761427
            )
        .end()
        .output(OutputVerbosity.BODY, OutputModificator.CENTER, OutputOrder.QT, 100)
        .build()
        ;
*/

/* 查詢範例2: 
        在(25.1735, 121.446, 25.1775, 121.455)範圍內,撈取
           非座椅設施,商店,觀光點,辦公室,繄急設施之節點
           屬於大學設施之線條,關係
           不屬於公路、路線、森林、邊界之線條,關係
        列出其中心位置及相關屬性(標籤)

        A. 查詢指令
    [out:json][timeout:25];
    // gather results
    (
      node[amenity][amenity!=bench](25.1735, 121.446, 25.1775, 121.455);
      node[shop](25.1735, 121.446, 25.1775, 121.455);
      node[tourism](25.1735, 121.446, 25.1775, 121.455);
      node[office](25.1735, 121.446, 25.1775, 121.455);
      node[emergency](25.1735, 121.446, 25.1775, 121.455);
      //way[amenity="university"](25.1735, 121.446, 25.1775, 121.455);
      relation[amenity="university"](25.1735, 121.446, 25.1775, 121.455);
      way[!highway][type!="route"][landuse!="forest"][!boundary](25.1735, 121.446, 25.1775, 121.455);
      relation[!highway][type!="route"][!boundary](25.1735, 121.446, 25.1775, 121.455);
    );
    // print results
    out center;
---
  註:  淡江大學之範圍為 25.1735, 121.446, 25.1775, 121.455
*/       
       String query = String.join("\n",
               "[out:json][timeout:25];",
               "(",
//               "node[amenity][amenity!=bench](25.1735, 121.446, 25.1775, 121.455);",
//               "node[shop](25.1735, 121.446, 25.1775, 121.455);",
               "node[tourism](25.1735, 121.446, 25.1775, 121.455);",
//               "node[office](25.1735, 121.446, 25.1775, 121.455);",
//               "node[emergency](25.1735, 121.446, 25.1775, 121.455);",
//               "relation[amenity='university'](25.1735, 121.446, 25.1775, 121.455);",
//               "way[!highway][type!='route'][landuse!='forest'][!boundary](25.1735, 121.446, 25.1775, 121.455);",
               "relation[!highway][type!='route'][!boundary](25.1735, 121.446, 25.1775, 121.455);",
               ");",
               "out center;");

        System.out.println(query);
        return query;
    }
    
    public static void asyncRequest(OverpassService apiClient, String request)
    {
       //Call call = service.interpreter(query);
       apiClient.interpreter(request).enqueue(new Callback()
       {
        @Override
        public void onResponse(Call call, Response response)
        {
            if(response.isSuccessful()==false)
            {
                System.out.println("asyncRequest: response.isSuccessful(): false");
                System.out.println(response.errorBody());
                return;
            }
            
            OverpassQueryResult result = response.body();
            postProcess(result);
         }

        public void onFailure(Call call, Throwable t) {
        // DO failure handling 
          System.out.println("onFailure");
          System.out.println(t.getLocalizedMessage());
        }
       });
       
       System.out.println("end of asyncRequest()");
    }
    
    public static OverpassQueryResult syncRequest(OverpassService apiClient, String request)
    {
        OverpassQueryResult result = null;
        Call callSync =   apiClient.interpreter(request); 

        try
        {
            Response response = callSync.execute();
            //OverpassQueryResult apiResponse = response.body();
     
            //API response
            System.out.println(response);
            result = response.body();
        }
        catch (Exception ex) 
        { 
            ex.printStackTrace();
        }
        
        System.out.println("end of syncRequest()");
        return result;
    }
    
    public static void postProcess(OverpassQueryResult result)
    {
        if(result==null) return;

        // DO success handling 
        StringBuilder sb = new StringBuilder();
        System.out.println(result.elements.size() + " elements...");
        int count = 1;
        for (Element p : result.elements) 
        {
            sb.append(count); count++;
            sb.append(String.format(". id:%s, type:%s, lat:%s, lon:%s", p.id, p.type, p.lat, p.lon));
            Tags tags = p.tags;
            for (Field f : tags.getClass().getFields()) {
                f.setAccessible(true);
                try 
                {
                    if (f.get(tags) != null) {
                       sb.append(String.format(", %s:%s", f.getName(), f.get(tags)));
                    }
                }
                catch (IllegalAccessException e)
                { // shouldn't happen because I used setAccessible
                }

            }
            //if(p.tags. != null)
            //  sb.append(String.format("tags:%s", p.tags.name));
            sb.append("\n");
        }
        System.out.println(sb.toString());
    }
    
    public static void main(String args[])
    {
        // 建立服務連線客戶端及請求內容
        OverpassService requestClient = OverpassServiceProvider.get();
        String request = composeRequest();
        
        // 非同步請求
        asyncRequest(requestClient, request);
        
        // 同步請求
        OverpassQueryResult result = syncRequest(requestClient, request);
        postProcess(result);
        
        System.out.println("end of main()");
    }
}

how to decode web content in gzip or deflate format using Java API?

有些網站回傳網頁會進行內容壓縮,壓縮方法常見有gzip或deflate,
可由回傳內容的ContentEncoding標頭決定如何處理回傳的壓縮內容。

以Java為例,解壓縮寫法如下:


      // 設定下載網址
      URL url = new URL("http://comment.bilibili.tv/29545595.xml");  
      HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
      //conn.setRequestProperty("Accept-Encoding", "identity");

      // 連線取得網頁輸入流
      conn.connect();  
      System.out.printf("con.getContentEncoding()=%s\n",conn.getContentEncoding());
      InputStream in = conn.getInputStream();

      if(conn.getContentEncoding().equals("gzip"))
          in = new GZIPInputStream(conn.getInputStream());  

      if(conn.getContentEncoding().equals("deflate"))
          in = new InflaterInputStream(conn.getInputStream(), new Inflater(true));

      // 從網頁輸入流列印內容到螢幕
      BufferedReader bin = new BufferedReader(new InputStreamReader(in, "UTF-8"));  
      String s = null;  
      while((s=bin.readLine())!=null){  
         System.out.println(s);  
      }  
      bin.close();

java right shift and and/or operators in comparison

⬛ >> versus >>>
 Java的位元運算有兩個很類似的右移運算子,目的是將其唯一的運算元進行往右的位元平移。
 當運算元為正時,兩者結果相同,分不出來;遇負數時,兩者結果才有差異。
 說明如下:

 邏輯右移運算子 >>>: number >>> bits; 表示將2進位數字右移bits位元,左邊補0
 算術右移運算子 >> : number >> bits; 也表示將2進位數字右移bits位元,左邊保留目前符號位元

     例1: 對於負數,兩種位元右移結果的比較: >> versus >>>

        int n = -7;
        System.out.printf("n = %d (%x)\n",n,n);
        System.out.printf("n >> 2 = %d (%x)\n",n >> 2,n >> 2);
        System.out.printf("n >>> 2 = %d (%x)\n",n >>> 2, n >>> 2);

     則3行輸出如下:
        n = -7 (fffffff9)               // 二進位表示為 1111 1111  1111 1111  1111 1111  1111 1001
        n >> 2 = -2 (fffffffe)          // 二進位表示為 1111 1111  1111 1111  1111 1111  1111 1110
        n >>> 2 = 1073741822 (3ffffffe) // 二進位表示為 0011 1111  1111 1111  1111 1111  1111 1110
     其中,第一行二進位為原始數字;
          第二行運算將二進位數字往右平移2位元,左邊補目前符號位元1;
          第三行運算將二進位數字往右平移2位元,左邊補0

  註: 須要維持正負號的右移運算可用 >>
  註: 不補符號位元的右移運算可用 >>>
  註: 至於左移運算子,因為右邊永遠補0,沒有其他選擇,所以只有一種 << 運算子


⬛ & versus &&  as well as  | versus ||
 Java的且及或運算也各有很接近的運算子,其說明如下:

 邏輯且:  b = bool_1    &&  bool_2;     // 有短路求值,遇bool_1假,不看bool_2值,一律回傳假
 位元且:  b = bool_1     &  bool_2;     // 無短路求值
         n = number_1   &  number_2; 

 邏輯或:  b = bool_1    ||  bool_2;     // 有短路求值,遇bool_1真,不看bool_2值,一律回傳真
 位元或:  b = bool_1     |  bool_2;     // 無短路求值
         n = number_1   |  number_2;

     例2: 對於數字及布林值,兩種【且】運算的比較: & versus &&

        System.out.printf("true &   false    = %b\n", true & false);
        System.out.printf("true &&  false    = %b\n", true && false);
        System.out.printf("1100 &   0101     = %x\n", 0b1100 & 0b0101);
      //System.out.printf("1100 &&  0101     = %d\n", 0b1100 && 0b0101);

     則3行輸出如下:
        true &   false    = false
        true &&  false    = false
        1100 &   0101     = 4        // 二進位表示 為0100
     其中,第一行兩布林值進行【位元且】運算,只當兩布林值同時為真時才為真,故得到假;
          第二行兩布林值進行【邏輯且】運算,只當兩布林值同時為真時才為真,故得到假;
          第三行兩數字進行【位元且】運算,只留下兩數字同時為1的位元才為1,故得到0100=4。
     另外,第四行語法不允許兩數字進行【邏輯且】運算,故註解起來。


     例3: 對於數字及布林值,兩種【或】運算的比較: | versus ||

        System.out.printf("true |   false    = %b\n", true | false);
        System.out.printf("true ||  false    = %b\n", true || false);
        System.out.printf("1100 |   0101     = %x\n", 0b1100 | 0b0101);
      //System.out.printf("1100 ||  0101     = %d\n", 0b1100 || 0b0101);

     則3行輸出如下:
        true |   false    = true
        true ||  false    = true
        1100 |   0101     = d        // 二進位表示 為1101
     其中,第一行兩布林值進行【位元或】運算,只當兩布林值同時為假時才為假,故得到真;
          第二行兩布林值進行【邏輯或】運算,只當兩布林值同時為假時才為假,故得到真;
          第三行兩數字進行【位元或】運算,只留下兩數字同時為0的位元才為0,故得到1101=13=d。
     另外,第四行語法不允許兩數字進行【邏輯或】運算,故註解起來。


     例4: 對於布林值,短路求值與否的【且】運算比較: & versus &&

         b = b1 = b2 = true;
         b = (b1=false) & (b2=false); 
         System.out.printf("no short circuit evaluation: b:%b, b1:%b, b2:%b\n",b,b1,b2);
         b = b1 = b2 = true;
         b = (b1=false) && (b2=false); 
         System.out.printf("use short circuit evaluation:b:%b, b1:%b, b2:%b\n",b,b1,b2);

     則2行輸出如下:
         no short circuit evaluation: b:false, b1:false, b2:false
         use short circuit evaluation:b:false, b1:false, b2:true
     其中,第一行輸出不作短路求值,所以b1及b2皆接收到false值;
          第二行輸出有作短路求值,所以b1接收回傳false值後,不須進行b2=false運算,就可判定b為假,故b2維持true值。


     例5: 對於布林值,短路求值與否的【或】運算比較: | versus ||

         b = b1 = b2 = false;
         b = (b1=true) | (b2=true); 
         System.out.printf("no short circuit evaluation: b:%b, b1:%b, b2:%b\n",b,b1,b2);
         b = b1 = b2 = false;
         b = (b1=true) || (b2=true); 
         System.out.printf("use short circuit evaluation:b:%b, b1:%b, b2:%b\n",b,b1,b2);

     則2行輸出如下:
         no short circuit evaluation: b:true, b1:true, b2:true
         use short circuit evaluation:b:true, b1:true, b2:false
     其中,第一行輸出不作短路求值,所以b1及b2皆接收到true值;
          第二行輸出有作短路求值,所以b1接收回傳true值後,不須進行b2=true運算,就可判定b為真,故b2維持false值。

  註: 數字的且/或運算只能用 &, |
  註: 條件式或布林值的且/或運算,若須要短路求值可用 &&, ||
  註: 條件式或布林值的且/或運算,若不須要短路求值可維持 &, |

參考: 
1.StackOverflow: Difference between >>> and >>
2.Wiki: Short Circuit Evaluation

C Pointer Concepts in Java

學過 C 語言,轉換到 Java 語言時,常有如下困擾。 相較於 C 語言的變數分成 指標變數 (pointer variable) 和 非指標變數 (non-pointer variable),兩者可由型別前面是否有加 * 號作區隔。那麼, Java 語言沒有加 * 號的指標變數,若遇到須要指標的情境,例如鏈結清單 (linked list) 或 圖形結構(graph),該如何應對?

答案是 Java 語言有所謂參照變數 (reference variable) 的設計,以對應於 C 語言的指標變數。以下將舉例說明。

C 語言中只要變數宣告時,型別前面加 * 號就是指標變數,例如:


     int i = 3; // 宣告整數變數 i,記憶體切一塊整數空間,裏頭填入整數 3
     int *p; // 宣告整數指標變數 p,記憶體切一塊指標空間,裏頭值未定
     p = &i; // 將變數 i 住址填入變數 p 指標空間

其中,i 是一般 整數型別 (int) 變數,變數 i 存放的是記憶體空間中,某位址的整數,透過 i 可以存取該整數。 p 前面加 *, p 就變成 整數指標型別 (int *) 變數,變數 p 存放的是記憶體空間中,某塊可存放整數的住址,透過 p 可以存取該住址的整數。在第3行指令,p 接收 i 住址之後,兩者產生連動,*p 和 i 將看到相同內容,整數 3。

Java 語言則沒有明確的指標觀念,所有變數只分成基本型別 (primitive type) 及非基本型別 (non-primitive type)。基本型別限定8種,包含 boolean, char, byte, short, int, long, float, double。基本型別的保留字開頭皆小寫,用途和 C 語言類似,其變數可直接存取該變數值。

非基本型別又稱 參照型別 (reference type),包含所有 類別,介面,陣列,列舉等參照型別。參照型別的變數存放的是參照值,可想像成記憶體位置值,或物件索引值。例如:


     // 宣告整數參照變數 r,記憶體切一塊參照空間,裏頭填入 null
     Integer r; 
     
     // 建立存放整數 4 的整數物件,將其索引值填入變數 r 的參照空間
     r = new Integer(4);

其中, r 就是整數參照變數。變數 r 存放某整數物件的索引值之後,透過 r 可以存取該整數物件的整數 4。

以上介紹 Java 的參照型別可對應於 C 指標型別的概念及簡單對照用例,若想進一步了解如何用 Java 實現如鏈結清單這類需要動態記憶體與節點連結的資料結構,可參考下文「Linked Lists from C to Java」。

four kinds of design for adding operation of two rationals

有理數加法的幾種設計: 結果回傳或覆蓋 vs 類別或物件方法
  (1) public static Rational add(Ratinoal r1, Rational r2)
      // 用法:  Rational r3 = Rational.add(r1,r2);  // r3=r1+r2

  (2) public static void add(Ratinoal r1, Rational r2)
      // 用法:  Rational.add(r1,r2);  // r1=r1+r2

  (3) public Rational add(Rational r2)
      // 用法:  Rational r3 = r1.add(r2);  // r3=r1+r2

  (4) public void add(Rational r2)
      // 用法:  r1.add(r2);  // r1=r1+r2

object transfer over Internet in Java

Here is a Client/Server connection sample for remote function calls in Java.
Map data structures are used to wrap parameters and results.
All serializable types are supported in function calls which include String, Vector, and ImageIcon.
Multithreading is used to allow multiple client requests simultaneously.


/*--------------------------------------------
$ java ClientMap4
connecting...
socketOut
socketIn
cmd:login1
writeObject(input:login1)
readObject()->output
output:LOGIN1
output1:user
output2:passwd
cmd:
*/
import java.util.concurrent.*;  // Executors
import java.util.*; // Vector
import java.net.*; // Socket, ServerSocket
import java.io.*; // InputStream,InputStreamReader,BufferedReader
   // OutputStream,PrintWriter
import javax.swing.ImageIcon;

public class ClientMap4
{
  final int defaultPort = 1234;

  static BufferedReader consoleIn;  // 控制台輸入流
  static PrintStream consoleOut;  // 控制台輸出流

  static Socket skt;
  static ObjectInputStream  socketIn;  // 連線插座輸入流
  static ObjectOutputStream socketOut;  // 連線插座輸出流

  @SuppressWarnings("unchecked")
  public static void main(String args[]) throws Exception
  {
    int port = 1234;

    if(args.length==1)    // 命令列有給埠號參數
      port = new Integer(args[0]).intValue(); // 則依命令列埠號

    consoleOut = System.err;
    consoleIn  = new BufferedReader(new InputStreamReader(System.in));
        consoleOut.printf("connecting...\n");

    skt = new Socket("localhost",port);

        consoleOut.printf("socketOut\n");
    OutputStream socketOs = skt.getOutputStream();
    socketOut = new ObjectOutputStream(socketOs);

        consoleOut.printf("socketIn\n");
    InputStream socketIs = skt.getInputStream();
    socketIn = new ObjectInputStream(socketIs);

    Map<String, Object> packetOut;
    Map<String, Object> packetIn;


    while(true)
    {
      String cmd,parm1="user",parm2="passwd";
      String output=null,output1=null,output2=null;

      // 測試字串陣列容器
      Vector<String> parm = new Vector<String>();
      parm.add(parm1);
      parm.add(parm2);

      // 測試結果集容器
      Vector<Vector<String>> vecResultSet = new Vector<Vector<String>>();
      Vector<String> record1 = new Vector<String>();
      record1.add("user_id_1");
      record1.add("passwod_1");
      record1.add("email_1");
      Vector<String> record2 = new Vector<String>();
      record2.add("user_id_2");
      record2.add("passwod_2");
      record2.add("email_2");
      vecResultSet.add(record1);
      vecResultSet.add(record2);

      // 測試圖片
      ImageIcon pic = new ImageIcon("test.jpg");

        consoleOut.printf("cmd:");
      cmd = consoleIn.readLine();
      packetOut = new HashMap<String, Object>();
      packetOut.put("cmd",cmd);
      packetOut.put("parm1",parm1);
      packetOut.put("parm2",parm2);
      packetOut.put("parm",parm);
      packetOut.put("vecResultSet",vecResultSet);
      packetOut.put("pic", pic);

        consoleOut.printf("writeObject(input:%s)\n",cmd);
      socketOut.writeObject(packetOut);
      socketOut.flush();

        consoleOut.printf("readObject()->output\n");
      packetIn = (Map<String, Object>) socketIn.readObject();
      output  = (String) packetIn.get("output");
      output1 = (String) packetIn.get("output1");
      output2 = (String) packetIn.get("output2");

        consoleOut.printf("output:%s\n",output);
        consoleOut.printf("output1:%s\n",output1);
        consoleOut.printf("output2:%s\n",output2);
    }
  }
}





/*--------------------------------------------------
$ java ServerMap4
ListenTask: thread:pool-1-thread-1 (9) waiting at port:1234
ServiceTask: thread: pool-1-thread-1 (9) serving /127.0.0.1:59626
ServiceTask: socketOut
ServiceTask: socketIn
ServiceTask: end of constructor
ListenTask: thread:pool-1-thread-1 (9) waiting at port:1234
        begin running
        waiting input
        cmd=<login1>
        parm1=<user>
        parm2=<passwd>
        parm=<[user, passwd]>
        vecResultSet=<[[user_id_1, passwod_1, email_1], [user_id_2, passwod_2, email_2]]>
        pic=width:728,height:90,write to 'test2.jpg'
        output=<LOGIN1>
        output1=<user>
        output2=<passwd>
        waiting input
*/
import java.util.concurrent.*;  // Executors
import java.util.*; // 用到 Vector
import java.net.*; // 用到 Socket, ServerSocket
import java.io.*; // 用到 InputStream,InputStreamReader,BufferedReader
   // OutputStream,PrintWriter
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.Graphics2D;

class ServiceTask implements Runnable
{
  BufferedReader consoleIn;  // 控制台輸入流
  PrintStream consoleOut;  // 控制台輸出流

  Socket skt;
  ObjectInputStream  socketIn;  // 連線插座輸入流
  ObjectOutputStream socketOut;  // 連線插座輸出流

  String host;
  String port;

  public ServiceTask(Socket skt) throws IOException
  {
    // 取得螢幕輸出流
    consoleOut = System.err;

    // 由連線插座,取得主機,埠號
    this.skt = skt;
    host = skt.getInetAddress().toString();
    port = String.valueOf(skt.getPort());
    consoleOut.printf("ServiceTask: thread: %s (%d) serving %s:%s\n",
     Thread.currentThread().getName(),
     Thread.currentThread().getId(), host,port);

    // 由連線插座,取得插座輸出入資料流
    consoleOut.printf("ServiceTask: socketOut\n");
    OutputStream socketOs = skt.getOutputStream();
    socketOut = new ObjectOutputStream(socketOs);

    consoleOut.printf("ServiceTask: socketIn\n");
    InputStream socketIs = skt.getInputStream();
    socketIn = new ObjectInputStream(socketIs);

    consoleOut.printf("ServiceTask: end of constructor\n");
  }

  @SuppressWarnings("unchecked")
  public void run()
  {
        consoleOut.printf("\tbegin running\n");

    Map<String, Object> packetOut;
    Map<String, Object> packetIn;

    // 測試字串陣列容器,結果集容器,圖片
    Vector<String> parm;
    Vector<Vector<String>> vecResultSet;
    ImageIcon pic;

    try
    {
      // 進行多次對話,直到輸出為QUIT為止
      while(true)
      {
        consoleOut.printf("\twaiting input\n");
        packetIn = (Map<String, Object>) socketIn.readObject();
        String cmd = (String) packetIn.get("cmd");
        String parm1 = (String) packetIn.get("parm1");
        String parm2 = (String) packetIn.get("parm2");
        parm = (Vector<String>) packetIn.get("parm");
        vecResultSet = (Vector<Vector<String>>)packetIn.get("vecResultSet");
        pic = (ImageIcon) packetIn.get("pic");

         consoleOut.printf("\tcmd=<%s>\n",cmd);
         consoleOut.printf("\tparm1=<%s>\n",parm1);
         consoleOut.printf("\tparm2=<%s>\n",parm2);
         consoleOut.printf("\tparm=<%s>\n",parm);
         consoleOut.printf("\tvecResultSet=<%s>\n",vecResultSet);
         consoleOut.printf("\tpic=width:%d,height:%d,write to '%s'\n",pic.getIconWidth(),pic.getIconHeight(),"test2.jpg");

         // save image for comparison
         Image img = pic.getImage();
         BufferedImage bi = new BufferedImage(img.getWidth(null),img.getHeight(null),BufferedImage.TYPE_INT_RGB);
         Graphics2D g2 = bi.createGraphics();
         g2.drawImage(img, 0, 0, null);
         g2.dispose();
         ImageIO.write(bi, "jpg", new File("test2.jpg"));

        String output = cmd.toUpperCase();
        String output1 = parm1;
        String output2 = parm2;
        packetOut = new HashMap<String, Object>();
        packetOut.put("output",output);
        packetOut.put("output1",output1);
        packetOut.put("output2",output2);
        socketOut.writeObject(packetOut);
        socketOut.flush();
         consoleOut.printf("\toutput=<%s>\n",output);
         consoleOut.printf("\toutput1=<%s>\n",output1);
         consoleOut.printf("\toutput2=<%s>\n",output2);

        if(cmd.equals("quit")) break; // 遇到quit指令結束
      }
    }
    catch(Exception e)
    {
      consoleOut.printf("ServiceTask: exception:%s\n",e);
    }
    finally
    {
      // 關閉插座資料流和插座本身
      try
      {
        socketIn.close();
        socketOut.close();
        skt.close();
      }
      catch(IOException e)
      {
       consoleOut.printf("ServiceTask: socket close error:%s\n",e);
      }
    }
  }
}

class ListenTask implements Runnable
{
  int listenPort;   // 監聽埠號
  ServerSocket listenSocket;  // 監聽插座
  PrintStream consoleOut;  // 控制台輸出流

  ExecutorService pool;  // 緒池

  public ListenTask(ExecutorService pool, int port) throws IOException
  {
    this.listenPort = port;
    this.pool = pool;

    consoleOut = System.err;

    // 建立監聽插座,可能丟例外
    listenSocket = new ServerSocket(listenPort);

    Runnable listenTask = this;
    Future f = pool.submit(listenTask);
  }

  // 監聽緒工作
  public void run()
  {
    Socket connectedSocket=null;

    try
    {
      while(true)
      {
        // 等待新連線
        consoleOut.printf("ListenTask: thread:%s (%d) waiting at port:%d\n",
         Thread.currentThread().getName(),
         Thread.currentThread().getId(),listenPort);

        connectedSocket = listenSocket.accept();

        // 啟動專屬緒,為新連線服務
        Runnable serviceTask = new ServiceTask(connectedSocket);
        pool.execute(serviceTask);
      }
    }
    catch(IOException e)
    {
     consoleOut.printf("ListenTask: accept raised IOException:%s\n",e);
      System.exit(0);
    }
  }
}

public class ServerMap4
{
  final int defaultPort = 1234;
  final ExecutorService pool;

  public ServerMap4(int port)
  {
    pool = Executors.newCachedThreadPool();  // 建立緒池
    //pool = Executors.newFixedThreadPool(10);

    try
    {
      if(port < 1024) port = defaultPort;
      ListenTask listenTask = new ListenTask(pool,port); // 開監聽緒
    }
    catch(IOException e)
    {
      System.err.printf("ServerMap4: %s\n", e);
    }
  }

  public static void main(String args[]) throws Exception
  {
    int port = -1;

    if(args.length==1)    // 命令列有給埠號參數
      port = new Integer(args[0]).intValue(); // 則依命令列埠號

    new ServerMap4(port);  // 啟動監聽及服務緒
  }
}

combination enumerator in java


/*
  Combination.java

  generates all combinations of C(n,m) for n >= m >= 0

Usage: java Combination n m

Sample Output:
> java Combination 5 2
--- recursive one-shot generation ---
k=1 ---> {1, 2}
k=2 ---> {1, 3}
k=3 ---> {1, 4}
k=4 ---> {1, 5}
k=5 ---> {2, 3}
k=6 ---> {2, 4}
k=7 ---> {2, 5}
k=8 ---> {3, 4}
k=9 ---> {3, 5}
k=10 ---> {4, 5}
--- nonrecursive item-wise generation ---
k=1 ---> {1, 2}
k=2 ---> {1, 3}
k=3 ---> {1, 4}
k=4 ---> {1, 5}
k=5 ---> {2, 3}
k=6 ---> {2, 4}
k=7 ---> {2, 5}
k=8 ---> {3, 4}
k=9 ---> {3, 5}
k=10 ---> {4, 5}

--- Combination.java ---
*/
import java.util.Vector;
import java.util.Enumeration;

// class for generating all m-item selection sets from an n-item population set
public class Combination {
    Vector < Object > n_set; // population set of objects for combination
    int n; // population size, n >= m >= 0
    int m; // selection size

    // constructor for combination object
    //   n_set: population set of objects for combination
    //   m:  selection size
    public Combination(Vector < Object > n_set, int m) {
        this.n_set = n_set;
        this.n = n_set.size();
        this.m = m;

        if (n < m || m < 0 || n <= 0) {
            System.err.printf("combine(n=%d, m=%d): illegal n,m!\n", n, m);
        }
    }

    // generate integer objects from 1 to n
    public static Vector < Object > generateIntegers(int n) {
        Vector < Object > set = new Vector < Object > ();
        for (int i = 1; i <= n; i++) set.add(i);
        return set;
    }

    // generate integer objects from 1 to n
    public static Vector < Vector < Object >> cnm(int n, int m) {
        Vector < Object > set = generateIntegers(n);
        Combination c = new Combination(set, m);
        Vector < Vector < Object >> set_list = c.cnm();
        return set_list;
    }

    // generate all m-item selection sets from population set n_set
    //   returns set of all m-item selection sets from population set n_set
    Vector < Vector < Object >> cnm() {
        return cnm(n_set, m);
    }

    // recursive generation of all m-item selection sets from population set n_set
    //   n_set: population set
    //   m: selection size
    //   returns set of all m-item selection sets from population set n_set
    Vector < Vector < Object >> cnm(Vector < Object > n_set, int m) {
        Vector < Object > set = new Vector < Object > ();
        Vector < Vector < Object >> result_list = new Vector < Vector < Object >> ();

        int n = n_set.size();
        if (n < m || m < 0 || n <= 0) {
            System.err.printf("combine(n=%d, m=%d): illegal n,m!\n", n, m);
            return null;
        }

        // base case 1: m=0
        if (m == 0) {
            result_list.add(set);
            return result_list;
        }
        // base case 2: m=n
        else if (m == n) {
            result_list.add(n_set);
            return result_list;
        }

        // recursive call:
        Vector < Vector < Object >> result_with_first = new Vector < Vector < Object >> ();
        Vector < Vector < Object >> result_without_first = new Vector < Vector < Object >> ();

        Vector < Object > n_minus_1_set = new Vector < Object > (n_set);
        n_minus_1_set.removeElementAt(0);
        result_with_first = cnm(n_minus_1_set, m - 1);
        result_without_first = cnm(n_minus_1_set, m);
        for (Vector < Object > comb: result_with_first) {
            comb.insertElementAt(n_set.firstElement(), 0);
        }

        result_list.addAll(result_with_first);
        result_list.addAll(result_without_first);

        return result_list;
    }

    // get enumerator for all m-item selection sets from population set n_set
    Enumeration < Vector < Object >> enumeration() {
        if (n < m || m < 0 || n <= 0) {
            System.err.printf("combine(n=%d, m=%d): illegal n,m!\n", n, m);
            return null;
        }
        return new Enumerator();
    }

    // inner class of Combination class
    // enumerator for non-recursive generation of all m-item selection sets from population set n_set
    public class Enumerator implements Enumeration < Vector < Object >> {
        int index[];
        int index_upper[];
        boolean carry;
        boolean hasMore;
        Vector < Object > element;

        // constructor for m-item selection set enumerator
        public Enumerator() {
            element = new Vector < Object > ();
            hasMore = true;

            //carry = new boolean[m];
            index = new int[m];
            index_upper = new int[m];
            for (int i = 0; i <= m - 1; i++) {
                index[i] = i;
                index_upper[i] = n - m + i;
            }
        }

        public boolean hasMoreElements() {
            return hasMore;
        }

        public Vector < Object > nextElement() {
            int i;
            element.clear();
            for (i = 0; i <= m - 1; i++) {
                element.add(n_set.get(index[i]));
            }

            // point to next index
            carry = false;
            for (i = m - 1; i >= 0; i--) {
                int digit = index[i];
                if (digit + 1 <= index_upper[i]) {
                    index[i]++;

                    if (carry == true)
                        while (i + 1 <= m - 1) {
                            index[i + 1] = index[i] + 1;
                            i++;
                        }
                    break;
                } else
                    carry = true;
            }

            if (i < 0) hasMore = false;
            return element;
        } // end of nextElement
    } // end of enumerator class

    // main program for test
    public static void main(String args[]) {
        int n = 5;
        int m = 2;

        if (args.length == 2) {
            n = Integer.parseInt(args[0]);
            m = Integer.parseInt(args[1]);
        }

        System.out.println("--- recursive one-shot generation ---");

        Vector < Vector < Object >> set_list = Combination.cnm(n, m);
        int k = 1;
        for (Vector < Object > set: set_list) {
            System.out.printf("k=%d ---> {", k);
            boolean first = true;
            for (Object o: set) {
                if (first) first = false;
                else System.out.print(", ");
                System.out.print((Integer) o);
            }
            System.out.println("}");
            k++;
        }

        System.out.println("--- nonrecursive item-wise generation ---");

        Vector < Object > set = Combination.generateIntegers(n);
        Combination c = new Combination(set, m);
        Enumeration < Vector < Object >> e = c.enumeration();
        k = 1;
        while (e.hasMoreElements()) {
            set = e.nextElement();
            System.out.printf("k=%d ---> {", k);
            boolean first = true;
            for (Object o: set) {
                if (first) first = false;
                else System.out.print(", ");
                System.out.print((Integer) o);
            }
            System.out.println("}");
            k++;
        }
    }
}


註: 本程式使用 GitHub JavaScript code prettifier 工具標示顏色。其方法如下:
   1.參考 [Blogger] 如何在 Blogger 顯示程式碼 - Google Code Prettify
     於【Blogger 版面配置 HTML/JavaScript小工具】安裝如下套件
       <script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js"></script>
   2.文章編輯再以HTML模式為程式包上如下標籤。
       <code class="prettyprint lang-java linenums"> ... </code>

simple notes on libsvm java

libsvm java版使用例
==================
                                             
1.libsvm程式下載點:
  http://www.csie.ntu.edu.tw/~cjlin/libsvm+zip

2.範例資料下載點:
  http://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets

3.程式編譯法:
C:\libsvm\libsvm-2.89\java>javac -cp libsvm.jar *.java
產生尺度調整器svm_scale.class
    訓練器svm_train.class
    預測器svm_predict.class

4.資料採稀疏格式,一列一案例,先預測數值,再列舉所有非零(維度:值)

5.訓練測試範例
  以下範例訓練集資料: a1a.txt
  以下範例測試集資料: a1a_t.txt

A.未作尺度調整例:
A1.呼叫訓練器
    輸入訓練資料: a1a.txt
    輸出學得模型: a1a_model.txt
C:\libsvm\libsvm-2.89\java>java -cp libsvm.jar svm_train a1a.txt a1a_model.txt
*
optimization finished, #iter = 495
nu = 0.46026768501985826
obj = -673.0313934890871, rho = -0.6285688589260043
nSV = 754, nBSV = 722
Total nSV = 754

A2.呼叫預測器
    輸入測試資料: a1a_t.txt
    輸入學得模型: a1a_model.txt
    輸出預測結果: a1a_predict.txt
C:\libsvm\libsvm-2.89\java>java -cp libsvm.jar svm_predict a1a_t.txt a1a_model.txt a1a_predict.txt
Accuracy = 83.58638066933712% (25875/30956) (classification)
--
B.作尺度調整例:
B1.呼叫尺度調整器2次
     輸入訓練資料:     a1a.txt
     輸出調整模型:     a1a_param.txt
     輸出調整訓練資料: a1a_scale.txt
C:\libsvm\libsvm-2.89\java>java -cp libsvm.jar svm_scale -s a1a_param.txt a1a.txt > a1a_scale.txt
Warning: original #nonzeros 22249
         new      #nonzeros 181365
Use -l 0 if many original feature values are zeros

     輸入測試資料:     a1a_t.txt
     輸入調整模型:     a1a_param.txt
     輸出調整測試資料: a1a_t_scale.txt
C:\libsvm\libsvm-2.89\java>java -cp libsvm.jar svm_scale -r a1a_param.txt a1a_t.txt > a1a_t_scale.txt
Warning: original #nonzeros 429343
         new      #nonzeros 3807588
Use -l 0 if many original feature values are zeros

B2.呼叫訓練器
    輸入訓練資料: a1a_scale.txt
    輸出學得模型: a1a_model_scale.txt
C:\libsvm\libsvm-2.89\java>java -cp libsvm.jar svm_train a1a_scale.txt
 a1a_model_scale.txt
*
optimization finished, #iter = 682
nu = 0.4077289259698594
obj = -593.6459193183854, rho = -0.48104500731367267
nSV = 694, nBSV = 622
Total nSV = 694

B3.呼叫預測器
    輸入測試資料: a1a_t_scale.txt
    輸入學得模型: a1a_model_scale.txt
    輸出預測結果: a1a_predict_scale.txt
C:\libsvm\libsvm-2.89\java>java -cp libsvm.jar svm_predict a1a_t_scale
.txt a1a_model_scale.txt a1a_predict_scale.txt
Accuracy = 84.05478744023776% (26020/30956) (classification)

--
-- 以上B相對於A,多作了輸出入範圍尺度調整,準確率略提昇.
-- 另外,A1及B2呼叫訓練器時,針對預設C-SVC學習器,
-- 若能適當作參數網格搜尋最佳化,準確率會更提昇
-- C-SVC參數有-g gamma及-c cost兩項,詳libsvm首頁guide.pdf
--  http://www.csie.ntu.edu.tw/~cjlin/papers/guide/guide.pdf
--
1. 尺度調整器選項:
C:\libsvm\libsvm-2.89\java>java -cp libsvm.jar svm_scale
Usage: svm-scale [options] data_filename
options:
-l lower : x scaling lower limit (default -1)
-u upper : x scaling upper limit (default +1)
-y y_lower y_upper : y scaling limits (default: no y scaling)
-s save_filename : save scaling parameters to save_filename
-r restore_filename : restore scaling parameters from restore_filename

2. 訓練器選項:
C:\libsvm\libsvm-2.89\java>java -cp libsvm.jar svm_train
Usage: svm_train [options] training_set_file [model_file]
options:
-s svm_type : set type of SVM (default 0)
        0 -- C-SVC
        1 -- nu-SVC
        2 -- one-class SVM
        3 -- epsilon-SVR
        4 -- nu-SVR
-t kernel_type : set type of kernel function (default 2)
        0 -- linear: u'*v
        1 -- polynomial: (gamma*u'*v + coef0)^degree
        2 -- radial basis function: exp(-gamma*|u-v|^2)
        3 -- sigmoid: tanh(gamma*u'*v + coef0)
        4 -- precomputed kernel (kernel values in training_set_file)
-d degree : set degree in kernel function (default 3)
-g gamma : set gamma in kernel function (default 1/k)
-r coef0 : set coef0 in kernel function (default 0)
-c cost : set the parameter C of C-SVC, epsilon-SVR, and nu-SVR (default 1)
-n nu : set the parameter nu of nu-SVC, one-class SVM, and nu-SVR (default 0.5)
-p epsilon : set the epsilon in loss function of epsilon-SVR (default 0.1)
-m cachesize : set cache memory size in MB (default 100)
-e epsilon : set tolerance of termination criterion (default 0.001)
-h shrinking : whether to use the shrinking heuristics, 0 or 1 (default 1)
-b probability_estimates : whether to train a SVC or SVR model for probability estimates, 0 or 1 (default 0)
-wi weight : set the parameter C of class i to weight*C, for C-SVC (default 1)
-v n : n-fold cross validation mode
-q : quiet mode (no outputs)

3. 預測器選項:
C:\libsvm\libsvm-2.89\java>java -cp libsvm.jar svm_predict
usage: svm_predict [options] test_file model_file output_file
options:
-b probability_estimates: whether to predict probability estimates, 0 or 1 (default 0); one-class SVM not supported yet

code for testing if two line segments are intersecting


/*
   Line.java

     Test if two line segments are intersecting or not
     Line segment 1 is between endpoints (x1,y1) and (x2,y2)
     Line segment 2 is between endpoints (u1,v1) and (u2,v2)

   Usage: java Line x1 y1 x2 y2 u1 v1 u2 v2

   > java Line
   java Line 0 1 1 0 0 0 1 1

   > java Line 0 1 1 0 0 0 1 1
    t * 1.000000 = 0.000000 + u * 1.000000
    t * -1.000000 = -1.000000 + u * 1.000000
    ---
    t * 1.000000 = 0.000000 + u * 1.000000
    t * -1.000000 = -1.000000 + u * 1.000000
    ---
    t = nom(1.000000) / denom(2.000000) = 0.500000
    u = nom(-1.000000) / denom(-2.000000) = 0.500000
   true
*/
public class Line
{
  //  line1:  0 <= t <= 1
  // x = ax1 + t * (ax2 - ax1)
  //    y = ay1 + t * (ay2 - ay1)

  //  line2:  0 <= u <= 1
  // x = bx1 + u * (bx2 - bx1)
  //    y = by1 + u * (by2 - by1)

  //  returns true if two lines intersect; and false otherwise

  public static boolean isTwoLinesIntersecting(
    double ax1, double ay1, double ax2, double ay2,
    double bx1, double by1, double bx2, double by2)
  {
    double nom, denom;
    double ax21 = ax2 - ax1;
    double bax1 = bx1 - ax1;
    double bx21 = bx2 - bx1;
    double ay21 = ay2 - ay1;
    double bay1 = by1 - ay1;
    double by21 = by2 - by1;

    // t * (ax2 - ax1) = (bx1 - ax1) + u * (bx2 - bx1)
    // t * (ay2 - ay1) = (by1 - ay1) + u * (by2 - by1)
     System.out.printf(" t * %f = %f + u * %f\n", ax21, bax1, bx21);
     System.out.printf(" t * %f = %f + u * %f\n", ay21, bay1, by21);
     System.out.printf(" ---\n");

    // ==>
    // t * (ax2 - ax1) * (by2 - by1) = (bx1 - ax1) * (by2 - by1) + u * (bx2 - bx1) * (by2 - by1)
    // t * (ay2 - ay1) * (bx2 - bx1) = (by1 - ay1) * (bx2 - bx1) + u * (by2 - by1) * (bx2 - bx1)
     System.out.printf(" t * %f = %f + u * %f\n", ax21*by21, bax1*by21, bx21*by21);
     System.out.printf(" t * %f = %f + u * %f\n", ay21*bx21, bay1*bx21, by21*bx21);
     System.out.printf(" ---\n");

    // ==>
    // t = (bx1 - ax1) * (by2 - by1) - (by1 - ay1) * (bx2 - bx1)
    //    / [ (ax2 - ax1) * (by2 - by1) - (ay2 - ay1) * (bx2 - bx1)]
    // u = (ax1 - bx1) * (ay2 - ay1) - (ay1 - by1) * (ax2 - ax1)
    //    / [ (bx2 - bx1) * (ay2 - ay1) - (by2 - by1) * (ax2 - ax1)]
    nom = (bx1 - ax1) * (by2 - by1) - (by1 - ay1) * (bx2 - bx1);
    denom = (ax2 - ax1) * (by2 - by1) - (ay2 - ay1) * (bx2 - bx1);
    double t = nom / denom;
     System.out.printf(" t = nom(%f) / denom(%f) = %f\n", nom, denom, t);

    nom = (ax1 - bx1) * (ay2 - ay1) - (ay1 - by1) * (ax2 - ax1);
    denom = (bx2 - bx1) * (ay2 - ay1) - (by2 - by1) * (ax2 - ax1);
    double u = nom / denom;
     System.out.printf(" u = nom(%f) / denom(%f) = %f\n", nom, denom, u);

    if(t>=0 && t <=1 && u>=0 && u<=1)  return true;
    else return false;
  }

  public static void main(String[] args)
  {
    if(args.length != 8)
    {
      System.out.println("java line 0 1 1 0 0 0 1 1");
      System.exit(0);
    }

    double ax1 = Double.parseDouble(args[0]);
    double ay1 = Double.parseDouble(args[1]);
    double ax2 = Double.parseDouble(args[2]);
    double ay2 = Double.parseDouble(args[3]);
    double bx1 = Double.parseDouble(args[4]);
    double by1 = Double.parseDouble(args[5]);
    double bx2 = Double.parseDouble(args[6]);
    double by2 = Double.parseDouble(args[7]);

    System.out.println(Line.isTwoLinesIntersecting(
      ax1,ay1,ax2,ay2,bx1,by1,bx2,by2));
  }
}

interface finder


/*
 * @(#)InterfaceFinder.java v0.8, 2009/05/06-2020/2/8
 *
 *   Search the class path for classes which implement a specific interface
 *
 *   Usage: java -cp jar_file;class_folder;. InterfaceFinder interface_name_to_search
 */

import java.util.Enumeration;
import java.util.List;
import java.util.Vector;
import java.util.Stack;
import java.util.HashSet;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
  The interface finder is used to find all classes
  implementing a specific interface.
  The location in search includes all the subdirectories
  in the class path. Typical usage is as follows.

    List<Class> InterfaceFinder.getAvailableInterfaces("java.util.List");

 * @since 0.8
 * @version 0.8, 2009/05/06-2020/2/8
 * @author Seke Wei
*/
public class InterfaceFinder
{
  public static final String version="InterfaceFinder.java v0.8 2020/02/08";

  /**
    a file lister which uses the depth first strategy
    to traverse all files under a given directory.
  */
  public static class FileLister implements Enumeration
  {
    boolean hasMore;
    Stack<File> fstack;

    /**
      constructor for file lister.
      @param dir the root directory for search.
    */
    public FileLister(File dir)
    {
      fstack = new Stack<>();
      if(dir.isDirectory())
        fstack.push(dir);
      hasMore = true;
    }

    /**
      check if there is still file available for listing.
      @return true for elements available and false for otherwise.
    */
    public boolean hasMoreElements()
    {
      return hasMore;
    }

    /**
      get the next available file.
      directories are skipped.
      @return the file.
    */
    public File nextElement()
    {
      File next = null;

      while(fstack.empty()==false)
      {
        next = (File) fstack.pop();
        if(next.isFile())
          break;

        for(File f : next.listFiles())
          fstack.push(f);
      }

      if(fstack.empty()) hasMore=false;

      return next;
    }
  }

  /**
    get classes from a jar file which implements an interface.
    @param jarFileName the jar filename.
    @param iface the interface in search.
    @return the set of classes in jar implementing the interface.
  */
  public static List<Class> getClassesFromJARFile(String jarFileName, Class iface)
  {
    final List<Class> classes = new Vector<>();
    JarInputStream jarFile = null;
    String className = "";
    try
    {
      jarFile = new JarInputStream(new FileInputStream(jarFileName));
      JarEntry jarEntry;
      while(true)
      {
        jarEntry = jarFile.getNextJarEntry();
        if(jarEntry == null) break;

        className = jarEntry.toString();

        //System.err.println(className);

        if(className.endsWith(".class") && className.indexOf("$") < 0 )
        {
          //extractClassFromJar(jar, packageName, classes, jarEntry)
          className = className.replace('/','.');
          className = className.substring(0, className.length() - ".class".length());
          Class cl = Class.forName(className);
          if(iface.isAssignableFrom(cl))
          {
            //System.err.println("\t"+className+" can be assigned to "+iface);
            classes.add(cl);
          }
        }
      }
      jarFile.close();
    }
    catch (IOException ioe)
    {
      System.err.println("Unable to get Jar input stream from '"+jarFileName+"'"+ioe);
    }
    catch (ClassNotFoundException cnfe)
    {
      System.err.println("unable to find class named " + className.replace('/', '.') + "' within jar '" + jarFileName + "'"+cnfe);
    }

    return classes;
  }

  /**
    get classes from a root directory which implements an interface.
    @param dir the root directory.
    @param iface the interface in search.
    @return the set of classes in jar implementing the interface.
    @throws IOException when there is a file open error.
  */
  public static List<Class> getClassesFromDirectory(File dir, Class iface)
  {
    List<Class> classes = new Vector<>();

    FileLister lister = new FileLister(dir);
    while(lister.hasMoreElements())
    {
      File f = lister.nextElement();
      String className = f.toString();

      //System.err.println(className);

      if(className.endsWith(".class") && className.indexOf("$") < 0 )
      {
        className = className.replace(File.separatorChar,'.');
        className = className.substring(0, className.length() - ".class".length());
        while(className.charAt(0)=='.') className = className.substring(1);
        //System.err.println("\t"+className);

        try
        {
          Class cl = Class.forName(className);
          if(iface.isAssignableFrom(cl))
          {
            //System.err.println("\t"+className+" can be assigned to "+iface);
            classes.add(cl);
          }
        }
        catch(ClassNotFoundException cnfe)
        {}
      }
    }

    return classes;
  }

  /**
    get classes from the class path which implements an interface.
    @param iface the interface in search.
    @return the set of classes in jar implementing the interface.
  */
  public static List<Class> getAvailableClassesOfInterface(String iface)
  {
    List<Class> result = null;

    try
    {
      Class iface_class = Class.forName(iface);
      result = getAvailableClassesOfInterface(iface_class);
    }
    catch(ClassNotFoundException cnfe) {}

    return result;
  }

  /**
    get classes from the class path which implements an interface.
    @param iface the interface in search.
    @return the set of classes in jar implementing the interface.
  */
  public static List<Class> getAvailableClassesOfInterface(Class iface)
  {
    String cp = System.getProperty("java.class.path");
    //System.out.println("java.class.path = " + cp);

    List<Class> result = new Vector<>();

    // semicolon (Windows) or colon (Unix) by System.getProperty("path.separator")
    String separator = System.getProperty("path.separator");
    for(String p : cp.split(separator))
    {
      File f = new File(p);
      if(f.isDirectory())
        result.addAll(getClassesFromDirectory(f, iface));
      else if(f.isFile() && p.endsWith("jar"))
        result.addAll(getClassesFromJARFile(p, iface));
    }

    HashSet<Class> set = new HashSet<>(result);

    result.clear();

    for(Class c : set)
    {
      //System.err.println(c);
      result.add(c);
    }

    return result;
  }

  /**
     InterfaceFinder.java
 
        Search the class path for classes which implement a specific interface

     Usage: java -cp jar_file;class_folder;. InterfaceFinder interface_name_to_search

     Example:
     > javac InterfaceFinder.java
     > java -cp rt.jar;. InterfaceFinder java.util.List
     class javax.management.relation.RoleUnresolvedList
     class java.util.AbstractList
     class java.util.SubList
     class java.util.concurrent.CopyOnWriteArrayList
     class javax.management.AttributeList
     class java.util.AbstractSequentialList
     class java.util.RandomAccessSubList
     class java.util.ArrayList
     class java.util.Vector
     class java.util.Stack
     class java.util.LinkedList
     class javax.management.relation.RoleList
     interface java.util.List

     Note that since JDK 9 there is no rt.jar for testing
  */
  public static void main(String args[])
  {
    String iface = "java.util.List";
    if(args.length > 0)
      iface = args[0];

    List<Class> classes =
      getAvailableClassesOfInterface(iface);

    for(Class c : classes)
    {
      System.err.println(c);
    }
  }
}

scanner.next and nextLine

一般用掃瞄器物件(Scanner)時,若依下法,
在讀數字後,再讀文字,會有讀不到文字情況,


  Scanner sc = new Scanner(System.in);
  int i = sc.nextInt(); // 讀數字,換行字元未讀走
  String s = sc.nextLine(); // 讀換行前字串,會收到空字串


這時,建議改用如下寫法,


  Scanner sc = new Scanner(System.in);
  int i = sc.nextInt(); // 讀數字,換行字元未讀走
  String s = sc.nextLine(); // 故意用nextLine讀走下一換行前字串及換行字元
  String s = sc.nextLine(); // 再用nextLine重新讀下一換行前字串及換行字元

  Scanner sc = new Scanner(System.in);
  int i = sc.nextInt(); // 讀數字,換行字元未讀走
  String s = sc.next(); // 讀下一個空格隔開前文字,這樣非空格文字仍可讀到


如下測試範例 Test2.java 可自行試看看.

---- Test2.java -----------


/*
  Test2.java

  >javac Test2.java

  >java Test2
  input i=123
  input s=
  input s2=456
  i=123, s=, s2=456, end

*/
import java.util.*;

public class Test2
{
  public static void main(String[] arg)
  {
    Scanner sc = new Scanner(System.in);
    System.err.printf("input i=");
    int i = sc.nextInt();

    System.err.printf("input s=");
    String s = sc.nextLine();
    // 前面有讀數字,後面文字會讀不到,只收到空字串

    System.err.printf("\ninput s2=");
    String s2 = sc.nextLine();

    System.err.printf("i=%d, s=%s, s2=%s, end\n",i,s, s2);
  }
}

methods for creating a self-contained .jar with data file


Given Main.java, data1.dat, data2.dat,
methods for creating a self-contained .jar with data file
0.use getResourceAsStream to get jar data at package root
InputStream is = Main.class.getResourceAsStream("/"+dataName);

1.add to all *.java source
package my.package;

2.compile with package start location
java -d . *.java

3.vi manifest.txt
#Class-Path: my.package
Main-Class: my.package.Main

4.produce jar file with data files
jar cvfm mypackage.jar manifest.txt my data1.dat data2.dat

5.run by
java -jar mypackage.jar
java -cp mypackage.jar my.package.Main

how to read big5 files in Java


/*
  ReadBig5File.java
  
    read a file in big5 code
  
  > javac ReadBig5File.java
  > java ReadBig5File big5.txt
  Big5編碼文字檔
*/
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;

public class ReadBig5File
{
  public static void main(String args[]) throws
    java.io.FileNotFoundException,
    java.io.UnsupportedEncodingException,
    java.io.IOException
  {
    String file="big5.txt";
    FileInputStream fis = new FileInputStream(new File(file));
     // java.io.FileNotFoundException
    BufferedReader br =new BufferedReader(new InputStreamReader(fis,"BIG5"));
     // java.io.UnsupportedEncodingException
    
    // java.io.IOException
    while(br.ready())
    {
      String line=br.readLine();
      System.out.println(line);
      System.out.flush();
    }
    br.close();
  }
}

5 kinds of event handler styles

依處理器所掛的位置,總共有5種寫法,以計時器處理器為例,
A.處理器掛在外部類別下:

 import javax.swing.Timer;
 import java.awt.event.ActionListener;
 import java.awt.event.ActionEvent;
 public class TimerTest1
 {
   public static void main(String args[]) throws Exception
   {
     ActionListener al = new TimerHandler();
     Timer t = new Timer(1000,al);
     t.start();
     Thread.sleep(10000);
     t.stop();
   }
 }
 
class TimerHandler implements ActionListener
 {
   public void actionPerformed(ActionEvent ae)
   {
     System.out.println("執行每次叫醒要作的事1");
   }
 }


B.處理器掛在內部匿名類別下:

 import javax.swing.Timer;
 import java.awt.event.ActionListener;
 import java.awt.event.ActionEvent;
 public class TimerTest2
 {
   public static void main(String args[]) throws Exception
   {
     ActionListener al = new ActionListener()
       {
  public void actionPerformed(ActionEvent ae)
  {
    System.out.println("執行每次叫醒要作的事2");
  }
       };
     Timer t = new Timer(1000,al);
     t.start();
     Thread.sleep(10000);
     t.stop();
   }
 }


C.處理器掛在內部有名類別下:

 import javax.swing.Timer;
 import java.awt.event.ActionListener;
 import java.awt.event.ActionEvent;
 public class TimerTest3
 {
   public static void main(String args[]) throws Exception
   {
     new TimerTest3();
   }

   TimerTest3() throws Exception
   {
     ActionListener al = new TimerHandler();
     Timer t = new Timer(1000,al);
     t.start();
     Thread.sleep(10000);
     t.stop();
   }

   private class TimerHandler implements ActionListener
   {
     public void actionPerformed(ActionEvent ae)
     {
       System.out.println("執行每次叫醒要作的事3");
     }
   }
 }


D.處理器掛在本身類別下:

 import javax.swing.Timer;
 import java.awt.event.ActionListener;
 import java.awt.event.ActionEvent;
 public class TimerTest4 implements ActionListener
 {
   public void actionPerformed(ActionEvent ae)
   {
     System.out.println("執行每次叫醒要作的事4");
   }

   public static void main(String args[]) throws Exception
   {
     ActionListener al = new TimerTest4();
     Timer t = new Timer(1000,al);
     t.start();
     Thread.sleep(10000);
     t.stop();
   }
 }


E.處理器同時掛在內部和外部類別下:

 import javax.swing.Timer;
 import java.awt.event.ActionListener;
 import java.awt.event.ActionEvent;
 public class TimerTest5
 {
   public static void main(String args[]) throws Exception
   {
     ActionListener al = new ActionAdapter()
       {
  public void actionPerformed(ActionEvent ae)
  {
    System.out.println("執行每次叫醒要作的事5x");
  }
       };
     Timer t = new Timer(1000,al);
     t.start();
     Thread.sleep(10000);
     t.stop();
   }
 }

 class ActionAdapter implements ActionListener
 {
   public void actionPerformed(ActionEvent ae)
   {
     System.out.println("執行每次叫醒要作的事5");
   }
 }

strut, glue, rigid area in BoxLayout


盒子排版器(BoxLayout)有3種無互動,純佔面積用之視窗元件可用,
摘要如下,


1.strut (支架)
Component v = Box.createVerticalStrut(h); //新增隱形固定高度h像素之垂直支架
Component h = Box.createHorizontalStrut(w); //新增隱形固定寬度w像素之水平支架

2.glue (黏膠)
Component v = Box.createVerticalGlue(); //新增隱形垂直等間隔黏膠
Component h = Box.createHorizontalGlue(); //新增隱形水平等間隔黏膠
Component g = Box.createGlue(); //新增隱形等間隔黏膠,適用於垂直或水平盒子排版器

3.rigid area (硬塊), 相當於2維支架
Dimension d = new Dimension(h,w); // 新增寬h,高w尺寸
Component ra = Box.createRigidArea(d); // 依給定尺寸新增隱形硬塊

PS:
deitel-php-05-java how to program 6th ed

Disable UDP to solve frequent RDP disconnection

解決 RDP 遠端桌面經常斷線問題:「關閉 UDP 傳輸」 在使用 Windows 內建的 遠端桌面(RDP, Remote Desktop Protocol) 連線時,你是否也常遇到連線突然卡死、畫面凍結,或是頻繁跳出「連線已中...

總網頁瀏覽量