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

วิธีใช้ SortedSet Interface ใน Android

SortedSet ใน Android: จัดเก็บข้อมูลไม่ซ้ำและเรียงลำดับอัตโนมัติ

SortedSet เป็น Interface ใน Java Collections Framework ที่ขยายจาก Set โดยรับประกันว่า: (1) ไม่มีค่าซ้ำ (Unique) และ (2) จัดเรียงตามลำดับธรรมชาติ (Natural Ordering) หรือ Comparator ที่กำหนด ใช้งานผ่าน TreeSet ซึ่งเป็น Implementation หลัก

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

Android Studio → File → New Project → Empty Activity

ขั้นตอนที่ 2: Layout - activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="vertical"
        android:padding="16dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="SortedSet (TreeSet) Demo"
            android:textSize="18sp"
            android:textStyle="bold"
            android:layout_marginBottom="16dp" />

        <EditText
            android:id="@+id/etName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="กรอกชื่อ (จะเรียง A-Z อัตโนมัติ)"
            android:layout_marginBottom="16dp" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginBottom="16dp">

            <Button
                android:id="@+id/btnAdd"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginEnd="4dp"
                android:text="เพิ่ม" />

            <Button
                android:id="@+id/btnClear"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginStart="4dp"
                android:text="ล้างทั้งหมด" />

        </LinearLayout>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rvNames"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

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

package com.example.sortedsetdemo;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

public class MainActivity extends AppCompatActivity {

    private EditText etName;
    private Button btnAdd, btnClear;
    private RecyclerView rvNames;
    private NameAdapter adapter;
    private SortedSet<String> sortedNames;

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

        // TreeSet เรียงตาม Natural Ordering (String = Alphabetical)
        sortedNames = new TreeSet<>();

        etName = findViewById(R.id.etName);
        btnAdd = findViewById(R.id.btnAdd);
        btnClear = findViewById(R.id.btnClear);
        rvNames = findViewById(R.id.rvNames);
        rvNames.setLayoutManager(new LinearLayoutManager(this));

        adapter = new NameAdapter(new ArrayList<>());
        rvNames.setAdapter(adapter);

        btnAdd.setOnClickListener(v -> addName());
        btnClear.setOnClickListener(v -> {
            sortedNames.clear();
            adapter.setData(new ArrayList<>());
            Toast.makeText(this, "ล้างข้อมูลแล้ว", Toast.LENGTH_SHORT).show();
        });
    }

    private void addName() {
        String name = etName.getText().toString().trim();
        if (name.isEmpty()) {
            Toast.makeText(this, "กรุณากรอกชื่อ", Toast.LENGTH_SHORT).show();
            return;
        }

        // TreeSet.add() คืนค่า true ถ้าเพิ่มสำเร็จ (ไม่ซ้ำ), false ถ้ามีอยู่แล้ว
        boolean added = sortedNames.add(name);
        
        if (added) {
            Toast.makeText(this, "เพิ่ม '" + name + "' เรียบร้อย", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "'" + name + "' มีอยู่แล้ว (ไม่ซ้ำ)", Toast.LENGTH_SHORT).show();
        }

        // อัปเดต RecyclerView (TreeSet เรียงลำดับอัตโนมัติ)
        adapter.setData(new ArrayList<>(sortedNames));
        etName.setText("");
    }
}

NameAdapter.java

package com.example.sortedsetdemo;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class NameAdapter extends RecyclerView.Adapter<NameAdapter.ViewHolder> {

    private List<String> items;

    public NameAdapter(List<String> items) {
        this.items = items;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(android.R.layout.simple_list_item_1, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.textView.setText(items.get(position));
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    public void setData(List<String> newData) {
        this.items = newData;
        notifyDataSetChanged();
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        TextView textView;
        ViewHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(android.R.id.text1);
        }
    }
}

คุณสมบัติหลักของ SortedSet / TreeSet

คุณสมบัติคำอธิบาย
Unique Elementsไม่รับค่าซ้ำ (ใช้ equals() ตรวจสอบ)
Sorted Orderเรียงตาม Natural Order (Comparable) หรือ Comparator
Null Valuesไม่รองรับ null (จะโยน NullPointerException)
Thread Safetyไม่ Thread-Safe (ต้อง Synchronize เองหรือใช้ ConcurrentSkipListSet)
PerformanceO(log n) สำหรับ add, remove, contains

SortedSet Methods ที่น่าสนใจ

SortedSet<String> set = new TreeSet<>();

set.add("Charlie");
set.add("Alpha");
set.add("Beta");
// set = [Alpha, Beta, Charlie] (เรียงอัตโนมัติ)

// Navigation Methods
set.first();        // "Alpha" - ค่าน้อยที่สุด
set.last();         // "Charlie" - ค่ามากที่สุด

set.headSet("Beta");    // [Alpha] - น้อยกว่า "Beta" (Exclusive)
set.tailSet("Beta");    // [Beta, Charlie] - มากกว่าหรือเท่า "Beta" (Inclusive)
set.subSet("Beta", "Charlie"); // [Beta] - ระหว่าง Beta ถึง Charlie

// Poll (ดึงและลบ)
set.pollFirst();  // ลบและคืนค่าน้อยที่สุด
set.pollLast();   // ลบและคืนค่ามากที่สุด

// Descending View
NavigableSet<String> descending = set.descendingSet(); // [Charlie, Beta, Alpha]

Custom Comparator (เรียงแบบกำหนดเอง)

// เรียงตามความยาวสตริง (สั้นไปยาว)
SortedSet<String> byLength = new TreeSet<>((a, b) -> {
    int lenCompare = Integer.compare(a.length(), b.length());
    if (lenCompare != 0) return lenCompare;
    return a.compareTo(b); // ถ้ายาวเท่ากัน เรียงตามอักษร
});

byLength.add("A");
byLength.add("BBB");
byLength.add("CC");
byLength.add("DDDD");
// ผล: [A, CC, BBB, DDDD]

Thread-Safe Alternative: ConcurrentSkipListSet

import java.util.concurrent.ConcurrentSkipListSet;

// Thread-safe SortedSet (Lock-free, Scalable)
SortedSet<String> concurrentSortedSet = new ConcurrentSkipListSet<>();
// ใช้ได้ใน Multi-thread Environment โดยไม่ต้อง Synchronize

การใช้งานจริงใน Android

  1. Auto-complete/Suggestions: เก็บคำที่ผู้ใช้พิมพ์ ไม่ซ้ำ เรียง A-Z
  2. Tag/Category Management: จัดการ Tags เรียงลำดับ
  3. Leaderboard/Ranking: ใช้ TreeMap<Score, List<User>> หรือ TreeSet กับ Custom Comparator
  4. Deduplication + Sorting: กรองข้อมูลซ้ำจาก API แล้วแสดงเรียงลำดับ

สรุป

TreeSet (SortedSet) เหมาะสำหรับข้อมูลที่ต้องการ Unique + Sorted พร้อมกัน ใช้งานง่าย แค่ add() แล้วมันจัดเรียงให้อัตโนมัติ รองรับ Navigation Methods (first, last, headSet, tailSet) และ Custom Comparator สำหรับการเรียงแบบกำหนดเอง