Friday, June 28, 2024

Finding a Key’s Index in Java LinkedHashMap

Finding a Key’s Index in Java LinkedHashMap

The Java LinkedHashMap class combines a hash table and linked list to maintain predictable iteration order, unlike HashMap. However, LinkedHashMap does not provide a direct method to get the position (index) of a key-value pair. This article explores methods to retrieve the index of a key-value pair in a LinkedHashMap.

1. Using Iteration


One straightforward method is to iterate through the entrySet of the LinkedHashMap, comparing each key with the target key and returning the index when a match is found.

LinkedHashMapIterationApproach.java

public class LinkedHashMapIteration {
 
    public static void main(String[] args) {
         
        // Create a LinkedHashMap with Integer keys and String values
        LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put(101, "Alice");
        linkedHashMap.put(202, "Bob");
        linkedHashMap.put(303, "Charlie");
        linkedHashMap.put(404, "David");
 
        // Key to find position for
        Integer key = 303;
         
        // Find the position of the key using iteration approach
        int position = getPositionOfKey(linkedHashMap, key);
         
        // Output the result
        System.out.println("LinkedHashMap: " + linkedHashMap);
        System.out.println("Finding position of key: " + key);
        System.out.println("Position: " + position);
        //System.out.println("The position of the key \"" + key + "\" is: " + position);
    }
 
    public static <K, V> int getPositionOfKey(LinkedHashMap<K, V> map, K key) {
        int index = 0;
        for (Map.Entry<K, V> entry : map.entrySet()) {
            if (entry.getKey().equals(key)) {
                return index;
            }
            index++;
        }
        return -1; // Key not found
    }
}

Output:

LinkedHashMap: {101=Alice, 202=Bob, 303=Charlie, 404=David}
Finding position of key: 303
Position: 2

In this example, we create a LinkedHashMap<Integer, String> and populate it with key-value pairs. Next, we specify key = 303 to demonstrate finding the position of the key 303. The getPositionOfKey method iterates through the entrySet() of the LinkedHashMap and compares each key with key. Upon finding a match (key = 303), it returns the position (index) 2 since indexing starts from 0.

2. Using Key Set Conversion


Using this approach, we convert the key set to a list and then find the index of the key.

LinkedHashMapKeySetConversion.java

public class LinkedHashMapKeySetConversion {
 
    public static void main(String[] args) {
        LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("January", 1);
        linkedHashMap.put("February", 2);
        linkedHashMap.put("March", 3);
        linkedHashMap.put("April", 4);
 
        String key = "January";
        int position = getPositionOfKey(linkedHashMap, key);
        System.out.println("The position of the key \"" + key + "\" is: " + position);
    }
 
    public static <K, V> int getPositionOfKey(LinkedHashMap<K, V> map, K key) {
        List<K> keyList = new ArrayList<>(map.keySet());
        return keyList.indexOf(key);
    }
}

Here, the keySet of the LinkedHashMap is converted to an ArrayList. The indexOf method of ArrayList is then used to find the index of the key.

Output:

The position of the key "January" is: 0

3. Using Stream API (Java 8+)


With Java 8 and higher, the Stream API offers a straightforward way to solve this issue using functional programming principles.

LinkedHashMapStreamApiApproach.java

public class LinkedHashMapStreamApiApproach {
 
    public static int findKeyPosition(LinkedHashMap<String, Integer> map, String key) {
        int position = 0;
        Optional<String> foundKey = map.entrySet().stream()
                .filter(entry -> entry.getKey().equals(key))
                .map(Map.Entry::getKey)
                .findFirst();
 
        if (foundKey.isPresent()) {
            // Key found, iterate again to count position
            for (Map.Entry<String, Integer> entry : map.entrySet()) {
                if (entry.getKey().equals(key)) {
                    return position;
                }
                position++;
            }
        }
        return -1; // Key not found
    }
 
    public static void main(String[] args) {
         
        LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
        map.put("apple", 10);
        map.put("banana", 20);
        map.put("cherry", 30);
 
        int position = findKeyPosition(map, "banana");
 
        if (position != -1) {
            System.out.println("Key 'banana' found at position: " + position);
        } else {
            System.out.println("Key 'banana' not found");
        }
    }
}

In this approach, we use entrySet().stream() to create a Stream of key-value pairs from the map and use filter to filter the stream to keep only entries where the key matches the target key. We use map(Map.Entry::getKey) is to extract just the keys from the filtered entries and use findFirst to retrieve the first matching key wrapped in an Optional. If the Optional contains a value (meaning the key was found), we iterate through the entire entrySet again.

Inside the loop, we compare the current entry’s key with the target key. If there’s a match, we return the current position as it represents the target key’s position based on insertion order.

4. Using keySet() and List.copyOf() (Java 10+)


This approach leverages the List.copyOf() method to create a list from the key set and then find the index of the key.

LinkedHashMapListCopyOf.java

public class LinkedHashMapListCopyOf {
 
    public static void main(String[] args) {
        LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("apple", 10);
        linkedHashMap.put("banana", 20);
        linkedHashMap.put("cherry", 30);
        linkedHashMap.put("orange", 40);
 
        String key = "orange";
        int position = getPositionOfKey(linkedHashMap, key);
        System.out.println("The position of the key \"" + key + "\" is: " + position);
    }
 
    public static <K, V> int getPositionOfKey(LinkedHashMap<K, V> map, K key) {
        List<K> keyList = List.copyOf(map.keySet());
        return keyList.indexOf(key);
    }
}

In this approach, List.copyOf(map.keySet()) method creates an unmodifiable copy of the key set from the LinkedHashMap and we use keyList.indexOf(key method to return the index of the specified key in the list. The output is:

The Posotion of the key "orange" is: 3

Example Output Using List.copyOf for Java LinkedHashMap Key Position

5. Conclusion

In this article, we explored several methods to find the position of a key-value pair in a Java LinkedHashMap. We began with a straightforward iteration approach, followed by converting the key set to a list for direct index retrieval. We also demonstrated the power of Java 8’s Stream API for a more functional programming solution. Additionally, we leveraged Java 10’s List.copyOf() method to create an unmodifiable list from the key set, facilitating easy position finding. Each method showcases different aspects of Java’s rich API and highlights various ways to achieve the same goal.

Source: javacodegeeks.com

Wednesday, June 26, 2024

The Future of Programming: Exploring Oracle Java SE Innovations

The Future of Programming: Exploring Oracle Java SE Innovations

Introduction to Oracle Java SE


In the ever-evolving landscape of programming languages, Oracle Java SE (Standard Edition) stands out as a beacon of innovation and reliability. With each new version, Oracle introduces enhancements that redefine how developers interact with the language and build applications. As we delve into the future of programming, it's crucial to explore the latest innovations in Oracle Java SE and their potential impact on the software development industry.

Key Innovations in Oracle Java SE


Enhanced Performance and Scalability

One of the most significant advancements in Oracle Java SE is the continuous improvement in performance and scalability. Java SE has always been known for its robustness, but recent updates have taken it a step further. The introduction of features like Z Garbage Collector (ZGC) and Shenandoah GC has drastically reduced pause times, making Java applications more responsive and scalable. These garbage collectors are designed to handle large heaps efficiently, which is essential for modern, high-performance applications.

Project Loom: Lightweight Concurrency

Concurrency has been a challenging aspect of programming, but Project Loom aims to simplify it. This initiative introduces lightweight, user-mode threads known as fibers. Fibers are more efficient than traditional threads, allowing developers to write concurrent applications with ease. By reducing the complexity of concurrency, Project Loom is set to revolutionize how we develop scalable and high-performance applications.

Project Panama: Foreign Function Interface

Project Panama is another groundbreaking innovation in Oracle Java SE. It aims to bridge the gap between Java and native code, enabling seamless interaction with libraries written in languages like C and C++. This Foreign Function Interface (FFI) simplifies the process of calling native functions from Java, enhancing performance and expanding the capabilities of Java applications. With Project Panama, developers can leverage existing native libraries without compromising on the benefits of Java.

Project Amber: Language Productivity

Project Amber focuses on enhancing developer productivity by introducing new language features. These features include pattern matching, records, and sealed classes. Pattern matching simplifies the handling of complex data structures, while records provide a concise way to declare data carrier classes. Sealed classes, on the other hand, restrict the inheritance hierarchy, ensuring better control and maintainability of code. Together, these features make Java a more expressive and powerful language.

Security Enhancements


Improved Cryptographic Algorithms

In an age where cybersecurity is paramount, Oracle Java SE continues to prioritize security enhancements. The latest versions include support for modern cryptographic algorithms, ensuring that Java applications remain secure against emerging threats. By incorporating algorithms like ChaCha20 and Poly1305, Oracle Java SE provides developers with robust tools to protect sensitive data.

Stronger Authentication Mechanisms

Authentication is a critical component of application security, and Oracle Java SE has introduced stronger mechanisms to safeguard user identities. Features like OAuth 2.0 and OpenID Connect are now seamlessly integrated, allowing developers to implement secure and scalable authentication systems. These enhancements not only protect user data but also simplify the process of implementing authentication in Java applications.

Tools and Frameworks


Java Development Kit (JDK) Improvements

The Java Development Kit (JDK) is the backbone of Java programming, and Oracle continues to enhance it with each release. The latest JDK versions come with improved tools for debugging, profiling, and monitoring Java applications. Tools like JDK Mission Control and Flight Recorder provide developers with detailed insights into the performance and behavior of their applications, enabling them to optimize code and resolve issues efficiently.

Integration with Modern Development Frameworks

Oracle Java SE’s compatibility with modern development frameworks has significantly improved. Frameworks like Spring Boot, Micronaut, and Quarkus are fully supported, allowing developers to build microservices and cloud-native applications with ease. These frameworks leverage the latest features of Java SE, enabling rapid development and deployment of scalable applications.

The Future of Oracle Java SE


Continued Evolution and Community Support

The future of Oracle Java SE looks promising, with a strong commitment to evolution and innovation. Oracle’s active engagement with the developer community ensures that Java remains relevant and up-to-date. Community-driven projects and open-source contributions play a crucial role in shaping the future of Java, making it a language that evolves with the needs of developers.

Adoption of Cutting-Edge Technologies

Oracle Java SE is poised to adopt cutting-edge technologies like machine learning, artificial intelligence, and blockchain. By integrating these technologies, Java will continue to be a versatile and powerful language for developing next-generation applications. The ability to handle complex computations and data processing tasks makes Java an ideal choice for emerging tech trends.

Conclusion

Oracle Java SE continues to be at the forefront of programming innovation. With enhancements in performance, scalability, concurrency, and security, Java remains a robust and reliable choice for developers. The introduction of projects like Loom, Panama, and Amber showcases Oracle’s commitment to making Java a more powerful and expressive language. As we look to the future, the continuous evolution of Oracle Java SE ensures that it will remain a cornerstone of the programming world.

Monday, June 24, 2024

Easily install Oracle Java on Oracle Linux in OCI: It’s a perfect match!

Throughout its decades-long history, Oracle Java has constantly evolved to keep up with the growing demands of business-critical performance and scalability. Being a simple yet robust and secure programming language, Java enables millions of developers to craft portable applications for embedded systems, mobiles, and the cloud. Oracle Java is the #1 programming language and development platform, helping enterprises worldwide rapidly innovate and improve the performance and stability of their application services.

Oracle’s proven Java Development Kit (JDK), Oracle Java SE, allows developers to write more stable and secure applications with shortened development timeframes and reduced costs. With its modern and architecture-neutral approach, Oracle Java fits into all types of technology stacks, making it one of the strongest contenders to be used for DevOps and cloud development. For microservices and other containerized workloads, Oracle GraalVM provides more optimization options for cloud native applications, including ahead-of-time compilation to reduce memory and CPU usage.

Oracle Java on OCI


For Oracle Cloud Infrastructure (OCI) customers, an OCI subscription includes licenses and full support for all Oracle Java SE and Oracle GraalVM versions at no extra cost. OCI customers can even monitor and manage the use of Java in their enterprise with the Java Management Service (JMS), a reporting and management infrastructure integrated with OCI platform services. With JMS, Java users can monitor Java deployments on OCI instances and instances running on-premises in data centers, helping gain insights into Java application behavior, performance, and compliance.

Oracle Java is supported on Oracle’s long-standing and highly performant operating system, Oracle Linux. Oracle Linux is compatible with 64-bit Intel/AMD (x86-64) and 64-bit Arm (aarch64) processors, enabling applications to be quickly created, deployed, and used across a range of platforms. Moreover, because Oracle Linux is supported across on-premises and multicloud infrastructures and is optimized out of the box for Oracle software, it’s the ideal operating system on which to run Oracle Java.

Compute instances on OCI have access to a regional Oracle Linux yum server mirror for high-speed access to RPMs, which makes it easy to install Oracle Java and Oracle GraalVM. Take advantage of our tutorial to learn how you can use RPMs available from the OCI yum service to easily install Oracle Java on an Oracle Linux system running on OCI.

While installation on Oracle Linux is simple and straightforward, to make it even easier for developers to get started, OCI offers the Oracle Linux Cloud Developer image, which is available as a platform image.

Begin developing with the Oracle Linux Cloud Developer image in minutes


The Oracle Linux Cloud Developer image is a ready-to-run image that preinstalls and launches a comprehensive cloud development environment that includes various popular development languages such as Java with Oracle JDK or Oracle GraalVM, Python, Ruby, and more. Tooling for working with OCI, such as software developer kits (SDKs), CLIs, and Oracle Database connectors, are also included.

With the Oracle Linux Cloud Developer image, you don’t have to go through the typical installation process for development tools. Simply select the image when provisioning your OCI instance.

Easily install Oracle Java on Oracle Linux in OCI: It’s a perfect match!

When your instance is provisioned, jump straight into building applications.

Source: oracle.com

Friday, June 21, 2024

Key Features of Oracle Cloud Infrastructure

Key Features of Oracle Cloud Infrastructure

Introduction to Oracle Cloud Infrastructure


Oracle Cloud Infrastructure (OCI) is a suite of cloud services offered by Oracle Corporation, designed to help businesses run complex workloads in a secure, highly available, and scalable environment. With OCI, enterprises can deploy and manage applications with enhanced performance and lower costs. Oracle's cloud platform provides comprehensive solutions for computing, storage, databases, networking, and more, making it a versatile choice for businesses seeking robust cloud capabilities.

Key Features of Oracle Cloud Infrastructure


High-Performance Computing

OCI is renowned for its high-performance computing (HPC) capabilities. It offers bare metal instances that deliver the raw processing power needed for intensive workloads, such as simulations, data analytics, and scientific research. These instances provide direct hardware access, eliminating the overhead associated with virtualization and maximizing performance.


Scalability and Flexibility

One of the standout features of OCI is its scalability. Businesses can easily scale their infrastructure up or down based on their needs. OCI's flexible architecture supports a wide range of services, including virtual machines, bare metal servers, and Kubernetes clusters. This flexibility ensures that companies can tailor their cloud environment to meet specific requirements, optimizing resource utilization and cost-efficiency.

Comprehensive Security

Security is a paramount concern for any cloud service, and OCI excels in this area. Oracle implements a multi-layered security approach, encompassing data encryption, identity and access management (IAM), network security, and compliance with global standards. With OCI, organizations can safeguard their data against threats and unauthorized access, ensuring regulatory compliance and maintaining customer trust.

Global Availability

Oracle Cloud Infrastructure is available in multiple regions across the globe, providing global reach and redundancy. This geographical distribution allows businesses to deploy applications closer to their user base, reducing latency and enhancing user experience. Furthermore, OCI's robust network of data centers ensures high availability and disaster recovery capabilities, crucial for mission-critical applications.

OCI Compute Services


Virtual Machines

OCI offers a variety of virtual machine (VM) shapes to cater to different workloads. These VMs are preconfigured with various combinations of CPU, memory, and storage, enabling businesses to select the optimal configuration for their applications. Oracle's VM instances support a wide range of operating systems, including Oracle Linux, Windows, and Ubuntu, providing versatility and compatibility for diverse IT environments.

Bare Metal Instances

For applications requiring maximum performance and isolation, OCI provides bare metal instances. These instances deliver dedicated physical servers without the overhead of a hypervisor, ensuring uncompromised performance. Bare metal instances are ideal for high-performance computing tasks, large-scale databases, and applications demanding low latency and high I/O throughput.

Oracle Functions

Oracle Functions is a serverless compute service that enables developers to run code in response to events without managing infrastructure. This service supports various programming languages and integrates seamlessly with other OCI services, enhancing the agility and efficiency of application development and deployment.

OCI Storage Solutions


Block Storage

OCI's block storage service offers high-performance, low-latency storage for applications requiring fast, persistent storage. Block storage volumes can be attached to both VM and bare metal instances, providing flexibility and scalability. With features like data encryption, backup, and snapshot capabilities, OCI block storage ensures data integrity and availability.

Object Storage

Object storage in OCI is designed for storing unstructured data, such as media files, backups, and logs. This service provides virtually unlimited scalability, high durability, and accessibility from anywhere via the internet. OCI object storage supports lifecycle policies, enabling automatic data archiving and deletion to optimize storage costs.

File Storage

OCI's file storage service delivers a scalable, fully managed file system for applications requiring shared file access. It supports standard file protocols like NFS and SMB, making it compatible with various operating systems and applications. With high availability and performance, OCI file storage is suitable for enterprise applications, home directories, and development environments.

Networking in OCI


Virtual Cloud Network (VCN)

OCI's Virtual Cloud Network (VCN) provides a secure and customizable network environment for deploying cloud resources. VCNs can be segmented using subnets, security lists, and route tables, allowing businesses to isolate and control traffic flow. This feature ensures secure communication between resources and supports hybrid cloud architectures by connecting on-premises networks to the cloud.

Load Balancing

To ensure high availability and optimal performance of applications, OCI offers load balancing services. These services distribute incoming traffic across multiple instances, preventing overload and ensuring continuous operation. OCI load balancers support both public and private IPs, SSL termination, and automatic scaling, enhancing the reliability and efficiency of web applications.

FastConnect

OCI's FastConnect provides a dedicated, private connection between on-premises data centers and Oracle Cloud. This service offers higher bandwidth, lower latency, and more consistent performance compared to standard internet connections. FastConnect is ideal for businesses with stringent security and performance requirements, ensuring seamless integration between cloud and on-premises environments.

Oracle Database Services


Autonomous Database

OCI's Autonomous Database is a revolutionary service that leverages machine learning to automate database management tasks, such as tuning, patching, and backups. This service provides autonomous capabilities for both transaction processing and data warehousing, enhancing performance, security, and cost-efficiency. Autonomous Database supports multiple deployment options, including dedicated and shared infrastructure.

Exadata Cloud Service

Exadata Cloud Service offers the power of Oracle Exadata in a cloud environment, delivering unmatched performance for Oracle databases. This service provides extreme scalability, high availability, and advanced security features, making it ideal for mission-critical database workloads. Businesses can benefit from the optimized performance of Exadata hardware combined with the flexibility and scalability of the cloud.

Database Cloud Service

OCI's Database Cloud Service provides fully managed Oracle databases, offering a range of editions and configurations to suit different needs. This service supports Oracle Real Application Clusters (RAC) for high availability and scalability, ensuring continuous operation and disaster recovery. With automated backups, patching, and monitoring, Database Cloud Service simplifies database management and reduces operational overhead.

Conclusion

Oracle Cloud Infrastructure offers a comprehensive suite of services designed to meet the diverse needs of modern businesses. From high-performance computing and scalable storage solutions to advanced networking and database services, OCI provides the tools necessary to build and manage robust cloud environments. With a focus on security, performance, and flexibility, OCI empowers organizations to innovate and achieve their strategic objectives in the cloud era.

Wednesday, June 19, 2024

What are the Benefits of Obtaining an Oracle Java Certification for My Career?

What are the Benefits of Obtaining an Oracle Java Certification for My Career?

In today’s competitive job market, professionals constantly seek ways to stand out and enhance their skills. One significant way to achieve this is through certifications, and Oracle Java Certification is one of the most sought-after credentials in the IT industry. This article delves into the myriad benefits of obtaining an Oracle Java Certification and how it can propel your career to new heights.

1. Enhanced Job Opportunities


Obtaining an Oracle Java Certification opens doors to a plethora of job opportunities. Companies across the globe recognize and value the certification as it demonstrates a strong foundation in Java programming and related technologies. Certified professionals are often preferred over non-certified candidates for roles such as Java Developer, Software Engineer, and Application Developer. The certification acts as a testament to your commitment to mastering Java, thereby enhancing your employability.

2. Increased Earning Potential


One of the most compelling reasons to pursue an Oracle Java Certification is the potential for higher earnings. Certified Java professionals typically command higher salaries compared to their non-certified counterparts. According to industry surveys, individuals with an Oracle Java Certification can expect a significant increase in their annual salary. This financial benefit makes the certification a worthwhile investment in your career.

3. Comprehensive Knowledge and Skill Development


The process of preparing for Oracle Java Certification exams is rigorous and thorough. It ensures that candidates gain an in-depth understanding of Java programming and its various components. This comprehensive knowledge includes learning about Java SE (Standard Edition), Java EE (Enterprise Edition), and various Java frameworks and libraries. The certification process also enhances your problem-solving and coding skills, making you a more proficient developer.

4. Recognition and Credibility


Being Oracle certified gives you a badge of credibility in the IT industry. Oracle is a globally recognized and respected organization, and its certifications are synonymous with high standards of expertise. When you obtain an Oracle Java Certification, you join an elite group of professionals who are acknowledged for their technical proficiency and dedication to continuous learning. This recognition can be a significant boost to your professional reputation.

5. Career Advancement and Promotion


Certification often acts as a catalyst for career advancement. Many organizations have policies that favor the promotion of certified employees. An Oracle Java Certification can be a decisive factor when it comes to climbing the corporate ladder. It showcases your commitment to professional growth and your ability to handle advanced Java-related projects, making you a prime candidate for senior positions and leadership roles.

6. Networking Opportunities


Being part of the Oracle certified community provides excellent networking opportunities. You can connect with other certified professionals, attend exclusive events, and participate in forums and discussions. Networking can lead to valuable insights, job leads, and collaborations that can further your career. The community support can also be beneficial in staying updated with the latest industry trends and best practices.

7. Access to Oracle Resources and Updates


Certified professionals gain access to exclusive Oracle resources, including software updates, technical support, and training materials. This access ensures that you stay at the forefront of technological advancements and can continuously upgrade your skills. Being up-to-date with the latest Java developments can give you a competitive edge in the job market.

8. Improved Job Performance


The skills and knowledge gained through Oracle Java Certification can directly translate to improved job performance. Certified professionals are equipped to handle complex Java projects more efficiently and effectively. This proficiency can lead to higher productivity, better quality of work, and ultimately, career success. Employers value employees who can contribute significantly to the success of their projects and the organization.

9. Confidence Boost


Achieving certification can be a significant confidence booster. The rigorous preparation and successful completion of the exam validate your expertise and abilities. This confidence can positively impact your job performance and career trajectory. It empowers you to take on challenging projects and positions you as a knowledgeable and skilled professional in your field.

10. Enhanced Problem-Solving Skills


Oracle Java Certification exams are designed to test not just theoretical knowledge but also practical problem-solving skills. Preparing for these exams hones your ability to think critically and solve real-world programming issues efficiently. These enhanced problem-solving skills are invaluable in a fast-paced and ever-evolving IT landscape.

11. Employer Benefits


Employers also benefit from having Oracle certified professionals on their team. Certified employees bring a higher level of expertise and efficiency to their roles, which can lead to better project outcomes and overall organizational success. Companies often support their employees in obtaining certifications by providing resources and incentives, recognizing the value it brings to their workforce.

12. Staying Competitive in the Job Market


In the highly competitive IT job market, staying relevant and competitive is crucial. Oracle Java Certification helps you stay ahead of the curve by validating your skills and knowledge. It distinguishes you from other candidates and demonstrates your commitment to staying current with industry standards and practices.

Conclusion

Obtaining an Oracle Java Certification is a strategic career move that offers numerous benefits, from enhanced job opportunities and increased earning potential to improved job performance and professional recognition. The certification process equips you with comprehensive knowledge, advanced skills, and a network of peers and resources that can significantly boost your career. As the IT industry continues to evolve, staying certified and updated with the latest advancements is essential for sustained career growth and success.

Monday, June 17, 2024

Filter a Map by Keys and Values using Java Stream

Filter a Map by Keys and Values using Java Stream

Discover how to filter a Map by keys, values, or both using Java 8’s Stream API with the filter() and collect() methods. These straightforward code snippets demonstrate how to create generic functions for filtering any Map based on its keys or values. Let us delve into understanding how to filter a map by keys and values.

1. Introduction


In Java, a Map is a part of the Java Collections Framework and is used to store key-value pairs. Each key in a map is unique, and each key maps to exactly one value. Maps provide a way to look up values based on their corresponding keys, making data retrieval efficient.

Common implementations of the Map interface include HashMap, TreeMap, and LinkedHashMap. These implementations differ in terms of ordering, performance, and underlying data structures. For instance, HashMap offers average constant-time performance for most operations like get and put, while TreeMap maintains a sorted order of keys.

Maps are versatile and widely used in Java applications to manage and manipulate collections of data where relationships between keys and values are important.

1.1 Filter a Map by Keys


To filter a map by keys, we can use the Stream API to process the entries and collect them back into a map. Here is an example:

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
 
public class FilterMapByKey {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("apple", 10);
        map.put("banana", 20);
        map.put("cherry", 30);
        map.put("date", 40);
 
        // Filter the map to keep only keys starting with 'a'
        Map filteredMap = map.entrySet()
            .stream()
            .filter(entry -> entry.getKey().startsWith("a"))
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
 
        System.out.println(filteredMap);
    }
}

Here’s a code breakdown:

  • map.entrySet().stream() – Creates a stream from the map’s entry set.
  • filter(entry -> entry.getKey().startsWith("a")) – Filters the entries where the key starts with ‘a’.
  • collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) – Collects the filtered entries back into a map.

The code output is:

{apple=10}

1.2 Filter a Map by Values


Filtering a map by values follows a similar approach. Here is an example:

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
 
public class FilterMapByValue {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("apple", 10);
        map.put("banana", 20);
        map.put("cherry", 30);
        map.put("date", 40);
 
        // Filter the map to keep only values greater than 20
        Map filteredMap = map.entrySet()
            .stream()
            .filter(entry -> entry.getValue() > 20)
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
 
        System.out.println(filteredMap);
    }
}

Here’s a code breakdown:

  • map.entrySet().stream() – Creates a stream from the map’s entry set.
  • filter(entry -> entry.getValue() > 20) – Filters the entries where the value is greater than 20.
  • collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) – Collects the filtered entries back into a map.

The code output is:

{date=40, cherry=30}

1.3 Filter a Map by Keys and Values, Both


Sometimes, you may need to filter a map by both keys and values. Here is how you can do that:

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
 
public class FilterMapByKeyAndValue {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("apple", 10);
        map.put("banana", 20);
        map.put("cherry", 30);
        map.put("date", 40);
 
        // Filter the map to keep only keys starting with 'c' and values greater than 20
        Map filteredMap = map.entrySet()
            .stream()
            .filter(entry -> entry.getKey().startsWith("c") && entry.getValue() > 20)
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
 
        System.out.println(filteredMap);
    }
}

Here’s a code breakdown:

  • map.entrySet().stream() – Creates a stream from the map’s entry set.
  • filter(entry -> entry.getKey().startsWith("c") && entry.getValue() > 20) – Filters the entries where the key starts with ‘c’ and the value is greater than 20.
  • collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) – Collects the filtered entries back into a map.

The code output is:

{cherry=30}

Using the Stream API in Java 8 and later versions, filtering maps by keys, values, or both becomes a clean and efficient process. The examples provided demonstrate how to perform these operations effectively.

2. Conclusion


In conclusion, filtering maps by keys, values, or both in Java can be efficiently accomplished using the Stream API introduced in Java 8. By leveraging the power of streams, developers can create concise and readable code to handle these common tasks. Whether you need to filter entries based on specific key patterns, value ranges, or a combination of both, the examples provided demonstrate the flexibility and ease with which these operations can be performed. This approach not only enhances code readability but also ensures that operations on collections are both functional and expressive.

Source: javacodegeeks.com

Friday, June 14, 2024

How to Use Pair With Java PriorityQueue

How to Use Pair With Java PriorityQueue

Java’s PriorityQueue is a data structure that allows us to store and retrieve elements in a specific order. This article explores how to use pairs with PriorityQueue and demonstrates how to use Comparator interface to control the sorting order.

1. What is a PriorityQueue?


A PriorityQueue is a queue data structure where elements are ordered based on their priority rather than their insertion order. This data structure is part of the Java Collections Framework and is typically used when processing elements in a specific order, defined by their natural ordering or a custom comparator.

1.1 Ordering

One of the key characteristics of PriorityQueue is its ordering mechanism. Elements are ordered based on their priority. The default priority is determined by the natural ordering of elements (meaning the elements must implement the Comparable interface), or we can define a custom Comparator to specify the order.

1.2 What is a Pair?

The Pair class in Java was a convenient utility class found in JavaFX, which allowed developers to store a pair of values, essentially providing a simple way to return two related objects from a method. A Pair object holds two values, referred to as key and value, and provide methods to access these values. Here is a simple usage example:

import javafx.util.Pair;
 
Pair<Integer, String> pair = new Pair<>(1, "one");
System.out.println("Key: " + pair.getKey());
System.out.println("Value: " + pair.getValue());

The Pair class (javafx.util.Pair), along with other parts of JavaFX, was decoupled from the JDK in Java 11. The removal of JavaFX from the JDK meant that the Pair class is no longer a built-in part of the standard Java library from Java 11 onwards.

Developers needing Pair functionality now often resort to either custom implementations or third-party libraries such as Apache Commons Lang (org.apache.commons.lang3.tuple.Pair).

2. Using PriorityQueue with a Custom Class


To use PriorityQueue with a Custom class, we will create a class that implements the Comparable interface. Let’s consider an example using a custom class named Book, which includes a title and a publication year.

Book.java

public class Book implements Comparable<Book>{
 
    String title;
    int year;
 
    public Book(String title, int year) {
        this.title = title;
        this.year = year;
    }
 
    public String getTitle() {
        return title;
    }
 
    public int getYear() {
        return year;
    }
     
    @Override
    public int compareTo(Book other) {
        // Compare Books based on their year
        return Integer.compare(this.year, other.year);
    }
 
    @Override
    public String toString() {
        return title + " (" + year + ")";
    }
}

In this example, the Book class implements the Comparable interface.

Next, we create a PriorityQueue of Book objects, add some books to the priority queue and process elements from the PriorityQueue.

PriorityQueueExample1.java

public class PriorityQueueExample1 {
 
    public static void main(String[] args) {
 
        // Create a PriorityQueue and Add Books to the Queue
        PriorityQueue<Book> bookQueue = new PriorityQueue<>();
        bookQueue.add(new Book("To Kill a Mockingbird", 1960));
        bookQueue.add(new Book("1984", 1949));
        bookQueue.add(new Book("The Age of Reason", 1794));
        bookQueue.add(new Book("The Great Gatsby", 1925));
 
        // Process Books
        while (!bookQueue.isEmpty()) {
            Book book = bookQueue.poll();
            System.out.println(book);
        }
    }
}

This code will create a PriorityQueue of Book objects, sorted by year in ascending order. The output is:

The Age of Reason (1794)
The Great Gatsby (1925)
1984 (1949)
To Kill a Mockingbird (1960)

3. Using Comparators with PriorityQueue


To customize the sorting order in the PriorityQueue, we can use a Comparator.

3.1 Using Comparator.comparing()

Comparator.comparing() method from the Comparator class enables us to create comparators in a straightforward and readable manner. By leveraging Comparator.comparing(), we can specify custom sorting logic for a PriorityQueue like this:

ProrityQueueExample2.java

public class ProrityQueueExample2 {
 
    public static void main(String[] args) {
 
        PriorityQueue<Book> bookQueue = new PriorityQueue<>(Comparator.comparingInt(Book::getYear));
 
        bookQueue.add(new Book("To Kill a Mockingbird", 1960));
        bookQueue.add(new Book("1984", 1949));
        bookQueue.add(new Book("The Age of Reason", 1794));
        bookQueue.add(new Book("The Great Gatsby", 1925));
 
        while (!bookQueue.isEmpty()) {
            Book book = bookQueue.poll();
            System.out.println(book);
        }
 
    }
}

Here, the Books are sorted based on the year element in ascending order. Note that the Book class does not need to implement the Comparable interface for this example.

3.2 Using Lambda Expression

We can also utilize lambda expressions as follows:

public class ProrityQueueExample2 {
 
    public static void main(String[] args) {
 
       // Using Lambda expressions
        PriorityQueue<Book> bookQueue = new PriorityQueue<>((Book b1, Book b2) -> Integer.compare(b1.getYear(), b2.getYear()));
         
        bookQueue.add(new Book("To Kill a Mockingbird", 1960));
        bookQueue.add(new Book("1984", 1949));
        bookQueue.add(new Book("The Age of Reason", 1794));
        bookQueue.add(new Book("The Great Gatsby", 1925));
 
        while (!bookQueue.isEmpty()) {
            Book book = bookQueue.poll();
            System.out.println(book);
        }
 
    }
}

3.3 Changing the Sorting Order

To change the sorting order, simply modify the Comparator. For example, the code fragment below shows how to sort Books in descending order based on the year value:

PriorityQueue<Book> bookQueue = new PriorityQueue<>((Book b1, Book b2) -> Integer.compare(b2.getYear(), b1.getYear()));

The output becomes:

To Kill a Mockingbird (1960)
1984 (1949)
The Great Gatsby (1925)
The Age of Reason (1794)

Output demonstrating the use of a comparator to sort a Java PriorityQueue of pairs in descending order

4. Using Apache Commons Pair


The Apache Commons Lang library provides a Pair class which is a simple container to store a pair of objects. Using Apache Commons Pair with Java’s PriorityQueue can enhance the handling of paired data with specific priorities. Here’s an example of combining Apache Commons Pair with PriorityQueue:

ApachePairPriorityQueueExample.java

import java.util.Comparator;
import org.apache.commons.lang3.tuple.Pair;
import java.util.PriorityQueue;
 
public class ApachePairPriorityQueueExample {
 
    public static void main(String[] args) {
 
        // Example usage of the Pair class with PriorityQueue
        // PriorityQueue is initialized with the comparator
        PriorityQueue<Pair<String, Integer>> priorityQueue = new PriorityQueue<>(Comparator.comparingInt(Pair::getValue));
 
        // Adding Pairs to the priority queue
        priorityQueue.add(Pair.of("Finish Article", 3));
        priorityQueue.add(Pair.of("Buy Milk", 1));
        priorityQueue.add(Pair.of("Call Mom", 2));
 
        // Polling elements from the priority queue
        while (!priorityQueue.isEmpty()) {
            Pair<String, Integer> pair = priorityQueue.poll();
            System.out.println("Priority " + pair.getValue() + " : " + pair.getKey());
        }
    }
}

5. Conclusion

In this article, we explored how to use Java’s PriorityQueue with custom classes and the Apache Commons Pair class. We started with an overview of the PriorityQueue and delved into examples demonstrating how to create and manipulate a PriorityQueue with custom pairs, emphasizing how to use Comparator to control the sorting order. We also covered an example using a custom Book class, showcasing how PriorityQueue can be adapted to manage more complex objects. Finally, we demonstrated how to utilize Apache Commons’ Pair class for similar purposes.

Wednesday, June 12, 2024

Int to short Conversion in Java

Int to short Conversion in Java

When working with Java, we frequently face situations that require converting data types to meet specific needs. A common example is converting an int to a short. Let’s delve into understanding the Java Int to Short conversion process.

1. Convert from int to short in Java


In Java, converting an int to a short can be done in two ways.

◉ Casting int to short
◉ Using the Integer.shortValue() Method

1.1 Casting int to short


Casting is a straightforward way to convert an int to a short in Java. This involves explicitly telling the compiler to convert the int to a short. Since short is a smaller data type (16-bit) compared to int (32-bit), this can result in data loss if the int value exceeds the range of short (-32,768 to 32,767).

public class IntToShortCasting {
    public static void main(String[] args) {
        int intValue = 32000;
        short shortValue = (short) intValue;
        System.out.println("The int value: " + intValue);
        System.out.println("The short value: " + shortValue);
    }
}

Here’s a code breakdown:

  • intValue: This is the integer value that we want to convert.
  • shortValue = (short) intValue: The int value is cast to a short using the casting syntax.
  • System.out.println(): These lines print out the original int value and the converted short value.

The code output is:

The int value: 32000
 
The short value: 32000

If the int value is out of the short range, the result will be unexpected due to overflow:

public class IntToShortCastingOverflow {
    public static void main(String[] args) {
        int intValue = 33000;
        short shortValue = (short) intValue;
        System.out.println("The int value: " + intValue);
        System.out.println("The short value: " + shortValue);
    }
}

The code output is:

The int value: 33000
 
The short value: -32536

Here, the short value wraps around due to exceeding its maximum limit.

1.2. Using the Integer.shortValue()method


Another way to convert an int to a short is by using the Integer.shortValue() method. This method is part of the Integer class, which wraps a value of the primitive type int in an object.

public class IntToShortMethod {
    public static void main(String[] args) {
        Integer intValue = 32000;
        short shortValue = intValue.shortValue();
        System.out.println("The int value: " + intValue);
        System.out.println("The short value: " + shortValue);
    }
}

Here’s a code breakdown:

  • Integer intValue: Here, the int value is wrapped in an Integer object.
  • short shortValue = intValue.shortValue(): The shortValue() method is called on the Integer object to get its value as a short.
  • System.out.println(): These lines print out the original Integer value and the converted short value.

The code output is:

The int value: 32000
 
The short value: 32000

Similar to casting, if the int value is out of the short range, the result will also be unexpected:

public class IntToShortMethodOverflow {
    public static void main(String[] args) {
        Integer intValue = 33000;
        short shortValue = intValue.shortValue();
        System.out.println("The int value: " + intValue);
        System.out.println("The short value: " + shortValue);
    }
}

The code output is:

The int value: 33000
 
The short value: -32536

Again, the short value wraps around due to exceeding its maximum limit.

1.3 Potential Pitfalls


When converting from int to short in Java, there are several potential pitfalls to be aware of:

  • Data Loss: Since short has a smaller range than int, values that are too large or too small will not be accurately represented, leading to data loss.
  • Overflow: If the int value exceeds the range of short (-32,768 to 32,767), it will wrap around, resulting in unexpected and incorrect values.
  • Performance Considerations: Although casting and using the shortValue() method are both efficient, unnecessary conversions can lead to code that is harder to read and maintain.
  • Semantic Clarity: Converting types can sometimes obscure the original intent of the code, especially if the conversion is not well-documented or understood by other developers working on the same codebase.

Source: javacodegeeks.com

Monday, June 10, 2024

How to Traverse All Files from a Folder in Java

How to Traverse All Files from a Folder in Java

Traversing all files in a folder is a common task in Java, whether you need to read files, filter them based on certain criteria, or process them in some way. Java provides several ways to achieve this, from the traditional File class to the more modern java.nio.file package. This article will guide you through different methods for traversing files in a folder using Java.

1. Folder Structure Example


Let’s consider the folder /Users/omozegieaziegbe/development/oraclejavacertified/ containing the following files and folder structure for demonstration:

exampleFolder
    file4.doc
    file5.pdf
    subFolder1
        file.log
        file.txt
    subFolder2
        file3.txt
        subSubFolder2
            file5.txt

Directory structure used for example on traversing all files from a folder in Java

2. Using File.listFiles()


The File class is part of the java.io package and has been available since Java 1.0. It provides basic methods to list files and directories. To traverse all files and folders, including those within subdirectories, we need to recursively process each directory.

2.1 Example: Traverse All Files and Folders in a Directory Using File.listFiles()

FileTraversal.java

public class FileTraversal {
 
    public static void main(String[] args) {
 
        // Specify the directory path
        String directoryPath = "/Users/omozegieaziegbe/development/oraclejavacertified/";
 
        // Using File class (pre-Java 7)
        File directory = new File(directoryPath);
        traverseFiles(directory);
    }
 
    public static void traverseFiles(File folder) {
        if (folder.isDirectory()) {
            File[] files = folder.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        traverseFiles(file); // Recursive call for subdirectories
                    } else {
                        System.out.println("File: " + file.getAbsolutePath());
                    }
                }
            }
        }
    }
}

2.2 Explanation

  • Create a File Object: new File("path/to/your/folder") creates a File object representing the folder to traverse.
  • Check if It’s a Directory: Use isDirectory() to confirm the object is a directory.
  • List Files and Directories: listFiles() returns an array of File objects representing the files and directories in the folder.
  • Recursive Traversal: For each File object, if it’s a directory, print its path and recursively call traverseFiles(file). If it’s a file, print its path.

Output:

Running this code with the provided folder structure will produce the following output:

File: /Users/omozegieaziegbe/development/oraclejavacertified/exampleFolder/subFolder2/file3.txt
File: /Users/omozegieaziegbe/development/oraclejavacertified/exampleFolder/subFolder2/subSubFolder2/file5.txt
File: /Users/omozegieaziegbe/development/oraclejavacertified/exampleFolder/file5.pdf
File: /Users/omozegieaziegbe/development/oraclejavacertified/exampleFolder/file4.doc
File: /Users/omozegieaziegbe/development/oraclejavacertified/exampleFolder/subFolder1/file.txt
File: /Users/omozegieaziegbe/development/oraclejavacertified/exampleFolder/subFolder1/file.log

3. Traverse All Files and Folders Using Files.walk()


The Files.walk() method from the java.nio.file package offers a convenient way to traverse all files and directories within a folder and its subfolders. It returns a Stream of Path objects that can be filtered and processed using stream operations.

3.1 Example: Using Files.walk() to Traverse All Files

NIOFileTraversal.java

public class NIOFileTraversal {
 
    public static void main(String[] args) {
 
        Path folderPath = Paths.get("/Users/omozegieaziegbe/development/oraclejavacertified/"); // Adjust this path to match your folder structure
        try (Stream<Path> paths = Files.walk(folderPath)) {
            paths.filter(Files::isRegularFile)
                    .forEach(System.out::println); // Process each file
        } catch (IOException e) {
        }
    }
}

How to Traverse All Files from a Folder in Java
The program above uses Files.walk() method to generate a Stream of Path objects representing all files and directories within the specified folder and its subfolders. The filter(Files::isRegularFile) method filters out directories, leaving only regular files in the stream. The forEach(System.out::println) method processes each file by printing its path to the console.

3.2 Advantages of Using Files.walk()

  • Concise and Readable: The use of streams makes the code concise and easy to read.
  • Flexible: You can easily filter, map, and process the paths using various stream operations.
  • Handles Large Directories: The lazy evaluation of streams allows efficient handling of large directories without loading all paths into memory.

4. Find a File from a Folder and Its Subfolders


Finding a specific file in a directory and its subdirectories can be efficiently achieved using the Files.walk() API. This involves filtering the stream based on the file name or other criteria.

4.1 Example: Find a Specific File

FindFile.java

public class FindFile {
 
    public static void main(String[] args) {
         
        Path folderPath = Paths.get("/Users/omozegieaziegbe/development/oraclejavacertified/");
        String fileNameToFind = "file.log";
         
        try (Stream<Path> paths = Files.walk(folderPath)) {
            Optional<Path> foundFile = paths.filter(Files::isRegularFile)
                                            .filter(path -> path.getFileName().toString().equals(fileNameToFind))
                                            .findFirst();
 
            foundFile.ifPresent(System.out::println); // Process the found file
        } catch (IOException e) {
        }
    }
}

4.2 Explanation

  • Create a Path Object: Paths.get("path/to/your/folder") creates a Path object for the root folder.
  • Use Files.walk(): This method generates a stream of paths for the entire directory tree.
  • Filter and Find: The stream is filtered to include only regular files, and further filtered to match the desired file name. findFirst() is used to return an Optional<Path> of the first matching file.
  • Process the Found File: If the file is found, its path is printed or we can replace this with any other processing logic.

Running the code above will give the following output:

/Users/omozegieaziegbe/development/oraclejavacertified/exampleFolder/subFolder1/file.log

Source: javacodegeeks.com

Friday, June 7, 2024

Gson Support for Java 8 Date-Time Types

Gson Support for Java 8 Date-Time Types

Java 8 introduced a robust date and time API with classes such as LocalDate, LocalDateTime, and ZonedDateTime. These classes provide an improved way to handle dates and times compared to the legacy java.util.Date and java.util.Calendar classes. However, by default, Gson does not support these new date-time types. This lack of support can lead to issues when trying to serialize and deserialize these types to and from JSON. This article will demonstrate how to create these custom serializers and deserializers for LocalDate, LocalDateTime, and ZonedDateTime.

1. Set Up Gson


To use Gson, first, ensure the library is included in the project. If using Maven, add the following dependency to the pom.xml:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.11.0</version>
</dependency>

2. Example Problem


Consider a simple Java class containing a LocalDate field:

Person.java

public class Person {
    private String name;
    private LocalDate birthDate;
 
    public Person(String name, LocalDate birthDate) {
        this.name = name;
        this.birthDate = birthDate;
    }
 
    // Getters and setters omitted for brevity
}

Now, let’s try to serialize an instance of this Person class using default Gson:

DefaultGsonExample.java

public class DefaultGsonExample {
 
    public static void main(String[] args) {
         
        Person person = new Person("John Doe", LocalDate.of(2022, 10, 1));
        Gson gson = new Gson();
        String json = gson.toJson(person);
        System.out.println("Serialized JSON: " + json);
    }
}

Output is:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.gson.internal.reflect.ReflectionHelper (file:/Users/omozegieaziegbe/.m2/repository/com/google/code/gson/gson/2.10.1/gson-2.10.1.jar) to field java.time.LocalDate.year
WARNING: Please consider reporting this to the maintainers of com.google.gson.internal.reflect.ReflectionHelper
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Serialized JSON: {"name":"John Doe","birthDate":{"year":2022,"month":10,"day":1}}

As seen, the birthDate field is not properly serialized. Now let’s try to deserialize a JSON string back to a Person object:

DefaultGsonExample.java

public class DefaultGsonExample {
 
    public static void main(String[] args) {
        String json = "{\"name\":\"John Doe\",\"birthDate\":\"1990-01-01\"}";
        Gson gson = new Gson();
        Person person = gson.fromJson(json, Person.class);
        System.out.println("Deserialized Person: " + person.getBirthDate());
    }
}

Output is:

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 33 path $.birthDate
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read (ReflectiveTypeAdapterFactory.java:397)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.readIntoField (ReflectiveTypeAdapterFactory.java:212)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$FieldReflectionAdapter.readField (ReflectiveTypeAdapterFactory.java:433)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read (ReflectiveTypeAdapterFactory.java:393)
    at com.google.gson.Gson.fromJson (Gson.java:1227)
    at com.google.gson.Gson.fromJson (Gson.java:1137)
    at com.google.gson.Gson.fromJson (Gson.java:1047)
    at com.google.gson.Gson.fromJson (Gson.java:982)
    at com.jcg.defaultjsonexample.DefaultGsonExample.main (DefaultGsonExample.java:18)
    at org.codehaus.mojo.exec.ExecJavaMojo$1.run (ExecJavaMojo.java:279)
    at java.lang.Thread.run (Thread.java:834)

The deserialization fails with a JsonSyntaxException, indicating that Gson expected a JSON object but encountered a string instead. This occurs because Gson does not know how to handle the LocalDate type, resulting in a mismatch between the expected and actual JSON structures.

3. Solution: Custom Serializers and Deserializers


To resolve this issue, we need to create custom serializers and deserializers for Java 8 date-time types and register them with Gson. These custom adapters convert LocalDateTime instances to JSON strings and vice versa. This is crucial because LocalDateTime is not natively supported by Gson, and attempting to serialize or deserialize LocalDateTime objects without custom adapters will result in errors or incorrect data representation.

3.1 LocalDate Serializer and Deserializer

We will create a class for handling LocalDate type in Gson.

LocalDateAdapter.java

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
 
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
 
public class LocalDateAdapter implements JsonSerializer<LocalDate>, JsonDeserializer<LocalDate> {
 
    private static final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
 
    @Override
    public JsonElement serialize(LocalDate src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(src.format(formatter));
    }
 
    @Override
    public LocalDate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        return LocalDate.parse(json.getAsString(), formatter);
    }
}

3.1.1 Register LocalDateAdapter with Gson

With the custom serializer and deserializer LocalDateAdapter ready, we need to register it with a Gson instance.

CustomDateTimeExample.java

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.time.LocalDate;
 
public class CustomDateTimeExample {
 
    public static void main(String[] args) {
        Gson gson = new GsonBuilder()
                .registerTypeAdapter(LocalDate.class, new LocalDateAdapter())
                .create();
 
        Person person = new Person("John Doe", LocalDate.of(2022, 10, 1));    
         
        // Serialize
        String json = gson.toJson(person);
        System.out.println("Serialized Person with LocalDate: " + json);
         
        //Deserialize
        Person deserializedPerson = gson.fromJson(json, Person.class);
        System.out.println("Deserialized Person with LocalDate: " + deserializedPerson);
    }
 
}

The program output is:

Serialized Person with LocalDate: {"name":"John Doe","birthDate":"2022-10-01"}
Deserialized Person with LocalDate: Person(name=John Doe, birthDate=2022-10-01)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

Output from using custom serializer and deserializer for gson support with Java 8 local time

3.2 LocalDateTime Serializer and Deserializer

Create a LocalDateTimeAdapter class to implement both JsonSerializer<LocalDateTime> and JsonDeserializer<LocalDateTime>. I used the DateTimeFormatter.ISO_LOCAL_DATE_TIME to ensure that the date-time is formatted according to the ISO-8601 standard.

LocalDateTimeAdapter.java

import com.google.gson.JsonDeserializer;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonParseException;
 
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
 
public class LocalDateTimeAdapter implements JsonSerializer<LocalDateTime>, JsonDeserializer<LocalDateTime> {
    private static final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
 
    @Override
    public JsonElement serialize(LocalDateTime src, Type typeOfSrc, com.google.gson.JsonSerializationContext context) {
        return new JsonPrimitive(src.format(formatter));
    }
 
    @Override
    public LocalDateTime deserialize(JsonElement json, Type typeOfT, com.google.gson.JsonDeserializationContext context) throws JsonParseException {
        return LocalDateTime.parse(json.getAsString(), formatter);
    }
}

Explanation

◉ Serialization: The serialize method takes a LocalDateTime object and converts it to a JSON primitive string using the ISO-8601 format. This ensures that the LocalDateTime is represented as a standard string in JSON.
◉ Deserialization: The deserialize method takes a JSON element (expected to be a string) and converts it back to a LocalDateTime object using the same ISO-8601 format. This ensures that the string is correctly parsed back into a LocalDateTime instance.

3.2.1 Register LocalDateTimeAdapter with Gson

To handle LocalDateTime objects correctly, we need to register our custom LocalDateTimeAdapter with Gson. Here is how to register the LocalDateTimeAdapter:

CustomDateTimeExample.java

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.time.LocalDateTime;
 
public class CustomDateTimeExample {
 
    public static void main(String[] args) {
       Gson gson = new GsonBuilder()
                .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter())
                .create();
 
 
        Person person = new Person("John Doe", LocalDateTime.now());    
         
        // Serialize
        String json = gson.toJson(person);
        System.out.println("Serialized Person with LocalDateTime: " + json);
         
        //Deserialize
        Person deserializedPerson = gson.fromJson(json, Person.class);
        System.out.println("Deserialized Person with LocalDateTime: " + deserializedPerson);
    }
 
}

Output is:

Serialized Person with LocalDateTime: {"name":"John Doe","birthDate":"2024-06-05T17:32:36.982656"}
Deserialized Person with LocalDateTime: Person{name=John Doe, birthDate=2024-06-05T17:32:36.982656}

3.3 ZonedDateTime Serializer and Deserializer

Let’s create a ZonedDateTimeAdapter class that implements both JsonSerializer<ZonedDateTime> and JsonDeserializer<ZonedDateTime>. I used the DateTimeFormatter.ISO_ZONED_DATE_TIME to ensure that the date-time, including the time zone information, is formatted according to the ISO-8601 standard.

ZonedDateTimeAdapter.java

import com.google.gson.JsonDeserializer;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonParseException;
 
import java.lang.reflect.Type;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
 
 
public class ZonedDateTimeAdapter implements JsonSerializer<ZonedDateTime>, JsonDeserializer<ZonedDateTime> {
    private static final DateTimeFormatter formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME;
 
    @Override
    public JsonElement serialize(ZonedDateTime src, Type typeOfSrc, com.google.gson.JsonSerializationContext context) {
        return new JsonPrimitive(src.format(formatter));
    }
 
    @Override
    public ZonedDateTime deserialize(JsonElement json, Type typeOfT, com.google.gson.JsonDeserializationContext context) throws JsonParseException {
        return ZonedDateTime.parse(json.getAsString(), formatter);
    }
}

3.3.1 Register ZonedDateTimeAdapter with Gson

Similarly, to handle ZonedDateTime objects, we need to register our custom ZonedDateTimeAdapter with Gson. This ensures that Gson knows how to correctly serialize and deserialize ZonedDateTime instances, preserving both the date-time and the time zone information.

Here’s how to register the ZonedDateTimeAdapter:

CustomDateTimeExample.java

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.time.ZonedDateTime;
 
public class CustomDateTimeExample {
 
    public static void main(String[] args) {
       Gson gson = new GsonBuilder()
                .registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeAdapter())
                .create();
 
 
        Person person = new Person("John Doe", ZonedDateTime.now());    
         
        // Serialize
        String json = gson.toJson(person);
        System.out.println("Serialized Person with ZonedDateTime: " + json);
         
        //Deserialize
        Person deserializedPerson = gson.fromJson(json, Person.class);
        System.out.println("Deserialized Person with ZonedDateTime: " + deserializedPerson);
    }
}

In the code above, the registerTypeAdapter method is used to register our custom-type adapter (ZonedDateTimeAdapter) for the ZonedDateTime class.

Output is:

Serialized Person with ZonedDateTime: {"name":"John Doe","birthDate":"2024-06-05T17:36:30.947555+01:00[Africa/Lagos]"}
Deserialized Person with ZonedDateTime: Person{name=John Doe, birthDate=2024-06-05T17:36:30.947555+01:00[Africa/Lagos]}

Source: javacodegeeks.com