การนับจำนวนครั้งที่ตัวอักษรแต่ละตัวปรากฏในสตริง (Character Frequency Count)
ตัวอย่างนี้สาธิตการนำข้อความจาก EditText มานับจำนวนครั้งที่แต่ละตัวอักษร (Character) ปรากฏ โดยใช้ HashMap<Character, Integer> แล้วแสดงผลใน TextView
ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่
สร้างโปรเจกต์ Android ใหม่ใน Android Studio
ขั้นตอนที่ 2: ออกแบบ Layout (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
android:padding="24dp">
<EditText
android:id="@+id/etInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="ป้อนข้อความเพื่อนับตัวอักษร"
android:inputType="text" />
<Button
android:id="@+id/btnCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="นับและแสดงผล" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="24dp">
<TextView
android:id="@+id/tvResult"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:fontFamily="monospace"
android:lineSpacingExtra="4dp" />
</ScrollView>
</LinearLayout>
ขั้นตอนที่ 3: MainActivity.java
package com.example.myapplication;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.HashMap;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
private EditText etInput;
private TextView tvResult;
private Button btnCount;
private HashMap<Character, Integer> charCountMap = new HashMap<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etInput = findViewById(R.id.etInput);
tvResult = findViewById(R.id.tvResult);
btnCount = findViewById(R.id.btnCount);
btnCount.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String input = etInput.getText().toString();
if (!input.isEmpty()) {
charCountMap.clear(); // ล้างข้อมูลเก่า
// แปลงสตริงเป็นอาร์เรย์ char
char[] charArray = input.toCharArray();
// นับจำนวนแต่ละตัวอักษร
for (char c : charArray) {
// getOrDefault: ถ้ามี key อยู่แล้ว ให้ค่าเดิม + 1, ถ้าไม่มี ให้ 0 + 1 = 1
charCountMap.put(c, charCountMap.getOrDefault(c, 0) + 1);
}
// สร้างข้อความผลลัพธ์ที่อ่านง่าย
StringBuilder sb = new StringBuilder();
sb.append("ความยาวข้อความ: ").append(input.length()).append("\n\n");
sb.append("ตัวอักษร : จำนวนครั้ง\n");
sb.append("--------------------------\n");
// เรียงตามตัวอักษร (Optional) - ใช้ TreeMap แทน HashMap ก็ได้
for (Map.Entry<Character, Integer> entry : charCountMap.entrySet()) {
char ch = entry.getKey();
int count = entry.getValue();
String charDisplay = getCharDisplay(ch);
sb.append(" '").append(charDisplay).append("' : ").append(count).append("\n");
}
tvResult.setText(sb.toString());
Toast.makeText(MainActivity.this, "นับเสร็จสิ้น", Toast.LENGTH_SHORT).show();
} else {
etInput.setError("กรุณาป้อนข้อความ");
}
}
});
}
// แสดงตัวอักษรพิเศษให้เห็นชัด
private String getCharDisplay(char ch) {
switch (ch) {
case ' ': return "[Space]";
case '\t': return "[Tab]";
case '\n': return "[Newline]";
case '\r': return "[CR]";
default: return String.valueOf(ch);
}
}
}
คำอธิบายโค้ดสำคัญ
1. HashMap<Character, Integer> - โครงสร้างข้อมูลนับความถี่
- Key: Character (ตัวอักษรแต่ละตัว)
- Value: Integer (จำนวนครั้งที่ปรากฏ)
- Time Complexity: O(n) โดย n = ความยาวสตริง
2. getOrDefault() - รหัสลดความซับซ้อน
charCountMap.put(c, charCountMap.getOrDefault(c, 0) + 1);
- Java 8+ method - ถ้ามี key ให้ค่า value, ไม่มีให้ default value
- ทดแทนโค้ดเก่า:
// เก่า
if (map.containsKey(c)) {
map.put(c, map.get(c) + 1);
} else {
map.put(c, 1);
}
3. toCharArray() - แปลง String เป็น char[]
char[] charArray = input.toCharArray();
- เร็วกว่า charAt() ใน loop
- รองรับ Unicode (แต่ char เป็น UTF-16 code unit)
ตัวอย่างผลลัพธ์
Input: "Hello World"
ความยาวข้อความ: 11 ตัวอักษร : จำนวนครั้ง -------------------------- 'H' : 1 'e' : 1 'l' : 3 'o' : 2 '[Space]' : 1 'W' : 1 'r' : 1 'd' : 1
การปรับปรุงและขยายความสามารถ
1. เรียงลำดับตามความถี่ (มากไปน้อย)
List<Map.Entry<Character, Integer>> sorted = new ArrayList<>(charCountMap.entrySet()); sorted.sort((a, b) -> b.getValue().compareTo(a.getValue())); // DESC
2. ใช้ TreeMap แทน HashMap เพื่อเรียงตามตัวอักษรอัตโนมัติ
Map<Character, Integer> charCountMap = new TreeMap<>(); // เรียง A-Z อัตโนมัติ
3. นับเฉพาะตัวอักษรภาษาอังกฤษ (Ignore case)
char lower = Character.toLowerCase(c);
if (Character.isLetter(lower)) {
charCountMap.put(lower, charCountMap.getOrDefault(lower, 0) + 1);
}
4. รองรับ Emoji/Unicode ให้ถูกต้อง (Code Points)
// ใช้ codePointCount และ codePointAt สำหรับ Supplementary characters
input.codePoints().forEach(cp -> {
charCountMap.merge((char) cp, 1, Integer::sum); // เฉพาะ BMP
// สำหรับ full Unicode ต้องใช้ Map<Integer, Integer> (code point เป็น int)
});
กรณีการใช้งานจริง
- วิเคราะห์ความถี่ตัวอักษร (Frequency Analysis) - ทฤษฎีการเข้ารหัส
- ตรวจสอบคำซ้ำ/คำที่ใช้บ่อยในข้อความ
- เกมคำ (Word games) - Scrabble, Wordle helper
- Data compression (Huffman coding ต้องการ frequency table)
- Password strength analysis
ข้อควรระวัง
- char ใน Java เป็น UTF-16 - Emoji บางตัวใช้ 2 chars (Surrogate pairs)
- สำหรับ Unicode เต็มรูปแบบ ใช้
int(code point) แทนchar - HashMap ไม่รักษาลำดับ - ใช้ LinkedHashMap (รักษาลำดับเพิ่ม) หรือ TreeMap (เรียงตัวอักษร)