Java 8 Streams: Filter හා Map Operations | දත්ත පහසුවෙන් හසුරුවමු SC Guide

Java 8 Streams: Filter හා Map Operations | දත්ත පහසුවෙන් හසුරුවමු SC Guide

ආයුබෝවන් හැමෝටම! කොහොමද ඉතින්? අද අපි කතා කරන්න යන්නේ Java programming වල හරිම වැදගත් සහ ප්‍රයෝජනවත් දෙයක් ගැන. ඒ තමයි Java 8 Streams. විශේෂයෙන්ම, Streams වල තියෙන filter() සහ map() කියන operations දෙක කොහොමද අපේ code එක ලස්සනට, පහසුවෙන් තේරුම් ගන්න පුළුවන් විදියට ලියන්න උදව් කරන්නේ කියලා බලමු.

ඉස්සර අපි data processing කරන්න, collections manipulate කරන්න loops (for loops, while loops) නිතරම භාවිතා කළා නේද? ඒත් Java 8 එක්ක ආපු Streams වලින් ඒ වැඩේට අලුත්, functional programming style එකක් ආවා. මේකෙන් අපේ code එකට වෙනමම ලස්සනක් ලැබෙනවා. අපි බලමු මේ "Magic" එක කොහොමද වෙන්නේ කියලා.

මොනවද මේ Java Streams කියන්නේ?

ලෝකේ ගොඩක් දේවල් "Stream" එකක් විදියට හිතන්න පුළුවන්. උදාහරණයක් විදියට ගත්තොත්, ගඟක වතුර Stream එකක්, TV එකක Live Stream එකක්. මේ හැම එකකම පොදු දෙයක් තියෙනවා: ඒ තමයි "data flow" එක. Java Streams කියන්නෙත් ඒ වගේම data flow එකක්. මේක අපිට Collections (List, Set වගේ), Arrays, Input/Output channels වගේ තැන්වලින් එන data set එකක් declarative විදියට process කරන්න පුළුවන් powerful API එකක්.

Streams වල ප්‍රධාන වාසි කිහිපයක් තියෙනවා:

  • Declarative Style: අපේ code එක කියවනකොට මොකක්ද වෙන්නේ කියලා තේරුම් ගන්න ලේසියි. "කොහොමද කරන්නේ" (how to do it) කියන එකට වඩා "මොකක්ද කරන්නේ" (what to do) කියන එකට අවධානය යොමු කරනවා.
  • Functional Programming: Lambda expressions, Method references වගේ දේවල් එක්ක එකට වැඩ කරන නිසා code එක කෙටි වෙනවා වගේම කියවන්නත් ලේසියි.
  • Pipelining: විවිධ operations එකිනෙක සම්බන්ධ කරලා data processing pipeline එකක් හදන්න පුළුවන්.
  • Parallel Processing: අවශ්‍ය නම්, එකම stream එක parallel විදියට process කරන්නත් පුළුවන්. (අද අපි ඒ ගැන ගැඹුරින් කතා කරන්නේ නැහැ.)

Streams වල operations දෙවර්ගයක් තියෙනවා: Intermediate Operations සහ Terminal Operations. filter() සහ map() කියන්නේ Intermediate Operations. මේවා කිසිම result එකක් produce කරන්නේ නැහැ, ඒ වෙනුවට තවත් Stream එකක් return කරනවා. Terminal Operation එකක් ආවට පස්සේ තමයි actual processing එක වෙන්නේ. (උදා: forEach(), collect(), count())

Filter() මෙහෙයුම: දත්ත පෙරාගැනීම

හිතන්නකෝ ඔයාලගේ ගෙදරට එන කිරි ටික filter කරනවා වගේ වැඩක්. අපිට ඕන කරන දේවල් විතරක් තියාගෙන, අනවශ්‍ය දේවල් අයින් කරනවා. Java Stream වල filter() method එකත් කරන්නේ ඒ වගේ වැඩක්. මේකෙන් කරන්නේ Stream එකක තියෙන elements වලින් අපිට අවශ්‍ය Condition එකක් satisfy කරන elements ටික විතරක් තෝරලා අලුත් Stream එකක් හදන එක.

filter() method එකට Predicate කියන Functional Interface එකක් අවශ්‍ය වෙනවා. මේ Predicate එක true return කරන elements ටික අලුත් Stream එකට යනවා.

උදාහරණයක්: ඉරට්ටේ සංඛ්‍යා (Even Numbers) තෝරාගැනීම

අපි හිතමු අපිට numbers list එකක් තියෙනවා කියලා. ඒකෙන් ඉරට්ටේ සංඛ්‍යා (even numbers) ටික විතරක් select කරගන්න ඕනේ.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class FilterExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // සාම්ප්‍රදායික විදියට (Traditional Way)
        System.out.println("Traditional way:");
        for (Integer number : numbers) {
            if (number % 2 == 0) {
                System.out.print(number + " ");
            }
        }
        System.out.println("\n");

        // Stream එකක් භාවිතා කරලා (Using Streams)
        System.out.println("Using Stream filter():");
        List<Integer> evenNumbers = numbers.stream()
                                           .filter(number -> number % 2 == 0) // මෙතනදී තමයි filter() එක වැඩ කරන්නේ
                                           .collect(Collectors.toList());

        evenNumbers.forEach(System.out::print); // 2 4 6 8 10
        System.out.println("\n");

        // තව උදාහරණයක්: නම් වලින් 'A' අකුරෙන් පටන් ගන්න ඒවා තෝරාගැනීම
        List<String> names = Arrays.asList("Anusha", "Bimal", "Amara", "Chamara", "Amal");
        List<String> aNames = names.stream()
                                  .filter(name -> name.startsWith("A"))
                                  .collect(Collectors.toList());

        System.out.println("Names starting with 'A': " + aNames); // [Anusha, Amara, Amal]
    }
}

දැක්කනේ? filter(number -> number % 2 == 0) කියන එකෙන් කොච්චර ලේසියෙන් අපිට අවශ්‍ය Condition එක Apply කරන්න පුළුවන්ද කියලා. number -> number % 2 == 0 කියන්නේ Lambda Expression එකක්. මේක Predicate එකක් විදියට වැඩ කරනවා.

Map() මෙහෙයුම: දත්ත පරිවර්තනය

map() operation එකට "පරිවර්තනය" (transformation) කියන එක හරියටම ගැලපෙනවා. Stream එකක තියෙන හැම element එකක්ම අරගෙන, ඒක වෙනත් type එකක, වෙනත් form එකක element එකක් බවට පත් කරනවා. උදාහරණයක් විදියට, List<String> එකක තියෙන නම් ටික List<Integer> එකකට, ඒ නම් වල දිග (length) විදියට transform කරන්න පුළුවන්.

map() method එකට Function කියන Functional Interface එකක් අවශ්‍ය වෙනවා. මේ Function එක input එකක් අරගෙන output එකක් return කරනවා.

උදාහරණයක්: String වල දිග (Length) ලබාගැනීම

අපි හිතමු අපිට නම් list එකක් තියෙනවා කියලා. අපිට ඕනේ ඒ හැම නමකම දිග (length) අලුත් list එකකට ගන්න.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class MapExample {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("apple", "banana", "cat", "dog");

        // සාම්ප්‍රදායික විදියට
        System.out.println("Traditional way:");
        for (String word : words) {
            System.out.print(word.length() + " ");
        }
        System.out.println("\n");

        // Stream එකක් භාවිතා කරලා
        System.out.println("Using Stream map():");
        List<Integer> wordLengths = words.stream()
                                         .map(String::length) // මෙතනදී තමයි map() එක වැඩ කරන්නේ
                                         .collect(Collectors.toList());

        wordLengths.forEach(System.out::print); // 5 6 3 3
        System.out.println("\n");

        // තව උදාහරණයක්: User objects වලින් user names ටික විතරක් ගන්න
        class User {
            String name;
            int age;

            public User(String name, int age) {
                this.name = name;
                this.age = age;
            }

            public String getName() {
                return name;
            }
        }

        List<User> users = Arrays.asList(
            new User("Nimal", 25),
            new User("Kamala", 30),
            new User("Sunil", 22)
        );

        List<String> userNames = users.stream()
                                     .map(User::getName) // user object එක user name එකට map කරනවා
                                     .collect(Collectors.toList());

        System.out.println("User Names: " + userNames); // [Nimal, Kamala, Sunil]
    }
}

map(String::length) සහ map(User::getName) වගේ Method References භාවිතා කිරීමෙන් code එක තවත් කෙටි කරගන්න පුළුවන්. මේකෙන් කියවෙන්නේ String object එකක length() method එක call කරන්න, නැත්නම් User object එකක getName() method එක call කරන්න කියන එකයි.

Filter() සහ Map() එකට භාවිතා කරමු

Streams වල තියෙන සුන්දරම දේ තමයි අපිට මේ Intermediate Operations කිහිපයක් එකිනෙකට සම්බන්ධ කරලා (chaining) data processing pipeline එකක් හදන්න පුළුවන් වීම. මේකෙන් code එක හරිම කියවන්න ලේසි, තේරුම් ගන්න පහසු වෙනවා.

අපි බලමු උදාහරණයක්. අපිට Product objects list එකක් තියෙනවා කියලා හිතමු. අපිට අවශ්‍යයි, මිල රුපියල් 1000ට වඩා වැඩි, "Electronics" category එකට අයිති products වල නම් (Name) ටික uppercase කරලා list එකකට ගන්න.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

class Product {
    String name;
    double price;
    String category;

    public Product(String name, double price, String category) {
        this.name = name;
        this.price = price;
        this.category = category;
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    public String getCategory() {
        return category;
    }

    @Override
    public String toString() {
        return "Product{" +
               "name='" + name + '\'' +
               ", price=" + price +
               ", category='" + category + '\'' +
               '}';
    }
}

public class FilterMapCombinedExample {
    public static void main(String[] args) {
        List<Product> products = Arrays.asList(
            new Product("Laptop", 1500.0, "Electronics"),
            new Product("Shirt", 500.0, "Apparel"),
            new Product("Mouse", 50.0, "Electronics"),
            new Product("Keyboard", 1200.0, "Electronics"),
            new Product("Shoes", 800.0, "Apparel")
        );

        // මිල 1000ට වැඩි සහ Electronics category එකේ තියෙන products වල නම් uppercase කරලා ගන්න
        List<String> filteredAndTransformedProductNames = products.stream()
            .filter(p -> p.getPrice() > 1000) // පළමු filter: මිල 1000ට වඩා වැඩි products
            .filter(p -> p.getCategory().equals("Electronics")) // දෙවන filter: Electronics category එකේ products
            .map(p -> p.getName().toUpperCase()) // map: product name එක uppercase කරනවා
            .collect(Collectors.toList()); // Terminal Operation: list එකකට collect කරනවා

        System.out.println("Filtered and Transformed Product Names: " + filteredAndTransformedProductNames);
        // Output: Filtered and Transformed Product Names: [LAPTOP, KEYBOARD]
    }
}

මේ code එක බලන්නකො, කොච්චර කියවන්න ලේසිද කියලා. එක පේලියකින් අපිට අවශ්‍ය data processing pipeline එක ගොඩනගන්න පුළුවන් වෙලා තියෙනවා. Stream එකක් හරහා data එක ගමන් කරනකොට, මුලින්ම මිල condition එකෙන් filter වෙනවා, ඊට පස්සේ category condition එකෙන් filter වෙනවා, අන්තිමට ඒ ඉතුරු වෙච්ච product object වල name එක uppercase වෙනවා. හරිම neat නේද?

අවසාන වශයෙන්

ඉතින්, Java 8 Streams වල filter() සහ map() operations දෙක කොච්චර powerful ද කියලා ඔයාලට තේරෙන්න ඇති කියලා මම හිතනවා. මේවා භාවිතා කිරීමෙන් අපිට data processing tasks හරිම elegant විදියට කරන්න පුළුවන් වෙනවා. Traditional loops වලින් කරනකොට code එකේ readability එක අඩු වෙන්න පුළුවන්, ඒත් Streams වලින් Functional Programming concept එක ගෙනල්ලා code එකේ quality එක වැඩි කරනවා.

මේවා තව තවත් practice කරන්න. ඔයාලගේ project වල පුංචි පුංචි තැන්වලින් Stream API එක භාවිතා කරන්න පුරුදු වෙන්න. එතකොට ඔයාලට මේකේ නියම වටිනාකම තේරෙයි.

අදට එච්චරයි! මේ post එක ගැන ඔයාලගේ අදහස්, ප්‍රශ්න පහළින් comment කරන්න අමතක කරන්න එපා. තව මොන වගේ topics ගැනද ඔයාලට දැනගන්න ඕනේ කියලත් කියන්න. ආයෙත් මේ වගේ තවත් වැදගත් topic එකකින් හමුවෙමු! ගිහින් එන්නම්! 🙏