Thursday, September 11, 2025

Java collections

As developers advance past the basics of Java, they must understand how to effectively use Java collections in their daily work. This course offers you a deep dive into the framework and hands-on experience working with it. The Java Collections framework consists of interfaces, implementations, and utilities that together provide standard support for common data structures in the language.

 "Interfaces" :

  • Understanding Interfaces: Interfaces in Java are abstract types that cannot be instantiated and are used to define abstract methods that must be implemented by classes.
  • Implementation: When a class implements an interface, it must provide concrete implementations for all of the interface's abstract methods.
  • Inheritance: Interfaces can extend other interfaces, inheriting their abstract methods, which must then be implemented by any class implementing the extended interface.

These concepts are crucial for working with Java collections, as the framework heavily relies on interfaces to define the structure and behavior of different collection types.



 "Collection types"

  • The Java collections framework is built on several core interfaces like List, Set, and Map, which form a hierarchy through inheritance.
  • The Collection interface is the common parent for interfaces like List, Queue, and Set, sharing some methods while each maintaining unique behaviors.
  • Common implementations include ArrayList and HashMap, with naming conventions indicating the implementation style and interface (e.g., ArrayList, HashMap).
  • Understanding the Collection interface helps in quickly learning a large part of the framework since many collection types inherit from it.

















 "Collection methods" 

  • Adding Elements: Use the add method to insert a single element into a collection, and addAll to insert multiple elements. Both methods return a boolean indicating if the collection changed.
  • Removing Elements: Use the remove method to delete a single element, and removeAll to delete multiple elements. The clear method removes all elements, while retainAll keeps only the elements found in another collection.
  • Inspecting Collections: Use the contains method to check if a collection includes a specific element, and containsAll to check for multiple elements. The isEmpty method checks if the collection is empty, and the size method returns the number of elements in the collection.



 "Creating a collection":

  • Creating Collections: Collections can be declared and instantiated like any other object. Use the appropriate interface (e.g., Set, List) to ensure compatibility with the implementation (e.g., HashSet, ArrayList).
  • Generic Types: Specify the generic type for collections to ensure type safety. Use the diamond operator to infer the generic type from the variable type.
  • Best Practices: Use the most abstract type possible for variable types (e.g., List instead of ArrayList) to allow flexibility in switching implementations without changing dependent code.

 "Generic typing" :


  List newListWithoutGenerics          = new ArrayList();
  List<String> newListWithGenerics = new ArrayList<>();


  • Generics and Collections: Generics allow collections to store specific types, improving type safety and eliminating the need for casting.
  • Type Safety: Using generics shifts type-related issues to compile time, preventing runtime errors like ClassCastException.
  • Code Quality: Generics make code cleaner and more maintainable by providing type information, which helps the compiler catch errors early.


 "Object comparison":

  • Identity-Based Comparison: This checks if two objects reference the same memory location using Java's equality operator (==).
  • Value-Based Comparison: This checks if two objects are logically equivalent based on their field values, typically by overriding the equals method.  ( By default, the Object class's equals() method performs a reference comparison, similar to ==However, for meaningful content comparison, you should override the equals() method in your custom classes to define how their instances should be considered equal based on their attributes.)
  • Usage in Collections: Collections rely on value-based comparison for operations like removing elements or checking for their presence.




"Accessing collection elements with iterators":

  • Creating and Using Iterators: You can create an iterator from a collection using the iterator method, which allows you to traverse through the collection's elements.
  • Iterator Instances: Each iterator instance tracks its own progress through the collection, meaning multiple iterators can traverse the same collection independently.
  • Using Loops with Iterators: Iterators are commonly used with loops, such as while loops, to traverse collections. The hasNext method checks if there are more elements to iterate, and the next method retrieves the next element.
  • Enhanced For-Each Loop: The for-each loop provides a concise way to iterate through collections, leveraging the iterable interface and generic typing for type safety and simplicity.












"Modifying collections while iterating":

  • Concurrent Modification Exception: This exception occurs when a collection is modified while it is being iterated, which can lead to unexpected results.
  • Avoiding the Exception: One way to avoid this is by collecting elements to be removed in a separate collection and then removing them outside the iteration loop.
  • Using Iterators: Another method is to use an iterator's remove method within a while loop to safely remove elements during iteration without triggering the exception.

These techniques help ensure safe and effective modification of collections during iteration.










ConcurrentModificationException is a Java runtime exception thrown when a collection (like a list or map) is modified structurally (e.g., by adding or removing elements) while it's being iterated over, unless the modification is done through the iterator's own remove() method. This can occur in both single-threaded and multi-threaded environments and indicates that the collection's state has changed unexpectedly during iteration, often leading to undefined results.  
What Causes It?



  • Single-threaded:
    You directly modify a collection (e.g., list.add()list.remove()) while iterating through it, even within an enhanced for loop (for-each loop), which uses an iterator internally. 
  • Multi-threaded:
    One thread iterates over a collection while another thread adds or removes elements from the same collection. 
How to Avoid It
  1. 1. Use the Iterator's remove() method:
    If you need to remove elements during iteration, use the iterator.remove() method to remove the element that was last returned by next(). 
  1. 2. Use a ListIterator:
    For modifications on List implementations, ListIterator provides add() and remove() methods that are designed to be used safely during iteration. 
  2. 3. Iterate over a copy:
    Create a copy of the collection and iterate over the copy, modifying the original collection as needed. 
  3. 4. Use a dedicated removal method:
    Iterate through the collection, identify elements to remove, and then use a separate loop or stream to remove them from the original collection. 
  4. 5. Use concurrent collections:
    For multi-threaded scenarios, use concurrent collection implementations like CopyOnWriteArrayList or ConcurrentHashMap which are designed to allow modifications during iteration without throwing this exception



"Accessing collection elements with streams":

  • Streams API: Introduced in Java 8, it provides a functional style for processing sequences of elements.
  • Intermediate Operations: Operations like filter are used to perform tests on elements as they flow through the stream, determining if they continue for further processing.
  • Terminal Operations: Operations like forEach are used to end the pipeline, either returning a result or modifying the elements.


"Lambda expressions":

  • Lambda expressions simplify code by removing unnecessary elements like method names and return types, resulting in more concise and readable code.
  • Conversion process: You can convert functional interfaces to lambda expressions by removing everything to the left of the parameter, adding an arrow, and simplifying the method body.
  • Method references: Lambdas can be further simplified using method references, which allow you to pass methods into functions directly.


 "Stream operations" :

  • Stream Operations: Streams are crucial when working with collections in Java. They allow for efficient data processing.
  • Collect Operation: The collect operation is a terminal operation used to gather elements into a new collection.
  • Map Operation: The map operation transforms elements within the stream, allowing for modifications like converting objects to their names.
  • Sum Operation: The mapToDouble operation can be used to convert elements to doubles, and the sum operation can then total these values.





"Set":

  • No Duplicate Elements: Sets do not store duplicate elements. This is managed internally by comparing new elements with existing ones.
  • Additional Methods: Sets add two methods to the collection interface: of() and copyOf(), which help create immutable sets.
  • Implementations: Common implementations include HashSet (no order), LinkedHashSet (insertion order), and TreeSet (sorted order). TreeSet requires elements to implement the Comparable interface or use a comparator.


"HashSet" :

  • Unique CharacteristicsHashSet does not allow duplicate elements, and it does not maintain the order of elements.
  • Equality Check: The equals method is used to check for logical equivalence between elements to prevent duplicates.
  • Unmodifiable Sets: The of and copyOf methods create unmodifiable sets, which cannot be changed after creation.
  • Implementation Differences: Switching from HashSet to LinkedHashSet maintains the insertion order of elements.





"TreeSet" :

  • Binary Search TreeTreeSet uses a binary search tree to organize elements, which requires a way to compare elements.
  • Comparator and Comparable: It uses a comparator or elements that implement the Comparable interface for relational comparison, instead of the equals method.
  • Natural Sort Order: Elements in a TreeSet are sorted in natural order (e.g., ascending order for integers).
  • NavigableSet Methods: Methods like descendingSetheadSettailSet, and subSet provide various ways to view and manipulate subsets of the TreeSet.









interface:

  • List Interface: Allows storing an ordered group of elements with methods for working with elements by their position using a zero-based index.
  • ArrayList vs. LinkedList:
    • ArrayList: Backed by an array, it resizes automatically when capacity is exceeded. Fast for reading elements but slower for adding/removing elements in the middle.
    • LinkedList: Uses a doubly linked list, making it faster for adding/removing elements but slower for retrieving elements due to the need to traverse references.

  • Choosing Implementation: Start with an ArrayList and use the List interface as your variable type. Switch to LinkedList if performance issues arise.













 "List implementations" :

  • Adding Elements: The add method appends elements to the end of the list, while an overloaded version allows adding elements at a specific position.
  • Retrieving Elements: The get method retrieves an element at a specific index, and the indexOf method returns the position of the first occurrence of an element.
  • Removing Elements: The remove method can remove an element at a specific position and returns the removed element.
  • Replacing Elements: The set method replaces an element at a specific position and returns the original element.













"ArrayList" :

  • Creating an ArrayList: You can create an ArrayList with a specified initial capacity to avoid frequent resizing when adding many elements.
  • Adding Elements: Use the add method to insert elements at specific positions, ensuring loyalty program members are prioritized in the check-in list.
  • Managing Capacity: Initializing the ArrayList with a larger capacity (e.g., 100) can improve performance by reducing the need for resizing.






 "Queue interface" :

  • Queue Concept: A queue in Java is a collection used to hold elements for processing in a first-in, first-out (FIFO) order, similar to standing in a line.
  • Queue Methods: Key methods include add/offer to add items, and poll/remove to retrieve and remove items from the head of the queue. element/peek are used to examine the head without removing it.
  • Handling Full/Empty Queuesadd throws an exception if the queue is full, while offer returns false. remove throws an exception if the queue is empty, while poll returns null.
  • Queue Implementations: Common implementations include ArrayDequeLinkedList, and PriorityQueue, with ArrayDeque being the most commonly used.





.remove ()  will  throw  an  exception  while  poll  will  throw  null









Added  order is  not  in PriorityQueue







 Deque interface

  • Double-Ended Queue (Deque): A deque allows elements to be added or removed from both ends (front and back) of its linear structure, making it versatile.
  • Methods: Methods like addFirstofferFirstaddLast, and offerLast are used to add elements, while removeFirstpollFirstremoveLast, and pollLast are used to remove elements.
  • Stack Functionality: A deque can also function as a stack using methods like pushpop, and peek, enabling last-in, first-out (LIFO) operations.



Using an ArrayDeque as a Stack

  • ArrayDeque as a Stack: The ArrayDeque is recommended for stack implementations in Java, using a last-in, first-out (LIFO) approach.
  • Stack Operations: Key operations include push to add elements, pop to remove elements, and peek to view the top element without removing it.
  • Example Scenario: The video uses an example of a voicemail system to demonstrate how messages are added and removed from the stack, highlighting the LIFO order.







Map interface :

  • Purpose of a Map: A map stores entries with an association between a key and a value, similar to a dictionary where words represent keys and definitions represent values.
  • Unique Characteristics: Keys in a map are unique, making it fast and efficient for finding values using the key. The map interface does not extend the collection interface.
  • Core Methods:
    • put: Creates or updates an entry in the map.
    • get: Retrieves the value associated with a key.
    • remove: Removes an entry based on the key.









"Collection views":

  • Collection Views: These are used to obtain parts of a map, like its entries, keys, or values as a collection.
  • Dynamic Updates: Collection views are backed by the map, meaning any changes to the map will be reflected in the collection view.
  • Common Usage: Methods like keySetentrySet, and values provide collection views for a map's keys, entries, and values, respectively.










 "Map implementations":

  • TreeMap: Stores entries in sorted order within a tree structure, implementing the NavigableMap interface.
  • HashMap: Does not guarantee order but uses hashing to store objects. The LinkedHashMap subclass maintains insertion order or order of last access.
  • Hashing: Converts an object into a numeric value (hash value) using the hashCode method. Custom objects need their own hashCode implementation.
  • HashCode Rules:
    • Must consistently produce the same integer for the same object.
    • Equal objects must have the same hash value.
    • Unequal objects can have the same hash value (collisions).








"How a HashMap works":

  • Storage Mechanism: A HashMap uses an array to store nodes, with each node containing a key, value, and hash value. The default array size is 16.
  • Hashing Process: When a key-value pair is added using the put method, the key's hashCode method generates a hash value. This value is further hashed to determine the array index for storage.
  • Collision Handling: If multiple keys hash to the same index, nodes are linked together in a linked list within that array index.
  • Retrieval Process: When retrieving a value using the get method, the key's hash value determines the array index. If multiple nodes are present, the equals method is used to find the correct key.






 "Map methods":

  • Adding Entries: Use the put method to add key-value pairs to the map.
  • Retrieving Values: Use the get method with the key to retrieve the corresponding value.
  • Removing Entries: Use the remove method to delete a key-value pair from the map.
  • Switching Values: Demonstrated how to switch values between keys using put and remove methods.









"Iterating Maps":

  • EntrySet Method: Use the entrySet method to get a set of entries (key-value pairs) from the map.
  • For-Each Loop: Iterate through the entries using an enhanced for-each loop, which allows you to access each key and value.
  • Streams: Alternatively, you can use streams to iterate through the entries in the map.








"Ordering elements" 

  • Natural Order: Elements like currency, letters, and numbers have a natural order (monetary value, alphabetical, numeric). Java data types such as dates, numbers, and strings follow this natural order.
  • Comparable Interface: This interface is used to specify the natural order of a data type. It includes the compareTo method, which compares the current instance with another object and returns an integer indicating their order.
  • Comparator Interface: This interface allows for custom ordering of








    objects. It compares two objects externally using the compare method and can be used when the natural order is not suitable.






"Sorting collections" video:

  • Natural Order: Implement the Comparable interface to define the natural order for a class. This involves implementing the compareTo method to compare objects.
  • Sorting with Collections Class: Use the Collections.sort method to sort a list based on natural order or with a specified comparator.
  • List's Sort Method: In recent Java versions, use the sort method directly on a list with a comparator for more concise code.







"Comparators" video:

  • Custom Comparators: You can define a custom comparator to sort collections based on specific fields, such as a room's daily rate.
  • Chaining Comparisons: Use the thenComparing method to add secondary and tertiary sort keys, allowing for more complex sorting logic.
  • Reversing Order: The reversed method can be used to reverse the order of sorting, such as sorting rates in descending order.










"Finding elements in a collection" :

  • Binary Search: The Collections.binarySearch method is used to find elements in a sorted list. It returns the index of the element if found, or a negative value indicating the insertion point if not found.
  • Insertion Point: When an element is not found, the binary search method provides a negative result indicating where the element would be inserted in the list.
  • Min and Max Methods: The Collections.min and Collections.max methods help find the minimum and maximum values in a collection based on a comparator.

No comments:

Post a Comment

The AI Driven Software Developer, Optimize Innovate Transform

  The AI-Driven Software Developer: Optimize, Innovate, Transform": AI Transformation in Software Development : Understand how AI is re...