Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีนับจำนวนตัวอักษรซ้ำในสตริงบน Android

การนับจำนวนครั้งที่ตัวอักษรแต่ละตัวปรากฏในสตริง (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 (เรียงตัวอักษร)