Java Collections Framework: Map Interface, Variable Arguments, and Stream API

Map Interface

Overview and Characteristics

The Map interface represents a collection that maps keys to values. Each key can map to at most one value.

interface Map<K,V> where K is the key type and V is the value type

Key characteristics:

  • Key-value pair structure with one-to-one mapping between keys and values
  • Keys must be unique within the collection
  • Values can be duplicated across different keys

Basic implementation example:

public class MapExample {
    public static void main(String[] args) {
        // Create map instance
        Map<String, String> actorMap = new HashMap<String, String>();

        // Add key-value pairs using put method
        actorMap.put("actor001", "Meryl Streep");
        actorMap.put("actor002", "Leonardo DiCaprio");
        actorMap.put("actor003", "Scarlett Johansson");
        // Duplicate key overwrites previous value
        actorMap.put("actor003", "Emma Stone");

        // Display the complete map
        System.out.println(actorMap);
    }
}

Core Operations

Common methods available in Map implementations:

Method Descriptoin
V put(K key, V value) Adds a key-value mapping
V remove(Object key) Removes mapping by key
void clear() Empties all mappings
boolean containsKey(Object key) Checks for specific key existence
boolean containsValue(Object value) Checks for specific value existence
boolean isEmpty() Tests if map is empty
int size() Returns number of key-value pairs

Implementation example:

public class MapOperations {
    public static void main(String[] args) {
        Map<String, String> movieMap = new HashMap<String, String>();

        // Adding entries
        movieMap.put("director1", "Christopher Nolan");
        movieMap.put("director2", "Quentin Tarantino");
        movieMap.put("director3", "Martin Scorsese");

        // Removing by key
        // System.out.println(movieMap.remove("director2"));
        
        // Check key existence
        // System.out.println(movieMap.containsKey("director1"));
        
        // Get map size
        System.out.println("Number of entries: " + movieMap.size());
        
        System.out.println(movieMap);
    }
}

Retrieval Methods

Essential methods for accessing map data:

Method Purpose
V get(Object key) Retrieves value by key
Set<K> keySet() Gets all keys
Collection<V> values() Gets all values
Set<Map.Entry<K,V>> entrySet() Gets all key-value pairs

Usage example:

public class MapRetrieval {
    public static void main(String[] args) {
        Map<String, String> directorMap = new HashMap<String, String>();
        
        directorMap.put("movie1", "Steven Spielberg");
        directorMap.put("movie2", "Ridley Scott");
        directorMap.put("movie3", "James Cameron");

        // Retrieve all values
        Collection<String> directors = directorMap.values();
        for(String director : directors) {
            System.out.println(director);
        }
    }
}

Traversal Approach 1: Key-Based Access

This approach involves:

  • Obtaining the complete set of keys
  • Iterating through each key
  • Using each key to retrieve its corresponding value

Implementation:

public class MapTraversalKeys {
    public static void main(String[] args) {
        Map<String, String> characterMap = new HashMap<String, String>();
        characterMap.put("hero1", "Superman");
        characterMap.put("hero2", "Batman");
        characterMap.put("hero3", "Wonder Woman");

        Set<String> keyCollection = characterMap.keySet();
        for (String key : keyCollection) {
            String value = characterMap.get(key);
            System.out.println(key + ": " + value);
        }
    }
}

Traversal Approach 2: Entry-Based Access

Alternative approach using entry objects:

  • Acquire the complete set of key-value entry objects
  • Iterate through each entry object
  • Extract both key and value from each entry

Implementation:

public class MapTraversalEntries {
    public static void main(String[] args) {
        Map<String, String> filmMap = new HashMap<String, String>();
        filmMap.put("film1", "Inception");
        filmMap.put("film2", "Pulp Fiction");
        filmMap.put("film3", "Goodfellas");

        Set<Map.Entry<String, String>> entryCollection = filmMap.entrySet();
        for (Map.Entry<String, String> entry : entryCollection) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + ": " + value);
        }
    }
}

HashMap Implementation

Structure and Uniqueness

HashMap uses a hash table underlying structure. It relies on hashCode() and equals() methods to ansure key uniqueness. For custom objects as keys, these methods must be properly overridden.

Practical Example with Custom Objects

Scenario: Creating a map with Person objects as keys and location strings as values.

Person class implementation:

public class Person {
    private String name;
    private int age;

    public Person() {}

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

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        if (age != person.age) return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
}

Testing implementation:

public class HashMapExample {
    public static void main(String[] args) {
        HashMap<Person, String> locationMap = new HashMap<Person, String>();

        Person p1 = new Person("Alice", 25);
        Person p2 = new Person("Bob", 30);
        Person p3 = new Person("Charlie", 28);
        Person p4 = new Person("Alice", 25); // Same as p1

        locationMap.put(p1, "New York");
        locationMap.put(p2, "Los Angeles");
        locationMap.put(p3, "Chicago");
        locationMap.put(p4, "Miami"); // Should overwrite p1's entry

        Set<Person> keySet = locationMap.keySet();
        for (Person key : keySet) {
            String value = locationMap.get(key);
            System.out.println(key.getName() + ", " + key.getAge() + ", " + value);
        }
    }
}

TreeMap Implementation

Structure and Sorting

TreeMap uses a red-black tree structure. It sorts entries based on natural ordering of keys or a custom comparator. For custom objects, implement Comparable or provide a custom comparator.

Sorting Example with Custom Objects

Person class with sorting implementation:

class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person() {}

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

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }

    @Override
    public int compareTo(Person other) {
        // Primary sort by age (descending)
        int ageComparison = Integer.compare(other.age, this.age);
        // Secondary sort by name if ages are equal
        if (ageComparison == 0) {
            return other.name.compareTo(this.name);
        }
        return ageComparison;
    }
}

TreeMap usage example:

public class TreeMapExample {
    public static void main(String[] args) {
        TreeMap<Person, String> sortedMap = new TreeMap<>();
        
        Person p1 = new Person("John", 28);
        Person p2 = new Person("Sarah", 25);
        Person p3 = new Person("Mike", 25);
        
        sortedMap.put(p1, "California");
        sortedMap.put(p2, "Texas");
        sortedMap.put(p3, "Florida");
        
        sortedMap.forEach((person, location) -> {
            System.out.println(person + " --- " + location);
        });
    }
}

Character Counting Example

Count occurrences of each character in a string:

public class CharacterCounter {
    public static void main(String[] args) {
        String input = "aaabbbcccdde";
        TreeMap<Character, Integer> charCount = new TreeMap<>();

        for (int i = 0; i < input.length(); i++) {
            char currentChar = input.charAt(i);
            if (!charCount.containsKey(currentChar)) {
                charCount.put(currentChar, 1);
            } else {
                int existingCount = charCount.get(currentChar);
                charCount.put(currentChar, existingCount + 1);
            }
        }
        
        charCount.forEach((ch, count) -> {
            System.out.print(ch + "(" + count + ")");
        });
    }
}

Variable Arguments

Definition and Usage

Variable arguments allow methods to accept an arbitrary number of parameters of the same type. The variable argument must be the last parameter in the method signature.

Definition syntax:

modifier returnType methodName(dataType... variableName) { }

Implementation example:

public class VariableArgsExample {
    public static void main(String[] args) {
        System.out.println(calculateSum(5, 10));
        System.out.println(calculateSum(5, 10, 15));
        System.out.println(calculateSum(5, 10, 15, 20));
        System.out.println(calculateSum(5, 10, 15, 20, 25, 30));
    }

    public static int calculateSum(int... numbers) {
        int total = 0;
        for (int num : numbers) {
            total += num;
        }
        return total;
    }
}

Immutable Collections

Creating unmodifiable collections using static factory methods:

public class ImmutableCollections {
    public static void main(String[] args) {
        // Create immutable list
        List<String> immutableList = List.of("apple", "banana", "cherry");
        
        // Create immutable map with entries
        Map<String, String> immutableMap = Map.ofEntries(
            Map.entry("first", "value1"),
            Map.entry("second", "value2")
        );
        
        // Convert immutable to mutable
        ArrayList<String> mutableList = new ArrayList<>(List.of("item1", "item2", "item3"));
        
        System.out.println(immutableList);
        System.out.println(immutableMap);
        System.out.println(mutableList);
    }
}

Stream API

Introduction and Benefits

Stream API provides a functional programming approach for processing sequences of elements. It allows for concise and readable operations on collections.

Traditional approach vs. Stream approach:

public class StreamComparison {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<String>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David");
        names.add("Alex");
        names.add("Anna");

        // Traditional approach
        ArrayList<String> filteredNames = new ArrayList<String>();
        for (String name : names) {
            if (name.startsWith("A")) {
                filteredNames.add(name);
            }
        }
        
        ArrayList<String> finalResult = new ArrayList<String>();
        for (String name : filteredNames) {
            if (name.length() == 4) {
                finalResult.add(name);
            }
        }
        
        for (String name : finalResult) {
            System.out.println(name);
        }
        
        System.out.println("--- Stream approach ---");
        
        // Stream approach
        names.stream()
             .filter(name -> name.startsWith("A"))
             .filter(name -> name.length() == 4)
             .forEach(System.out::println);
    }
}

Stream Creation Methods

Different ways to create streams:

public class StreamCreation {
    public static void main(String[] args) {
        // From Collection
        List<String> list = new ArrayList<String>();
        Stream<String> listStream = list.stream();
        
        Set<String> set = new HashSet<String>();
        Stream<String> setStream = set.stream();
        
        // From Map
        Map<String, Integer> map = new HashMap<String, Integer>();
        Stream<String> keyStream = map.keySet().stream();
        Stream<Integer> valueStream = map.values().stream();
        
        // From Array
        String[] array = {"hello", "world", "java"};
        Stream<String> arrayStream = Arrays.stream(array);
        
        // From individual values
        Stream<String> valueStream = Stream.of("one", "two", "three");
        Stream<Integer> numberStream = Stream.of(100, 200, 300);
    }
}

Intermediate Operasions

Operations that return another stream for continued processing:

Method Purpose
Stream<T> filter(Predicate<T>) Filters elements based on condition
Stream<T> limit(long) Takes first n elements
Stream<T> skip(long) Skips first n elements
static <T> Stream<T> concat(Stream<T>, Stream<T>) Combines two streams
Stream<T> distinct() Removes duplicates

Examples:

public class StreamIntermediate {
    public static void main(String[] args) {
        ArrayList<String> items = new ArrayList<String>();
        items.add("Apple");
        items.add("Banana");
        items.add("Avocado");
        items.add("Cherry");
        items.add("Apricot");
        items.add("Blueberry");

        // Filter example
        items.stream().filter(item -> item.startsWith("A")).forEach(System.out::println);
        
        // Limit and skip example
        items.stream().skip(2).limit(2).forEach(System.out::println);
        
        // Concatenate streams
        Stream<String> stream1 = items.stream().limit(3);
        Stream<String> stream2 = items.stream().skip(3);
        Stream.concat(stream1, stream2).distinct().forEach(System.out::println);
    }
}

Terminal Operations

Operations that produce a result and end the stream pipeline:

Method Purpose
void forEach(Consumer<T>) Performs action on each element
long count() Counts elements in stream

Example:

public class StreamTerminal {
    public static void main(String[] args) {
        ArrayList<String> colors = new ArrayList<String>();
        colors.add("Red");
        colors.add("Green");
        colors.add("Blue");
        colors.add("Yellow");
        colors.add("Purple");

        // Count elements starting with 'B'
        long blueCount = colors.stream().filter(color -> color.startsWith("B")).count();
        System.out.println("Count: " + blueCount);
    }
}

Collection Operations

Collecting stream results back into collections:

Method Purpose
R collect(Collector<T>) Gathers results into specified collection

Collector utilities:

Method Purpose
public static <T> Collector<T> toList() Collects to List
public static <T> Collector<T> toSet() Collects to Set
public static Collector<T> toMap(Function<K>, Function<V>) Collects to Map

Example implementation:

public class StreamCollection {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("hello", "world", "java", "programming");
        
        // Collect filtered elements to List
        List<String> shortWords = words.stream()
                                     .filter(word -> word.length() < 6)
                                     .collect(Collectors.toList());
        
        // Process string data and collect to Map
        String[] data = {"John,25", "Jane,30", "Bob,35"};
        Map<String, Integer> personMap = Stream.of(data)
            .filter(entry -> Integer.parseInt(entry.split(",")[1]) > 25)
            .collect(Collectors.toMap(
                entry -> entry.split(",")[0],
                entry -> Integer.parseInt(entry.split(",")[1])
            ));
        
        personMap.forEach((name, age) -> System.out.println(name + ": " + age));
    }
}

Comprehensive Exercise

Combining multiple stream operations:

public class Actor {
    private String name;

    public Actor(String name) {
        this.name = name;
    }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

public class StreamExercise {
    public static void main(String[] args) {
        ArrayList<String> maleActors = new ArrayList<String>();
        maleActors.add("Tom Hanks");
        maleActors.add("Leonardo");
        maleActors.add("Brad Pitt");
        maleActors.add("Johnny Depp");
        maleActors.add("Robert Downey");
        maleActors.add("Chris Evans");

        ArrayList<String> femaleActors = new ArrayList<String>();
        femaleActors.add("Scarlett Johansson");
        femaleActors.add("Jennifer Lawrence");
        femaleActors.add("Emma Watson");
        femaleActors.add("Natalie Portman");
        femaleActors.add("Reese Witherspoon");
        femaleActors.add("Sandra Bullock");

        Stream.concat(
            maleActors.stream().filter(name -> name.length() == 10).limit(2),
            femaleActors.stream().filter(name -> name.startsWith("J")).skip(1)
        ).map(Actor::new)
         .forEach(actor -> System.out.println(actor.getName()));
    }
}

Tags: java Collections Map hashmap TreeMap

Posted on Sat, 20 Jun 2026 16:51:24 +0000 by Michael