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

วิธีจัดเก็บข้อมูลใน ListView เรียงลำดับตัวเลขบน Android

การจัดเก็บและแสดงข้อมูลตัวเลขใน ListView เรียงลำดับบน Android

ตัวอย่างนี้สาธิตการจัดเก็บข้อมูลตัวเลขใน ArrayList แล้วใช้ Collections.sort() ร่วมกับ Comparator เพื่อเรียงลำดับตัวเลขอย่างถูกต้อง (Numeric Sort) ไม่ใช่ Lexicographic Sort ก่อนแสดงใน ListView

ขั้นตอนที่ 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="16dp">

    <EditText
        android:id="@+id/etNumber"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="ป้อนตัวเลข"
        android:inputType="numberDecimal|numberSigned" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:orientation="horizontal">
        <Button android:id="@+id/btnSave" android:text="บันทึก" android:layout_width="wrap_content" android:layout_height="wrap_content" />
        <Button android:id="@+id/btnRefresh" android:text="รีเฟรช" android:layout_width="wrap_content" android:layout_height="wrap_content" />
    </LinearLayout>

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp">
    </ListView>
</LinearLayout>

ขั้นตอนที่ 3: MainActivity.java

package com.example.myapplication;

import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class MainActivity extends AppCompatActivity {
    private EditText etNumber;
    private ListView listView;
    private ArrayAdapter<String> adapter;
    private ArrayList<String> arrayList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        etNumber = findViewById(R.id.etNumber);
        listView = findViewById(R.id.listView);

        findViewById(R.id.btnRefresh).setOnClickListener(v -> {
            if (adapter != null) {
                adapter.notifyDataSetChanged();
                listView.invalidateViews();
                listView.refreshDrawableState();
            }
        });

        findViewById(R.id.btnSave).setOnClickListener(v -> {
            String input = etNumber.getText().toString().trim();
            if (!input.isEmpty()) {
                // เพิ่มข้อมูล (เก็บเป็น String แต่เป็นตัวเลข)
                arrayList.add(input);

                // จุดสำคัญ: เรียงลำดับแบบ Numeric (ตัวเลข) ไม่ใช่ Lexicographic (ข้อความ)
                Collections.sort(arrayList, new Comparator<String>() {
                    @Override
                    public int compare(String s1, String s2) {
                        try {
                            // แปลงเป็น Double เพื่อเปรียบเทียบค่าตัวเลข
                            double d1 = Double.parseDouble(s1);
                            double d2 = Double.parseDouble(s2);
                            return Double.compare(d1, d2);
                        } catch (NumberFormatException e) {
                            // หากแปลงไม่ได้ ใช้ String Compare เป็น Fallback
                            return s1.compareTo(s2);
                        }
                    }
                });

                adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, arrayList);
                listView.setAdapter(adapter);

                Toast.makeText(this, "บันทึกสำเร็จ (เรียงตัวเลข)", Toast.LENGTH_SHORT).show();
            } else {
                etNumber.setError("กรุณาป้อนตัวเลข");
            }
        });
    }
}

ปัญหา: Lexicographic Sort vs Numeric Sort

เมื่อเก็บตัวเลขเป็น String และใช้ Collections.sort() แบบธรรมดา (Natural Order) จะเกิดการเรียงแบบ Lexicographic (Dictionary Order):

Input OrderLexicographic Sort (String)Numeric Sort (Number)
10, 2, 30, 4, 510, 2, 30, 4, 52, 4, 5, 10, 30
100, 20, 3100, 20, 33, 20, 100
1.5, 10.2, 2.11.5, 10.2, 2.11.5, 2.1, 10.2

เหตุผล: String Compare เปรียบเทียบทีละตัวอักษร: '1' < '2' → "10" < "2"

วิธีแก้: ใช้ Comparator แปลงเป็นตัวเลขก่อนเปรียบเทียบ

1. ใช้ Double.compare() (รองรับทศนิยมและลบ)

Collections.sort(list, (s1, s2) -> {
    try {
        return Double.compare(Double.parseDouble(s1), Double.parseDouble(s2));
    } catch (NumberFormatException e) {
        return s1.compareTo(s2); // Fallback
    }
});

2. ใช้ Integer.compare() (เฉพาะจำนวนเต็ม)

Collections.sort(list, (s1, s2) -> 
    Integer.compare(Integer.parseInt(s1), Integer.parseInt(s2)));

3. ใช้ BigDecimal (ความแม่นยำสูง - Financial)

import java.math.BigDecimal;
Collections.sort(list, (s1, s2) -> 
    new BigDecimal(s1).compareTo(new BigDecimal(s2)));

4. Java 8+ Method Reference (สะดวก)

// ต้องแปลงเป็น List<Double> ก่อน หรือใช้ Custom Comparator
list.sort(Comparator.comparingDouble(Double::parseDouble));

แนวทางที่ดีกว่า: เก็บเป็น Number Object โดยตรง

// แทน ArrayList<String> ใช้ ArrayList<Double> หรือ ArrayList<Integer>
ArrayList<Double> numbers = new ArrayList<>();
numbers.add(10.0);
numbers.add(2.0);
numbers.add(30.0);

// เรียงได้โดยตรง (Double implements Comparable<Double>)
Collections.sort(numbers); // [2.0, 10.0, 30.0]

// แสดงใน ListView - ต้องแปลงเป็น String ก่อน
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, 
    android.R.layout.simple_list_item_1, 
    numbers.stream().map(String::valueOf).collect(Collectors.toList()));

ตัวอย่างการทำงานของแอป

  1. ป้อน: 10 → ListView: [10]
  2. ป้อน: 2 → ListView: [2, 10] (เรียง 2 ก่อน 10)
  3. ป้อน: 30 → ListView: [2, 10, 30]
  4. ป้อน: 4 → ListView: [2, 4, 10, 30]
  5. ป้อน: 5 → ListView: [2, 4, 5, 10, 30]

ขยายความสามารถ: เรียงแบบหลายเงื่อนไข

// สมมติข้อมูลเป็น Object: { value: Double, label: String }
list.sort(Comparator
    .comparingDouble(Item::getValue)      // 1. เรียงตามค่าตัวเลข
    .thenComparing(Item::getLabel));      // 2. ถ้าเท่ากัน เรียงตาม Label A-Z

กรณีการใช้งานจริง

  • Leaderboard: คะแนนสูงสุด (Descending)
  • ราคาสินค้า: ราคาต่ำสุด (Ascending)
  • ระยะทาง: ใกล้ที่สุด (Ascending)
  • อุณหภูมิ/ค่าเซ็นเซอร์: เรียงตามค่า
  • Financial Data: ใช้ BigDecimal ป้องกัน Floating Point Error

Performance Tips

  • เก็บเป็น Primitive/Wrapper Type (Double, Integer) แทน String → เร็วกว่า, ถูกต้องกว่า
  • ใช้ ArrayList.sort(Comparator) (Java 8+) แทน Collections.sort()
  • สำหรับข้อมูลมาก (>10,000) พิจารณา Database Query dengan ORDER BY
  • Cache ผลลัพธ์การแปลง String→Number หาก Sort บ่อย

สรุป

การเรียงตัวเลขที่เก็บเป็น String ต้องใช้ Custom Comparator ที่แปลงเป็น Number ก่อนเปรียบเทียบ มิฉะนั้นจะได้ Lexicographic Sort ที่ผิดสำหรับตัวเลข แนวทางที่ดีที่สุดคือเก็บเป็น Numeric Type ตั้งแต่ต้น