top of page

SEARCH RESULTS

70 items found for ""

  • Searching vs. Sorting in Java: Key Differences and Applications

    This article describes the differences between searching and sorting algorithms in Java, their distinct purposes, methodologies, and time complexities. It includes practical examples and implementations, such as Merge Sort for organizing data and Binary Search for efficient retrieval, demonstrating their roles in solving real-world problems. Alexander S. Ricciardi July 14, 2024 In Java, understanding searching and sorting algorithms and how they differ from each other, is crucial for the correct functionality of the application and for effectively managing data. While searching focuses on locating specific data within a collection, sorting rearranges data. This article explores their differences in purpose, methodology, and applications, by providing examples. The major differences between searching and sorting in Java lie in their purposes and outputs, as well as their efficiencies and time complexities. Please Table 1 for a detailed comparison. Table 1 Searching vs Sorting in Java Choosing between different searching or sorting algorithms often depends on the purpose or output wanted and the specific requirements of your application, such as the size of the dataset, and whether the data is already sorted. The following table, Table 2, gives examples of pseudocode and time complexity for several searches and sort algorithms: Table 2 Runtime Complexities for Various Pseudocode Examples Note: In Java without using the Comparable Interface the code above would only be viable for primitive types.  From Programming in Java with ZyLabs , 18.3 O notation, Figure 18.3.2 by  Lysecky, R., & Lizarraga, A. (2022). An example of a sort algorithm is the merge sort, which has the divide-and-conquer approach, it recursively divides a data array into smaller subarrays and sorts those subarrays, then merges the subarrays together to create a sorted array (GeeksforGeeks, 2020a).An example of a search algorithm is the binary search; which operates on a pre-sorted array by repeatedly dividing the search interval in half until the target element is found or determined to be absent (GeeksforGeeks, 2020b). The example below sorts using merge sort an ArrayList of book objects by year of publication then searches the sorted list using binary: Book.java /** * Book object with a title and publication year. This class implements * Comparable to allow sorting based on the publication year. * * @author Alexander Ricciardi * @version 1.0 * @date 07/14/2024 */ class Book implements Comparable { String title; int year; /** * Constructs a new Book object. * * @param title The title of the book. * @param year The year the book was published. */ public Book(String title, int year) { this.title = title; this.year = year; } /** * Compares this book with another book based on the publication year. * * @param other The book to compare with. * @return A negative integer, zero, or a positive integer as this book is less * than, equal to, or greater than the specified book. */ @Override public int compareTo(Book other) { return Integer.compare(this.year, other.year); } /** * Returns a string representation of the book. * * @return A string in the format "title (year)". */ @Override public String toString() { return title + " (" + year + ")"; } } BookSortingSearching.java i mport java.util.ArrayList; import java.util.Arrays; import java.util.Scanner; /** * Sorts and search a list of books. It implements merge sort for sorting and * binary search for searching. * * @author Alexander Ricciardi * @version 1.0 * @date 07/14/2024 */ public class BookSortingSearching { /** * The main method that demonstrates sorting and searching on a list of books. * * @param args Command line arguments (not used). */ public static void main(String[] args) { // Initialize the list of books ArrayList books = new ArrayList<>( Arrays.asList(new Book("To Kill a Mockingbird", 1960), new Book("1984", 1949), new Book("The Great Gatsby", 1925), new Book("One Hundred Years of Solitude", 1967), new Book("The Catcher in the Rye", 1951), new Book("Brave New World", 1932), new Book("The Hobbit", 1937), new Book("The Lord of the Rings", 1954), new Book("Pride and Prejudice", 1813), new Book("Animal Farm", 1945) )); // Print the original list System.out.println("Original list:"); books.forEach(System.out::println); // Sort the books using merge sort mergeSort(books, 0, books.size() - 1); // Print the sorted list System.out.println("\nSorted list by year:"); books.forEach(System.out::println); // Perform binary search based on user input Scanner scn = new Scanner(System.in); System.out.print("\nEnter a year to search for: "); int searchYear = scn.nextInt(); int result = binarySearch(books, searchYear); if (result != -1) { System.out.println("Book found: " + books.get(result)); } else { System.out.println("No book found for the year " + searchYear); } scn.close(); } /** * Sorts the given list of books using the merge sort algorithm. * * @param books The list of books to sort. * @param left The starting index of the subarray to sort. * @param right The ending index of the subarray to sort. */ private static void mergeSort(ArrayList books, int left, int right) { if (left < right) { int mid = (left + right) / 2; mergeSort(books, left, mid); // Sort left half mergeSort(books, mid + 1, right); // Sort right half merge(books, left, mid, right); // Merge the sorted halves } } /** * Merges two sorted subarrays of the books list. * * @param books The list of books containing the subarrays to merge. * @param left The starting index of the left subarray. * @param mid The ending index of the left subarray. * @param right The ending index of the right subarray. */ private static void merge(ArrayList books, int left, int mid, int right) { // Create temporary arrays ArrayList leftList = new ArrayList<>(books.subList(left, mid + 1)); ArrayList rightList = new ArrayList<>(books.subList(mid + 1, right + 1)); int i = 0, j = 0, k = left; // Merge the two lists while (i < leftList.size() && j < rightList.size()) { if (leftList.get(i).compareTo(rightList.get(j)) <= 0) { books.set(k++, leftList.get(i++)); } else { books.set(k++, rightList.get(j++)); } } // Copy remaining elements of leftList, if any while (i < leftList.size()) { books.set(k++, leftList.get(i++)); } // Copy remaining elements of rightList, if any while (j < rightList.size()) { books.set(k++, rightList.get(j++)); } } /** * Performs a binary search on the sorted list of books to find a book by its * publication year. * * @param books The sorted list of books to search. * @param year The publication year to search for. * @return The index of the book if found, -1 otherwise. */ private static int binarySearch(ArrayList books, int year) { int left = 0, right = books.size() - 1; while (left <= right) { int mid = left + (right - left) / 2; if (books.get(mid).year == year) { return mid; // Book found } if (books.get(mid).year < year) { left = mid + 1; // Search in the right half } else { right = mid - 1; // Search in the left half } } return -1; // Book not found } } Output: To Kill a Mockingbird (1960) 1984 (1949) The Great Gatsby (1925) One Hundred Years of Solitude (1967) The Catcher in the Rye (1951) Brave New World (1932) The Hobbit (1937) The Lord of the Rings (1954) Pride and Prejudice (1813) Animal Farm (1945) Sorted list by year: Pride and Prejudice (1813) The Great Gatsby (1925) Brave New World (1932) The Hobbit (1937) Animal Farm (1945) 1984 (1949) The Catcher in the Rye (1951) The Lord of the Rings (1954) To Kill a Mockingbird (1960) One Hundred Years of Solitude (1967) Enter a year to search for: 1951 Book found: The Catcher in the Rye (1951) In other words, merge sort is efficient for sorting large sets of data due to its complexity of O(n log(n)) , while binary search with its targeted approach to search is better suited for machine learning applications, such as those for training neural networks or finding the optimal hyperparameters for a model. In summary, searching and sorting algorithms have interconnected roles in programming but serve different purposes. Sorting algorithms like Merge Sort organize the data, allowing searching methods such as Binary Search to be more efficient. Together, these algorithms are indispensable for solving real-world problems, from data analysis to application development. References GeeksforGeeks. (2020a, November 18). Merge sort. GeeksforGeeks https://www.geeksforgeeks.org/merge-sort/ GeeksforGeeks. (2020b, February 3). Binary search. GeeksforGeeks. https://www.geeksforgeeks.org/binary-search/ Lysecky, R., & Lizarraga, A. (2022). Programming in Java with ZyLabs[ Table]. Zyante, Inc.

  • GUI Design with JavaFX Layout Managers

    This article explores how Java Layout Managers provide an abstraction that streamlines the development of Graphical User Interfaces (GUIs) in JavaFX by automating component sizing and positioning. Using predefined layouts like HBox, VBox, and GridPane, developers can create organized and responsive interfaces. Alexander S. Ricciardi June 25, 2024 The Java Layout Manager provides an easy way to develop Graphical User Interfaces (GUIs), particularly by offering tools to manage and organize GUI components. It is responsible for determining the dimensions and placement of components within a container (Oracle Docs. n.d.). While components can suggest their preferred sizes and alignments, the layout manager of the container ultimately decides the final size and position of these components. The Java Layout Manager provides a simpler approach to using panes (Gordon, 2013). It also facilitates the creation and management of standard layouts like rows, columns, stacks, tiles, and more. Additionally, when the window is resized, the layout pane automatically adjusts the positioning and size of its contained nodes based on their properties, it is responsive. Additionally, this article offers a Zoo Exabit program example of how layout managers can be used to arrange UI elements. JavaFX offers a variety of layouts that can fit different GUI needs and functionalities. Layouts such as HBox, VBox, GridPane, BorderPane, StackPane, and FlowPane, see Figure 1. Figure 1 Example of JavaFX Layouts Note : from “3/10 - Introduction and overview of JavaFX panes or GUI containers for layout” by JavaHandsOnTeaching (2021) Zoo Exabit Program Example The program displays the animals found in various exhibits of a Zoo using the JavaFx VBox and HBox layouts, see Figure 2 to see how the different layout panes are positioned. Figure 2 Zoo Layout Panes Java File Source Code import javafx.application.Application; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage primaryStage) { // Title for the window Label title = new Label("Zoo Exhibits"); title.getStyleClass().add("title"); // Create main VBox layout VBox mainLayout = new VBox(20); // Center align the contents of the VBox (title and Horizontal boxes for the two // sets of exhibits) mainLayout.setAlignment(Pos.CENTER); // Horizontal boxes for the two sets of exhibits HBox firstSetExhibits = new HBox(10); firstSetExhibits.setAlignment(Pos.CENTER); // Center align the contents of the HBox firstSetExhibits.getChildren().add(createExhibitSection("Africa", "Lion", "Elephant", "Giraffe")); firstSetExhibits.getChildren().add(createExhibitSection("South America", "Jaguar", "Llama", "Macaw")); firstSetExhibits.getChildren().add(createExhibitSection("Australia", "Kangaroo", "Koala", "Platypus")); HBox secondSetExhibits = new HBox(10); secondSetExhibits.setAlignment(Pos.CENTER); // Center align the contents of the Exhibit // HBox secondSetExhibits.getChildren() .add(createExhibitSection("North America", "Bison", "Bald Eagle", "Grizzly Bear")); secondSetExhibits.getChildren().add(createExhibitSection("Asia", "Tiger", "Panda", "Orangutan")); secondSetExhibits.getChildren().add(createExhibitSection("Europe", "Wolf", "Brown Bear", "Red Deer")); // Add the title and horizontal sets to the main layout mainLayout.getChildren().addAll(title, firstSetExhibits, secondSetExhibits); // Create a Scene Scene scene = new Scene(mainLayout, 500, 500); // Load the CSS file scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); // Set the scene on the primary stage primaryStage.setTitle("Zoo Exhibits"); primaryStage.setScene(scene); primaryStage.show(); } // String... passes multiple string an array of strings not a set size private VBox createExhibitSection(String continent, String... animals) { VBox exhibitSection = new VBox(5); exhibitSection.setAlignment(Pos.CENTER); // Center align the exhibit section labels exhibitSection.getStyleClass().add("exhibit-section"); // Title label for the continent Label continentLabel = new Label(continent); continentLabel.getStyleClass().add("continent-label"); exhibitSection.getChildren().add(continentLabel); // Vertical box to hold animal labels VBox animalsBox = new VBox(5); animalsBox.setAlignment(Pos.CENTER); // Center align the animal labels for (String animal : animals) { Label animalLabel = new Label(animal); animalLabel.getStyleClass().add("animal-label"); animalsBox.getChildren().add(animalLabel); } // Add the VBox to the section exhibitSection.getChildren().add(animalsBox); return exhibitSection; } public static void main(String[] args) { launch(args); } } CSS File .title { -fx-font-size: 24px; -fx-font-weight: bold; -fx-padding: 10px; -fx-background-color: #f0f0f0; } .main-content-title { -fx-font-size: 20px; -fx-font-weight: bold; -fx-padding: 5px; -fx-background-color: #d0d0d0; } .exhibit-section { -fx-padding: 10px; -fx-background-color: #f9f9f9; -fx-border-radius: 5px; } .continent-label { -fx-font-size: 16px; -fx-font-weight: bold; } .animal-label { -fx-font-size: 14px; -fx-border-radius: 3px; -fx-padding: 3px; } Outputs: To summarize, Java Layout Managers provided an easy way to develop GUI by offering a robust and flexible framework. Java Layout Managers’ abstraction allows developers to focus on creating organized layouts using predefined panes like HBox, VBox, GridPane, and others without having to manually manage the placement and sizing of each component. The Zoo Exhibits program shows how layout managers can be used to arrange UI elements and ensure that applications remain adaptable to window resizing and different display environments. Java Layout Managers is a powerful abstraction that not only facilitates the development process but also enhances the user experience by providing consistent and dynamic interface structures. References: Gordon, J. (2013, June). JavaFX: Working with layouts in JavaFX [PDF]. Oracle Docs. https://docs.oracle.com/javafx/2/layout/jfxpub-layout.pdf/ JavaHandsOnTeaching (2021, June 19). 3/10 - Introduction and overview of JavaFX panes or GUI containers for layout [Video]. YouTube. https://www.youtube.com/watch?v=GH-3YRAuHs0&t=905s Oracle Docs. (n.d). Using layout managers . The Java™ Tutorials. Oracle. https://docs.oracle.com/javase%2Ftutorial%2Fuiswing%2F%2F/layout/using.html

  • Bayes' Theorem: Risk Prediction and AI in The Insurance Sector

    The article explains Bayes' Theorem as a mathematical formula used to update probabilities based on new evidence, illustrating its applications in risk prediction for insurance companies. It highlights the theorem's integration into AI systems to enhance decision-making while addressing potential biases, privacy, and security concerns. Alexander S. Ricciardi November 13, 2024 In probability theory, Bayes' Theorem, also known as Bayes' Rule or Bayes' Law, is a mathematical formula that updates the probability of a hypothesis based on new evidence. In statistics, it is a way to revise or update existing predictions or theories based on new or additional evidence (Hayes, 2024), In other words, it is a mathematical formula for determining conditional probability. A conditional probability is the likelihood of an outcome occurring based on a previous outcome that occurred under similar circumstances. Thus, Bayes' Theorem can also be defined as a mathematical property that allows conditional probability to be expressed in terms of the inverse of the conditional (Data Science Discovery, n.d.). The Bayes' Theorem formula: Where: P(A) is the prior probability of an event A . P(B) is the probability of an event B . P(B|A) is the probability of an event B occurring given that A has occurred. P(A|B) is the probability of an event A occurring given that B has occurred. Therefore, the Bayes' Theorem computes the reverse of the conditional probability. That is the probability of a cause A given an effect B ,  P(A|B) , when the overall probability of the cause, P(A), the probability of the effect, P(B) , and the probability of the effect occurring given that the cause has occurred, P(B|A) are known.   Note: P(B) = P(B∣A) P(A) + P(B∣¬A) P(¬A) Where: P(B∣¬A) – the probability of event B occurring given that ¬A event has occurred P(B∣A) – the probability of event B occurring given that A event has occurred (Taylor, 2023) Another way to formulate Bayes' Theorem is to base it on a hypothesis context: Where: H : The hypothesis (e.g., a person has a certain disease) E: Evidence (e.g., the person tests positive in a diagnostic test) P(H): Prior probability is the known probability about the hypothesis or the initial belief about the hypothesis, that is the beliefs before observing the evidence. (e.g., the probability someone has a specific disease before considering specific symptoms or test results) P(E) : Marginal probability is the probability of observing the evidence under all possible scenarios. It may be thought of as an unconditional probability, or it is not conditioned on another event (Albright, n.d.).    P(E) = P(E∣H) P(H) + P(E∣¬H) P(¬H) (e.g., the probability that a diagnostic test would show a positive result, whether or not the person actually has the disease). P(E∣H) : Likelihood is the probability of observing the evidence. (e.g., a positive test result) given that the hypothesis is true. In other words, it is the likelihood of E being true based on H being true. (e.g., the likelihood of a positive test result being accurate, knowing that the person has the disease; note that medical tests are not 100% accurate, false positive) On the other hand, P(H∣E) is the likelihood of H being true based on E being true. P(H∣E) : The posterior probability is the updated probability of the hypothesis given the observed evidence. (e.g., the probability that the person actually has the disease, given that they tested positive) The formulated Bayes' Theorem above can be used to assess risks by calculating the likelihood of an event, such as accidents, illnesses, or natural disasters. This information is very valuable for an insurance company, enabling it to better understand and predict potential risks. The steps below describe how the theorem can be applied to predict the risk of a person developing a disease: Prior Knowledge: Establish the disease hypothesis, such as the probability someone has a specific disease before considering specific symptoms or test results, then collect relevant data and compute the prior probability P(H) from it.  For example: If 5 in 1,000 people in the general population have the disease, therefore the prior is 0.005. P(H) = 0.005 Incorporate the Evidence: Incorporate evidence such as a positive result from a diagnostic test. That is the Likelihood, P(E∣H). That is the likelihood of a positive test result being accurate, knowing that the person has the disease. For example: The test is accurate 96% of the time. P(E∣H) = 0.96 Account for False Positives: Compute The marginal probability P(E) . That is the probability that a diagnostic test would show a positive result, whether or not the person actually has the disease. - True Positives P(E∣H)P(H) , the test correctly identifies the disease. - False Positives P(E∣¬H)P(¬H) , the test incorrectly identifies the disease. Then P(E) = P(E∣H) P(H) + P(E∣¬H) P(¬H) For example: P(E∣¬H) = 0.04 (4% false positive rate) P(¬H) = 1 − P(H) =0.995 P(¬H) = 0.995. P(E) = P(E∣H) P(H) +P(E∣¬H) P(¬H) = (0.96)( 0.005) +( 0.04)(0.995) P(E)= 0.0048 + 0.0398 = 0.0446 P(E) = 0.0446 Compute the Posterior: Use Bayes' Theorem to update the probability based on the evidence. That is the probability that the person actually has the disease, given that they tested positive. For example: P(H∣E) ≈ 0.1076 Thus, even though the test is 96% accurate the posterior probability P(H∣E) is equal to 10.76%. This is due to P(H ) being only 0.5% (5 in 1,000 people in the general population have the disease). In other words, a person having a positive test result increases the person's probability of having the disease from 0.5% to about 10.76% ; however, it is still more likely that the person does not have the disease. For the insurance, it means that it needs to consider both test accuracy, P(E∣H) = 0.96 , and disease prevalence, P(H) , to not overestimate risks, as risks are low even with a positive test, and to set insurance premiums that represent a fair and accurate assessment of the probability of a person having the diseases. As shown from the example above, the Bayes' Theorem is a powerful tool that provides more accurate predictions than solely relying on simple probabilities or data alone. An Artificial Intelligence (AI) Bayes' Theorem implementation can transform and enhance the insurance company's decision-making processes by providing: More accurate predictions of risks, such as accidents, illnesses, or natural disasters real-time data processing. More personalized and refined prediction-based individual-level data. By analyzing vast amounts of data.- By automating systems streamlining claims processing and fraud detection. However, a Bayes' Theorem AI may invertedly create biases. Additionally, by using vast personal and private amounts of data such as medical history and financial information the AI system introduces privacy and security concerns, as well as data regulatory compliance challenges. These potential issues need to be carefully considered when using such systems. To summarize, Bayes' Theorem is a mathematical formula that updates the probability of a hypothesis based on new evidence providing a way to incorporate prior knowledge and observed data in real time into predictions.  It is a powerful tool that provides more accurate predictions than solely relying on simple probabilities or data alone and when implemented in an AI model can transform and enhance the insurance company's decision-making processes. References: Albright, E. (n.d.). Probability: Joint, marginal and conditional probabilities. ENV710 Statistics Review. Nicholas School of the Environment | Duke University. https://sites.nicholas.duke.edu/statsreview/jmc/ Data Science Discovery (n.d.). Bayes’ theorem. University of Illinois at Urbana-Champaign (UIUC). https://discovery.cs.illinois.edu/learn/Prediction-and-Probability/Bayes-Theorem/ Hayes, A. (2024, March 30). Bayes' theorem: What it is, the formula, and examples. Investopedia. https://www.investopedia.com/terms/b/bayes-theorem.asp Taylor, S. (2023, November 21). Bayes’ Theorem. Corporate Finance Institute. https://corporatefinanceinstitute.com/resources/data-science/bayes-theorem/

  • Securing Sensitive Data in Java: Best Practices and Coding Guidelines

    The article explores the importance of protecting sensitive data in Java applications and highlights common vulnerabilities, including improper data handling, injection attacks, and input validation failures. It provides secure coding guidelines from Oracle, along with examples of unsafe and safe code practices. Alexander S. Ricciardi November 14, 2024 Sensitive data is information that individuals or organizations want to protect from public exposure as if its unintentional release or stolen could result in harm to the person or the organization in the form, for example, of identity theft or other criminal intentions (Baig, 2021). For individuals, this may include personal details like payment information or birth dates, and for organizations, it could be proprietary corporate information. Java, as a programming language, incorporates several abstractions to secure sensitive data. However, data security can still be compromised, in an application, by different factors such as improper handling of sensitive information and vulnerabilities to data injection attacks, as well as insufficient input validation and the unsafe handling of mutable objects. Oracle (n.d.), the corporation that owns the rights to Java, provides coding guidelines for Java SE, The following is a list of these guidelines. - Guideline 2 Confidential Information (Oracle, n.d.). Guideline 2-1 / CONFIDENTIAL-1: Purge sensitive information from exceptions Sensitive information in exceptions should not reveal internal states or paths. Guideline 2-2 / CONFIDENTIAL-2: Do not log highly sensitive information Logs should exclude sensitive details like passwords or security tokens. Guideline 2-3 / CONFIDENTIAL-3: Consider purging highly sensitive information from memory after use Clearing sensitive data from memory reduces its exposure window. If sensitive information is logged or stored insecurely, it becomes vulnerable to unauthorized access.  Code examples: Unsafe code, an application that logs sensitive user passwords in clear text violates the principle of purging sensitive information from logs. public class PasswordLogger { public void logPassword(String password) { // Logs sensitive data—violates secure coding guidelines System.out.println("Password: " + password); } } Safe code, to comply with secure coding guidelines, sensitive data should be sanitized or excluded from logs entirely. public class SecurePasswordLogger { public void logPassword() { System.out.println("Password logging is not permitted."); } } - Guideline 3 Injection and Inclusion (Oracle, n.d.). Guideline 3-1 / INJECT-1: Generate valid formatting Input should always be sanitized to prevent incorrect formatting issues. Guideline 3-2 / INJECT-2: Avoid dynamic SQL Always use parameterized SQL statement queries to eliminate SQL injection risks. These vulnerabilities may allow attackers to manipulate queries and access, modify, or delete sensitive data Code examples: Unsafe code, using dynamic SQL queries to process user inputs without sanitization is a common mistake. String query = "SELECT * FROM users WHERE username = '" + username + "'"; Statement stmt = connection.createStatement();ResultSet rs = stmt.executeQuery(query); Safe code, instead, parameterized queries should be used to prevent injection attacks: String query = "SELECT * FROM users WHERE username = ?"; PreparedStatement pstmt = connection.prepareStatement(query); pstmt.setString(1, username); ResultSet rs = pstmt.executeQuery(); - Guideline 5 Input Validation (Oracle, n.d.). Guideline 5-1 / INPUT-1: Validate inputs Input from untrusted sources should be sanitized and validated. Guideline 5-2 / INPUT-2: Validate output from untrusted objects as input Output from untrusted sources should be revalidated before further processing. These vulnerabilities may allow attackers may exploit improperly validated inputs to execute malicious code or access restricted data. Code example: Safe code, proper input validation ensures that malicious code is not injected. public void validateInput(String userInput) { if (userInput == null || userInput.isEmpty() || userInput.contains("..")) { throw new IllegalArgumentException("Invalid input detected."); } System.out.println("Validated input: " + userInput); } - Guideline 6 Mutability (Oracle, n.d.). Guideline 6-1 / MUTABLE-1: Prefer immutability for value types Immutable objects avoid unintended modifications in shared contexts. Guideline 6-2 / MUTABLE-2: Create copies of mutable output values Return copies of mutable objects to ensure encapsulation. These vulgarities can lead to inconsistent object states or security vulnerabilities, especially when mutable objects expose sensitive information. Code example: Safe code, creating immutable objects or making safe copies reduces risks associated with mutable states. public class ImmutableExample { private final List items; public ImmutableExample(List items) { this.items = new ArrayList<>(items); // Creates a safe copy } public List getItems() { return Collections.unmodifiableList(items); // Returns an immutable view } } To summarize, sensitive data is information that individuals or organizations want to protect from public exposure as if it is exposed could result in harm to the person or the organization. Factors such as improper handling of sensitive information, vulnerabilities to data injection attacks, the unsafe handling of mutable objects, and insufficient input validation can compromise an application's integrity. However, by adhering to secure coding guidelines such as avoiding the logging of sensitive information, using SQL parameterized queries to prevent injection attacks, validating all inputs, and handling mutable objects correctly, developers can build Java applications that are secure and keep sensitive data protected.  References: Baig, A. (2021, May 17). What is sensitive data? Securiti. https://securiti.ai/blog/what-is-sensitive-data/ Oracle (n.d.). Secure coding guidelines for Java SE. Updated May 2023. Oracle. https://www.oracle.com/java/technologies/javase/seccodeguide.html

  • Recursion: Concepts, Components, and Practical Applications - Java

    This article explains the concept of recursion in programming. It describes its key components: the base case and the recursive case. Using a Java example, it illustrates how recursion is implemented and emphasizes safeguards to prevent infinite loops and stack overflow errors. Alexander S. Ricciardi July 8, 2024 In computer science, understanding the concept of recursion is essential as it is often the base of more complex algorithms, and in programming, it is a tool used to solve problems by breaking them down into smaller, more manageable subproblems. This post explores the components of a recursive method—the base case and the recursive case—using the programming language Java. Recursive Method Explanation A recursive algorithm or method solves complex problems by calling itself and by breaking the problems into smaller, more manageable subproblems. The basic components to create a recursive method are a base case and a recursive case. A base case is a condition that when met stops the recursion, usually in an if statement. A recursive case is a set of code lines or functionalities that are computed 'if' the base case condition is not met, always followed by the recursive method calling itself usually with a modified input. Typically,  the code lines and the recursive call are found in an ' else' statement following the ' if' statement checking if the base condition is met. However, If the ' if' statement contains a ' return' statement, the code lines and the recursive call are found right after the ' if' statement. Note that a recursive method that calls itself with an unmodified input, or a recursive method that does not take an input, will not create an infinitely recursive loop if and only if the base case condition is based on external factors that change independently of the method's input. To avoid creating an infinitely recursive method, the method needs to contain at least one base case that will eventually be reached. Note that a recursive method can have more than one base case. For example, the recursive method can contain a base case that checks a specific condition, and others can act as safeguards. If the first base case condition is never reached, a safeguard such as a counter can limit the number of recursions based on the available computing memory, preventing a stack overflow error. On a side note: the Python programming language has a built-in mechanism that limits the number of recursions a program can perform. If needed, this limit can be modified, either decreased or increased, by using the Python system (sys) library. Here is an example of a recursion method: import java.util.Random; public class AreWeThereYet { private static final Random randomGenerateMiles = new Random(); public static void askAreWeThereYet(int totalMilesDriven, int tripTotalMiles) { // ---- Base case ---- We've arrived! if (totalMilesDriven >= tripTotalMiles) { System.out.println(" We're here! Finally! "); return; } // ---- Recursive case ---- // Miles driven int milesDriven = randomGenerateMiles.nextInt(50) + 1; // Drive 1-50 miles // Keep asking and driving System.out.println("Are we there yet?"); System.out.println(" Not yet, we've traveled " + totalMilesDriven + " miles. "); if (milesDriven + totalMilesDriven >= tripTotalMiles) { milesDriven = tripTotalMiles - totalMilesDriven; } System.out.println(" --- Drives " + milesDriven + " miles --- "); totalMilesDriven += milesDriven; // ---- Recursive call ---- askAreWeThereYet(totalMilesDriven, tripTotalMiles); } public static void main(String[] args) { int tripTotalMiles = 100; // Total trip distance System.out.println(" Trip total miles: " + tripTotalMiles); askAreWeThereYet(0, tripTotalMiles); } } Outputs Trip total miles: 100 Are we there yet? Not yet, we've traveled 0 miles. --- Drives 10 miles --- Are we there yet? Not yet, we've traveled 10 miles. --- Drives 26 miles --- Are we there yet? Not yet, we've traveled 36 miles. --- Drives 17 miles --- Are we there yet? Not yet, we've traveled 53 miles. --- Drives 12 miles --- Are we there yet? Not yet, we've traveled 65 miles. --- Drives 23 miles --- Are we there yet? Not yet, we've traveled 88 miles. --- Drives 12 miles --- We're here! Finally! To summarize, recursion is an elegant and powerful approach to solving complex problems. By defining a base case and a recursive case, developers can create algorithms that effectively manage problem complexity. However, it is important to ensure that recursion stops appropriately to prevent infinite loops or stack overflow errors. The provided Java example, "AreWeThereYet," illustrates these principles in action, showing how recursion can be used dynamically to solve a problem while maintaining clarity and functionality. As we continue to explore programming techniques, recursion remains an invaluable skill that underscores the importance of thoughtful problem decomposition and method design.

  • AI's Expanding Role: Opportunities and Challenges Across Industries

    Artificial Intelligence (AI) is set to transform diverse industries, including unexpected sectors like mining, by enhancing efficiency, precision, and safety through advanced data analysis and automation. However, while AI offers promising benefits, its rapid implementation also raises concerns about job displacement and the need for ethical alignment. Alexander S. Ricciardi November 6, 2024 Artificial Intelligence (AI) is expected to have a considerable impact on all industries, directly or indirectly, as early as the beginning of 2025, when the first AI agents will start to be implemented. Eric Schmidt, former Google CEO, AI consultant for the US military, and one of the most influential figures in AI, stated during a speech at Stanford University, that AI “is going to have an impact on the world at a scale that no one can yet fully comprehend” (Financial Wise, 2024, 3:37). In his speech, he used the word “impact” 11 times. The video was later removed by Stanford University because his comments about Google were deemed too controversial; however, the video is all over the internet. Ultimately, the impact of AI on society could be extremely beneficial or extremely damaging, depending on how it is implemented and for what goals. AI is starting to impact industries that may not seem to be ideal candidates for AI implementations. One of these industries is mining. AI-power systems are being introduced to improve mine sites' efficiency by improving resource estimation accuracy. AI can efficiently examine geological data patterns and incorporate historical mining data resulting in more precise estimates of mineral reserves than traditional methods. This can help the mining companies make more informed decisions regarding investment, production planning, and resource allocation (IMAC, 2025). Additionally, “AI has the potential to streamline mining operations and optimize asset management” (IMAC, 2025, p.1). AI can be integrated into Internet of Things (IoT) devices and sensors allowing real-time data collection. This will enable mining companies to monitor equipment performance, evaluate operational metrics, and identify potential bottlenecks. AI can also analyze the collected data to generate insights and predictions that can improve the companies’ decision-making and prevent unplanned downtime.  Furthermore, AI-power systems can automate mining systems improving efficiency and safety in mining operations (IMAC, 2025). AI systems are getting better at controlling autonomous vehicles and machinery and becoming more efficient than human operators at performing tasks with precision, navigating complex terrains, and optimizing routes. This could minimize errors and risk of accidents; therefore, improving safety. AI can also monitor equipment health, detect anomalies, and schedule maintenance activities improving uptime and extending the lifespan of mining equipment.  However, AI implementation in the mining sector can also have negative effects; particularly the automation of mining systems has the potential to limit employment opportunities and result in layoffs, as it would replace jobs traditionally held by human workers. In other words, the automation of mining operations, from autonomous vehicles to predictive maintenance, from data analyses to robotic drilling and excavation, would inevitably lead to job displacement. This raises troubling and complex questions about AI alignment; that is how AI systems can be developed and implemented to align with societal values and human needs. Thus, it is essential for society to understand those challenges and to ensure that AI implementations are balanced and beneficial not just to companies but to society as a whole.  To summarize, AI continues to evolve and is implemented in industries that may not seem to be ideal candidates, such as mining. This demonstrates that AI has the ability to impact all industries in the near future. Additionally, AI has the potential to be extremely beneficial or extremely damaging depending on how society implements AI and for what goals. Thus, it is crucial to ensure that AI implementations are balanced and beneficial not only for companies but also for society as a whole. The words of Eric Schmidt “AI is going to have an impact on the world at a scale that no one can yet fully comprehend” (Financial Wise, 2024, 3:37), resonate as both a warning and a promising future.  Eric Schmidt Speech at Stanford University References: Financial Wise. (2024, August 18). Eric Schmidt Full Controversial Interview on AI Revolution (Former Google CEO) [Video]. YouTube. https://www.youtube.com/watch?v=mKVFNg3DEng IMARC (2025, October). From changing a tire to uncovering the next lode: How AI is revolutionizing the mining industry | International Mining and Resources Conference (IMARC). IMAC Global. https://imarcglobal.com/news/articles/how-ai-is-revolutionising-the-mining-industry

  • Concurrency in C++: Mitigating Risks

    This article explores concurrency in C++ and explains how multithreading can improve application performance while also introducing potential vulnerabilities if not properly managed. It emphasizes the importance of following SEI CERT C++ Coding Standards to prevent issues like data races, deadlocks, and undefined behavior in concurrent programming. Alexander S. Ricciardi November 7, 2024 In computer science, concurrency or multithreading is a powerful that can be used to improve the performance and responsiveness of applications, is especially beneficial to applications needing to handle large amounts of computation or I/O tasks. Concurrency is an approach that allows multiple threads to be executed concurrently on multi-core processors, reducing the program execution time. C++, starting at the C++ 11 release, provides native support for threads such as ‘std::thread’, as well as concurrency control features such as  ‘std::mutex’, ‘std::lock_guard’, and ‘unique_lock’.  However, concurrency, in C++, also introduces vulnerabilities if not properly managed. An incorrect implementation of concurrency can result in serious issues such as data races, deadlocks, and undefined behavior. Thus, it is imperative to understand how to properly implement and manage threats to avoid potentially serious consequences. This can be done by adhering to best practices such as the one recommended by SEI CERT C++ Coding Standard, Rule 10 Concurrency (CON) (CMU - Software Engineering Institute, n.d.). Below is a list of common concurrency vulnerabilities and the corresponding SEI CERT C++ Coding Standard rules that address them. 1. Data Races:  It occurs when two or more threads access shared data simultaneously, and at least one thread modifies the data. This can lead to inconsistent or unexpected results. Accessing shared data in a safe manner by preventing data races is compliant with the SEI CERT C++ Coding Standard CON51-CPP: "Ensure actively held locks are released on exceptional conditions" (CMU - Software Engineering Institute, n.d.). Example 1: Data Race (CON51-CPP) – Code with vulnerability int shared_counter = 0; void increment_counter() { for (int i = 0; i < 1000; ++i) { ++shared_counter; // Data race here } } void dataRaceExample() { std::thread t1(increment_counter); std::thread t2(increment_counter); t1.join(); t2.join(); std::cout << "Counter: " << shared_counter << std::endl; } Solution example: Data protected with mutex std::mutex mtx; int shared_counter = 0; void increment_counter() { for (int i = 0; i < 1000; ++i) { std::lock_guard lock(mtx); ++shared_counter; } } void dataRaceSolution() { std::thread t1(increment_counter); std::thread t2(increment_counter); t1.join(); t2.join(); std::cout << "Counter: " << shared_counter << std::endl;} 2. Deadlocks It happens when two or more threads are waiting indefinitely for resources held by each other, preventing any of them from proceeding. Locking mutexes in a predefined order to avoid deadlocks is compliant with CON53-CPP: "Avoid deadlock by locking in a predefined order" (CMU - Software Engineering Institute, n.d.). Example 2: Deadlock (CON53-CPP) – Code with vulnerability std::mutex mtx1, mtx2; void thread_func1() { // locks mtx1 waits for mtx2 to be released to lock it std::lock_guard lock1(mtx1); // The sleep function allows the other threat to lock the next mutex     // While this treat is locking the previous mutex std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::lock_guard lock2(mtx2);        std::cout << "Hello from Threat-2\n"; } void thread_func2() { // locks mtx2 waits for mtx1 to be released to lock it std::lock_guard lock2(mtx2);     // The sleep function allows the other threat to lock the next mutex     // While this treat is locking the previous mutex     std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::lock_guard lock1(mtx1);    std::cout << "Hello from Threat-2\n"; } void deadlockExample() { std::thread t1(thread_func1); std::thread t2(thread_func2); t1.join(); t2.join(); } Solution: Lock mutexes in a predefined order std::mutex mtx1, mtx2; void thread_func1() { // locks mtx1 waits for mtx2 to be released to lock it std::lock(mtx1, mtx2); // Locks mutexes in a predefine order     std::lock_guard lock1(mtx1, std::adopt_lock); std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::lock_guard lock2(mtx2, std::adopt_lock); std::cout << "Hello from Threat-1\n"; } void thread_func2() { // locks mtx2 waits for mtx1 to be released to lock it std::lock(mtx2, mtx1); // Locks mutexes in a predefine order     std::lock_guard lock2(mtx2, std::adopt_lock); std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::lock_guard lock1(mtx1, std::adopt_lock); std::cout << "Hello from Threat-2\n"; } void deadlockExample() { std::thread t1(thread_func1); std::thread t2(thread_func2); t1.join(); t2.join(); } 3. Destroying Locked Mutexes Destroying a mutex while it is still locked can lead to undefined behavior and potential resource leaks. Not destroyed mutexes while locked is compliant with CON50-CPP: "Do not destroy a mutex while it is locked" (CMU - Software Engineering Institute, n.d.). Example 3: Destroying locked mutex (CON50-CPP) – Code with vulnerability void mutexDestructionExample() { std::mutex mtx; mtx.lock();     std::cout << "Hello from Threat!";      // Mutex goes out of scope while still locked // causing undefined behavior } Solution: Unlock Mutex Before Destruction void mutexDestructionSolution() { std::mutex mtx; mtx.lock();     std::cout << "Hello from Threat!";     mtx.unlock(); // Making sure that the mutex is unlocked } 4. Incorrect Use of Condition Variables Not wrapping condition variable waits in a loop can lead to spurious wake-ups and incorrect program behavior. Wrapping wait calls in a loop is compliant with CON54-CPP: "Wrap functions that can spuriously wake up in a loop" (CMU - Software Engineering Institute, n.d.). Example 4: Condition variable without loop (CON54-CPP) – Code with vulnerability static std::mutex m;static std::condition_variable condition;bool ready = false;void consume_list_element() {    std::unique_lock lk(m);    Ready = true;   condition.wait(lk /* The condition predicate is not checked */ );   std::cout << "Hello from Threat!"; } Solution: Wrap wait in a loop checking the predicate static std::mutex m; static std::condition_variable condition; bool ready = false; void consume_list_element() { std::unique_lock lk(m); ready = true; while(true){ // The condition predicate is checked          condition.wait(lk, [] { return ready; }); std::cout << "Hello from Threat!"; if (ready) { break; } } } 5. Speculative Locking of Non-recursive Mutexes Do not attend to lock a non-recursive mutex that is already locked by the same thread, this can result in undefined behavior and potential deadlocks. Not trying to lock already locked non-recursive mutexes is compliant with CON56-CPP: "Do not speculatively lock a non-recursive mutex that is already owned by the calling thread" (CMU - Software Engineering Institute, n.d.). Example 5: Speculative locking (CON56-CPP) – Code with vulnerability std::mutex mtx; // Not a recursive mutex void recursiveLock() { mtx.lock(); // Do something if (/* some condition */) { recursiveLock(); // causes deadlock } mtx.unlock(); } Solution: Use recursive mutex std::recursive_mutex mtx; // Is a recursive mutex void recursiveLock() { mtx.lock(); // Do something if (/* some condition */) { recursiveLock(); // no deadlock } mtx.unlock(); } 6. Accessing Adjacent Bit-fields from Multiple Threads Accessing bit fields concurrently can result in data race conditions. When accessing bit-fields preventing data races from multiple threads is compliant with CON52-CPP: "Prevent data races when accessing bit-fields from multiple threads" (CMU - Software Engineering Institute, n.d.). Example 6: Bit-field Data Race (CON52-CPP) – Code with vulnerability struct Flags { unsigned int flag1 : 1; unsigned int flag2 : 1; } flags; void setFlag1() { flags.flag1 = 1; } void setFlag2() { flags.flag2 = 1; } Solution: Use separate storage units or protect with mutex std::mutex mtx1, mtx2; struct Flags { unsigned int flag1; unsigned int flag2; } flags; void setFlag1() { std::lock_guard lock(mtx1); flags.flag1 = 1; } void setFlag2() { std::lock_guard lock(mtx2); flags.flag2 = 1; } 7. Thread Safety and Liveness with Condition Variables When using condition variables is essential to ensure thread safety and preserve liveness. Liveness is the process of the threads making continuous progress and does not end up in situations where they are unable to proceed. For example, being indefinitely blocked or waiting for resources that never will become available. Preserving thread safety and liveness is compliant with CON55-CPP: "Preserve thread safety and liveness when using condition variables" (CMU - Software Engineering Institute, n.d.). Below is a table summary of the risk assessment of the C++ concurrency coding rules from the SEI CERT C++ Coding Standard. Table 1 Concurrency Rules Risk Assessment Note: From "Rule 10. Concurrency (CON). SEI CERT C++ Coding Standard" by CMU - Software Engineering Institute (n.d.). Table Description: •    Rule: The specific rule from the SEI CERT C++ Coding Standard. •    Severity: The potential impact on security. •    Likelihood: The probability of the rule violation leading to a vulnerability. •    Remediation Cost: The effort required to fix the violation. To summarize, in computer science, concurrency is a powerful tool that can be used to improve the performance and responsiveness of applications. However, in C++, concurrency can introduce vulnerabilities if not properly managed, and it may result in serious issues such as data races, deadlocks, and undefined behavior. Thus, it is imperative to adhere to best practices such as the one recommended by SEI CERT C++ Coding Standard. References: CMU - Software Engineering Institute (n.d.)  Rule 10. Rule 10. Concurrency (CON). SEI CERT C++ coding standard. Carnegie Mellon University. Software Engineering Institute. https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=88046460

  • Securing C++ iostream: Key Vulnerabilities and Mitigation Strategies

    The C++ ' iostream ' library offers flexible input and output operations. However, it also introduces potential security vulnerabilities, including buffer overflows, inconsistent formatting, and thread safety issues. By following best practices such as boundary checks, resetting stream states, and implementing thread safety measures, developers can mitigate these risks effectively. Alexander S. Ricciardi October 31, 2024 In C++, the ‘iostrem’ library is an object-oriented library that provides input and output functionality using streams (cpluplus, n.d.). A stream is an abstraction representing a device on which input and output of operations can be performed. In other words, it is a representation of a source or destination of characters with indefinite length (can handle an unknown or variable amount of data). Streams can be many things, for example, they can handle input and output from a disk file, keyboard, console, or even in-memory strings. The diagram below provides an illustration of the C++ I/O classes and how they relate to each other. Figure 1z C++ I/O Classes Note: The diagram shows the hierarchical structure and relationships between different  C++ I/O classes, such as input streams (‘i stream ’), output streams (‘ ostream ’), and file streams (‘ fstream ’), as well as the buffers associated with of them. From “Input/Output” by cpluplus (n.d.).  The ‘ iostrem ’ library brings flexibility on how to manipulate input and output operations. However, it also introduces vulnerabilities, especially when formatting input data is handled improperly. Below is a list of the common formatting vulnerabilities that can emerge when using the ‘iostrem’ library. 1. Buffer overflow and data truncation, this can happen by not having a built-in mechanism that checks boundaries (Snyk Security Research Team, 2022). For example, user input can lead to data truncated or bindery overflow when formatting functions like ‘ std::setw ’ are used improperly. Truncated Code example, what not to do: #include #include #include void printInput(const std::string& input) { // Using std::setw without checking input length std::cout << "Formatted Input: " << std::setw(10) << input << std::endl; } int main() { std::string userInput; std::cout << "Enter a word: "; std::cin >> userInput; // This could cause data truncation if input is longer than 10 characters printInput(userInput); return 0; } 2. Inconsistent data formatting , the I/O streams have formatting states that once set need to be reset whenever manipulating the data with different manipulators (Moria, 2017). For example when using a combination of manipulators like ‘ std::hex ’, ‘ std::oct ’, or ‘ std::dec’ . If the states are not reset, the outputs may retain the format of the previous manipulator, resulting in incorrect data representation. Code example, what not to do: #include #include    void displayValues() { int number = 255; // std::hex, print the number in hexadecimal format std::cout << "Hexadecimal: " << std::hex << number << std::endl; // Print the number again without resetting the manipulator std::cout << "Decimal (Incorrect): " << number << std::endl; // Correct approach: reset to std::dec for decimal output std::cout << "Decimal (Correct): " << std::dec << number << std::endl; } int main() { displayValues(); return 0; } Output Hexadecimal: ff Decimal (Incorrect): ff Decimal (Correct): 255 3. Stream adjustor side-effects , when using manipulators that adjust stream such as width, precision, or fill characters (e.g., ‘std::setprecision’ , ‘ std::setfill ’), if not used properly, can result in unpredictable results. This can affect the subsequent output throughout the program. Code example, what not to do: #include #include void displayNumbers() { double num1 = 3.14159; double num2 = 42; // Set precision and fill for num1 std::cout << "Formatted num1: " << std::setprecision(4) << std::fixed << num1 << std::endl; // no resetting, these settings affect num2 as well std::cout << "Formatted num2 (Incorrect): " << num2 << std::endl; // Correct approach: reset manipulators for num2 std::cout << "Formatted num2 (Correct): " << std::setprecision(0) << num2 << std::endl; } int main() { displayNumbers(); return 0; } Output: Formatted num1: 3.1416 Formatted num2 (Incorrect): 42.0000 Formatted num2 (Correct): 42 4. Thread safety concerns , ‘ std::cout ’ is thread-safe for individual character operations; However concurrent writes from multiple threads can result in interleaved output, see code example below: Code example, what not to do: #include #include void print_message(const std::string& message) { std::cout << message << std::endl; } int main() { std::thread t1(print_message, "Hello from thread 1"); std::thread t2(print_message, "Hello from thread 2"); t1.join(); t2.join(); return 0; } Output: Hello from thread 1Hello from thread 2 A solution is to add a mutex locks #include #include #include std::mutex cout_mutex; void print_message(const std::string& message) { std::lock_guard lock(cout_mutex); std::cout << message << std::endl; } int main() { std::thread t1(print_message, "Hello from thread 1"); std::thread t2(print_message, "Hello from thread 2"); t1.join(); t2.join(); return 0; } Output: Hello from thread 1Hello from thread 2 All the examples above illustrate vulnerabilities that can be exploited by malicious actors if not properly addressed. Below is a list of tips for identifying and mitigating these vulnerabilities: Use tools for static analysis to detect buffer-related issues, tools such as Polyspace, Astrée, Splint, Asan, etc. These tools are very useful in identifying such vulnerabilities, especially when the source code consists of millions of lines. Enforce data type boundaries by integrating built-in boundary checks into the program code and performing tests to validate that the data is handled properly. After modifying stream states with manipulators, reset them or use local copies of streams. Use proper thread safety by using mechanics such as ‘mutex’ locks when accessing shared resources like in multithreaded applications. To summarize, the ‘ iostrem ’ library is an object-oriented library that provides input and output functionality using streams, it provides flexible input/output operations. However, it also introduces vulnerabilities if not managed carefully. Vulnerabilities like buffer overflow, inconsistent formatting, and thread safety concerns. By applying best practices such as resetting stream states, performing boundary checks, and using thread-safety mechanisms, these Vulnerabilities can be mitigated. References: cpluplus (n.d.). Input/Output. Cpluplus. https://cplusplus.com/reference/iolibrary/ Moria (2017, May 13). IOStream Is Hopelessly Broken. Moria. https://www.moria.us/articles/iostream-is-hopelessly-broken/ Snyk Security Research Team (2022, August 16). Top 5 C++ security risks. Snyk. https://snyk.io/blog/top-5-c-security-risks/

  • Creating Java GUIs with Swing Components

    This article explores Java's Swing toolkit. A library for building Graphical User Interfaces (GUIs) using components like JFrame, JDialog, and JApplet, which serve as essential top-level containers. It also demonstrates how to create a simple contact form using Swing components. Alexander S. Ricciardi June 18, 2024 Swing is a Java library used to create simple Graphical User Interfaces (GUIs). It is a GUI widget toolkit providing a collection of components for constructing GUIs. (Oracle Docs, n.d.a). Swing components are written entirely in the Java programming language. There are three generally useful top-level container classes: JFrame, JDialog, and JApplet (Oracle Docs, n.d. b). Top-level container: A JFrame is a top-level window with a title and a border. (Oracle Docs, n.d.b, How to Make Frames (Main Windows). A JDialog window is an independent sub-window that temporaries notice apart from the main Swing Application Window. (Oracle Docs, n.d.b, How to Make Dialogs) “ JApplet , a Java applet is a special kind of Java program that a browser enabled with Java technology can download from the internet and run. An applet is typically embedded inside a web page and runs in the context of a browser. An applet must be a subclass of the applet. Applet class. The Applet class provides the standard interface between the applet and the browser environment” (Oracle Docs, n.d.c). Every program that utilizes Swing components includes at least one top-level container. This top-level container serves as the root of a containment hierarchy, which encompasses all the Swing components within the container (Oracle Docs, n.d.b). Typically, a standalone application with a Swing-based GUI will have at least one containment hierarchy with a JFrame as the root. For instance, if an application features a main window and two dialogs, it will have three containment hierarchies, each with its own top-level container. The main window will have a JFrame as its root, while each dialog will have a JDialog as its root.A Swing-based applet also has at least one containment hierarchy, with one rooted by a JApplet object. For example, an applet that displays a dialog will have two containment hierarchies. The components within the browser window belong to a containment hierarchy rooted by a JApplet object, while the dialog belongs to a containment hierarchy rooted by a JDialog object. JComponent Class: Except for top-level containers, all Swing components that start with "J" are derived from the JComponent class. For instance, JPanel, JScrollPane, JButton, and JTable all inherit from JComponent. However, JFrame and JDialog do not, as they are top-level containers (Oracle Docs, n.d.b, The JComponent Class) Differences between Frame and Panel: Frame: A JFrame is a top-level container that represents a window with a title, borders, and buttons. It is typically used as the main window of an application. A JFrame can contain multiple components, including JPanel , JScrollPane , JButton, JTable , and etc. Panel: A JPanel is a generic container that is used to group a set of components together within a window. It does not have window decorations like a title bar or close button. A JPanel is often used to organize and manage layout within a JFrame . Every program that utilizes Swing components includes at least one top-level container. This top-level container serves as the root of a containment hierarchy, which encompasses all the Swing components within the container (Oracle Docs, n.d.b). Typically, a standalone application with a Swing-based GUI will have at least one containment hierarchy with a JFrame as the root. For instance, if an application features a main window and two dialogs, it will have three containment hierarchies, each with its own top-level container. The main window will have a JFrame as its root, while each dialog will have a JDialog as its root. A Swing-based applet also has at least one containment hierarchy, with one rooted by a JApplet object. For example, an applet that displays a dialog will have two containment hierarchies. The components within the browser window belong to a containment hierarchy rooted by a JApplet object, while the dialog belongs to a containment hierarchy rooted by a JDialog object. The example below includes a JFrame and a JPanel , as well as additional components like buttons, text fields, and labels using a GridBagLayout . Moreover, it also displays a message using JDialog , the JOptionPane component, and a Dialog window component. It is a simple graphical user interface (GUI) contact form using Swing components. / /--- Abstract Window Toolkit (AWT) // Provides layout manager for arranging components in five regions: // north, south, east, west, and center. import java.awt.BorderLayout; // Grid layout - Specifies constraints for components that are laid out using the GridBagLayout. import java.awt.GridBagConstraints; // Grid - layout manager that aligns components vertically and horizontally, // without requiring the components to be of the same size. import java.awt.GridBagLayout; // Gird padding - Specifies the space (padding) between components and their borders. import java.awt.Insets; // Button - Provides the capability to handle action events like button clicks . import java.awt.event.ActionEvent; // Button event - Allows handling of action events, such as button clicks. import java.awt.event.ActionListener; //--- swing GUI // Button - Provides a button component that can trigger actions when clicked. import javax.swing.JButton; // Frame - Provides a window with decorations // such as a title, border, and buttons for closing and minimizing. import javax.swing.JFrame; // Labels - Provides a display area for a short text string or an image, or both. import javax.swing.JLabel; // Submition Message - Provides standard dialog boxes such as message, input, and confirmation dialogs. import javax.swing.JOptionPane; // Panel - Provides a generic container for grouping components together. import javax.swing.JPanel; // Scroll user message - Provides to the a scrollable view of a lightweight component. import javax.swing.JScrollPane; // User message - Provides a multi-line area to display/edit plain text. import javax.swing.JTextArea; // Name & Email - Provides a single-line text field for user input. import javax.swing.JTextField; /** * This class generates a simple contact form. The form includes fields for the * user's name, email, and message, and a submit button to submit the form. * * @author Alejandro Ricciardi * @version 1.0 * @date 06/16/2024 */ public class contactForm { /** * The main method to create and display the contact form. * * @param args Command line arguments */ public static void main(String[] args) { /*------------ | Frame | ------------*/ // ---- Initializes frame // Creates the main application frame JFrame frame = new JFrame("Contact Form"); frame.setSize(400, 300); // Set the size of the frame // Close the application when the frame is closed frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); // Use BorderLayout for the frame /*------------ | Panel | ------------*/ // ---- Initializes panel // Create a panel with GridBagLayout JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gridForm = new GridBagConstraints(); gridForm.insets = new Insets(5, 5, 5, 5); // Add padding around components // ---- Creates and adds grid components to the panel // -- Name // Adds "Name" label JLabel nameLabel = new JLabel("Name:"); gridForm.gridx = 0; // Position at column 0 gridForm.gridy = 0; // Position at row 0 panel.add(nameLabel, gridForm); // Add text field for name input JTextField nameField = new JTextField(20); gridForm.gridx = 1; // Position at column 1 gridForm.gridy = 0; // Position at row 0 panel.add(nameField, gridForm); // -- Email // Add "Email" label JLabel emailLabel = new JLabel("Email:"); gridForm.gridx = 0; // Position at column 0 gridForm.gridy = 1; // Position at row 1 panel.add(emailLabel, gridForm); // Adds text field for email input JTextField emailField = new JTextField(20); gridForm.gridx = 1; // Position at column 1 gridForm.gridy = 1; // Position at row 1 panel.add(emailField, gridForm); // Adds "Message" label JLabel messageLabel = new JLabel("Message:"); gridForm.gridx = 0; // Position at column 0 gridForm.gridy = 2; // Position at row 2 panel.add(messageLabel, gridForm); // -- Message // Adds text area for message input with a scroll pane JTextArea messageArea = new JTextArea(5, 20); JScrollPane scrollPane = new JScrollPane(messageArea); gridForm.gridx = 1; // Position at column 1 gridForm.gridy = 2; // Position at row 2 panel.add(scrollPane, gridForm); // Adds "Submit" button JButton submitButton = new JButton("Submit"); gridForm.gridx = 1; // Position at column 1 gridForm.gridy = 3; // Position at row 3 panel.add(submitButton, gridForm); // Adds the panel to the frame's center frame.add(panel, BorderLayout.CENTER); // Make the frame visible frame.setVisible(true); /*------------ | JDialog | ------------*/ // Add action listener to the submit button ActionListener submitBtnClicked = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // Display a message dialog when the submit button is clicked JOptionPane.showMessageDialog(frame, "Message was sent!"); } }; submitButton.addActionListener(submitBtnClicked); } } If you are interested to learn more about dialogue windows, the following video describes how to implement a JDialog  JOptionPane message: To summarize, Java's Swing toolkit offers a set of components that enable developers to create user-friendly, visually structured GUIs. The library uses top-level containers like JFrame, JDialog, and JApplet, alongside essential elements like JPanel and JOptionPane. References: Oracle Docs. (n.d.a). Swing . Oracle. https://docs.oracle.com/javase/8/docs/technotes/guides/swing/ Oracle Docs. (n.d.b). Using Top-Level Containers . The Java™ Tutorials. Oracle. https://docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html Oracle Docs. (n.d.c). Java Applets . The Java™ Tutorials. Oracle. https://docs.oracle.com/javase/tutorial/deployment/applet/index.html

  • AI and Machine Learning Algorithms: Strengths, Weaknesses and Best Use Cases

    This article provides an overview of AI and Machine Learning algorithms. It lists their strengths, weaknesses, and applications across various fields. It also emphasizes the importance of selecting appropriate algorithms based on specific application needs. Alexander S. Ricciardi October 22, 2024 Machine Learning (ML) is an application of Artificial Intelligence (AI). Microsoft defines AI as “the capability of a computer system to mimic human cognitive functions such as learning and problem-solving. Through AI, a computer system uses math and logic to simulate the reasoning that people use to learn from new information and make decisions” (Microsoft, n.d.).  ML can be defined as an application of AI. "It’s the process of using mathematical models of data to help a computer learn without direct instruction. This enables a computer system to continue learning and improving on its own, based on experience” (Microsoft, n.d.). AI and ML algorithms are used to solve complex problems across a large variety of industries. Each algorithm is better suited for certain tasks than others, each with its strengths and weaknesses. Thus, selecting the appropriate algorithm that meets the specific needs of an application is essential. This post explores the different types of AI and ML strengths, weaknesses, and the specific applications for which they are best suited to solve problems. The field of AI utilizes a substantial number of algorithms. The table below lists some of them with their descriptions:  Table 1 AI Algorithms   Note: Data adapted from multiple sources: (Baheti, 2024; Baluja, 2024; Biswal, 2024; Boesch, 2024; Lui et al., 2018; Kerner, 2024; Tableau, n.d.; & Vargas et al., 2023) The algorithms in Table 1 can be further categorized into seven different categories as follows: Supervised Learning Algorithms (ML-specific) :  Regression, Logistic Regression, Decision Trees, Random Forest, and Support Vector Machines (SVM).  “Supervised Learning is a technique that is widely used in various fields such as finance, healthcare, marketing, and more. In supervised learning, the supervised learning algorithm learns a mapping between the input features and output labels” (Gupta, 2024)  Unsupervised Learning Algorithms (ML-specific) :  K-Means Clustering, Hierarchical Clustering, and Principal Component Analysis (PCA).  “Unsupervised learning is a type of machine learning in which models are trained using unlabeled dataset and are allowed to act on that data without any supervision” (JavaPoints, n.d.) Neural Networks and Deep Learning (ML and AI) :  Convolutional Neural Networks (CNNs), Recurrent Neural Networks (RNNs), and Generative Adversarial Networks (GANs). “Neural Networks are one particular type of Machine Learning technique. They are a type of artificial intelligence modeled on the brain. There are nodes or artificial neurons that are each responsible for a simple computation. These nodes are networked together with connections of varying strengths, and learning is reflected in changes to those connections. An important characteristic of neural networks is the relationship between nodes. Often, there is an input layer, an output layer, and one or more in between layers (called “hidden layers”), which can result in a model that has a lot of complexity, but may be difficult to interpret” (Network of the National Library of Medicine, n.d.)  Reinforcement Learning (AI-specific):   Q-Learning, Deep Q-Networks (DQN), and Proximal Policy Optimization (PPO). “Reinforcement learning (RL) is a type of machine learning process that focuses on decision making by autonomous agents. An autonomous agent is any system that can make decisions and act in response to its environment independent of direct instruction by a human user. Robots and self-driving cars are examples of autonomous agents. In reinforcement learning, an autonomous agent learns to perform a task by trial and error in the absence of any guidance from a human user. It particularly addresses sequential decision-making problems in uncertain environments, and shows promise in artificial intelligence development” (Murel, & Kavlakoglu, 2024) Optimization Algorithms (Used in ML and AI) :  Gradient Descent, Adam, and Simulated Annealing. “Optimization algorithms are a class of algorithms that are used to find the best possible solution to a given problem. The goal of an optimization algorithm is to find the optimal solution that minimizes or maximizes a given objective function” (Complexica, n.d.). Probabilistic and Evolutionary Models (ML-specific and used in AI):   Bayesian Networks, Markov Chain Monte Carlo (MCMC), and Evolutionary Algorithms (Genetic Algorithms). “Probabilistic models are an essential component of machine learning, which aims to learn patterns from data and make predictions on new, unseen data. They are statistical models that capture the inherent uncertainty in data and incorporate it into their predictions” (Visha, 2023) Other:   Autoencoders, Transfer Learning, t-SNE, and Fuzzy Logic Algorithms. They are used for specialized tasks. Tasks such as feature extraction, knowledge transfer, dimensionality reduction, and reasoning under uncertainty. AI algorithms have their strengths and weaknesses, which make them suitable and efficient for certain tasks and less suitable and efficient for others.  The table below lists the strengths and weaknesses of different AI-ML algorithms, as well as, the applications for which they are best suited. Table-2 AI-ML Algorithms Strengths, Weaknesses, and Applications Note: Data adapted from “A Review of Artificial Intelligence Algorithms Used for Smart Machine Tools” by Lui et al. (2018). As illustrated in Table 2, different AI Algorithms are better suited for specific tasks generally based on their accuracy, efficiency, scalability, and robustness. For example, Long Short-Term Memory (LSTMs) algorithms are well suited for tasks that require high accuracy over a period of time, such as time-series forecasting, because they can retain long-term dependencies in memory. Extreme Learning Machines (ELMs) and Echo State Networks (ESNs) suitable for real-time or lower-resource environments (efficiency), because they are faster to train and require less computational power for inference; however, they are less precise than other algorithms. Convolutional Neural Networks (CNNs) are well suited for image classification tasks and large datasets, due to their ability to learn patterns making them robust and highly scalable algorithms.  Thus, when selecting an AI algorithm for an application, it is essential to understand the strengths and weaknesses of the algorithm, as well as how well it meets the specific needs of the application. For example, if I have to choose a method, CNNs are well suited for image analysis applications such as detecting tumors from MRI scans; because they can learn and extract patterns from images. However, when trained, they require large datasets and significant computational resources when inferencing. Therefore, when using CNNs for this application, it is important to ensure that the application has access to sufficient data and computational power. References: Baheti, P. (2024, July 2). The Essential Guide to Neural Network architectures. V7. https://www.v7labs.com/blog/neural-network-architectures-guide Baluja, H. (2024, October 7). The Complete Guide to AI Algorithms. Hubspot. https://blog.hubspot.com/marketing/ai-algorithms Biswal, A. (2024, July 16). Top 10 Deep Learning Algorithms You Should Know in 2024. Simplilearn.com . https://www.simplilearn.com/tutorials/deep-learning-tutorial/deep-learning-algorithm Boesch, G. (2024, August 19). Deep Neural Network: The 3 Popular Types (MLP, CNN and RNN). viso.ai . https://viso.ai/deep-learning/deep-neural-network-three-popular-types/ Complexica (n.d.). Optimization Algorithms. Complexica Glossary. https://www.complexica.com/narrow-ai-glossary/optimization-algorithms Gupta, M. (2024, October 16). Supervised machine learning. GeeksforGeeks. https://www.geeksforgeeks.org/supervised-machine-learning/ Javatpoint (n.d.). Unsupervised Machine learning. Javatpoint. www.javatpoint.com . https://www.javatpoint.com/unsupervised-machine-learning Kerner, S. M. (2024, October 16). Types of AI algorithms and how they work. Enterprise AI. https://www.techtarget.com/searchenterpriseai/tip/Types-of-AI-algorithms-and-how-they-work Tavasoli, S. (2024, October 10). 10 Types of Machine Learning Algorithms and Models. Simplilearn.com . https://www.simplilearn.com/10-algorithms-machine-learning-engineers-need-to-know-article Liu, C.-H, Chang, C.-W.,&  Lee, H.-W. (2018). A review of artificial intelligence algorithms used for smart machine tools. In Prof. Dr. Chien-Hung Liu (Ed.), Inventions [Journal-article]. https://doi.org/10.3390/inventions3030041 Microsoft (n.d.). The difference between AI and machine learning. Microsoft Azure. https://azure.microsoft.com/en-us/resources/cloud-computing-dictionary/artificial-intelligence-vs-machine-learning Murel, J., & Kavlakoglu, E. (2024, August 30). Reinforcement Learning. What is reinforcement learning? IBM. https://www.ibm.com/topics/reinforcement-learning Network of the National Library of Medicine (n.d.). Neural Networks. NNLM. https://www.nnlm.gov/guides/data-glossary/neural-networks Tableau (n.d.). Artificial intelligence (AI) algorithms: a complete overview. Salesforce. https://www.tableau.com/data-insights/ai/algorithms Vargas, R., Jiang, N., & Sircar, S. (2023, December 1). What makes AI algorithms different from traditional computer algorithms? https://www.linkedin.com/advice/3/what-makes-ai-algorithms-different-from-omp7f Visha (2023, May 29). Probabilistic Models in Machine Learning. GeeksforGeeks. https://www.geeksforgeeks.org/probabilistic-models-in-machine-learning/

  • Pointers in C++: Memory Management, Arrays, and Smart Pointers

    This article explores the role of pointers in C++, their role in memory management, how they interact with arrays, how to utilize pointer arithmetic, and differ from arrays in handling memory allocation and access. It also highlights the use of smart pointers like unique_ptr for safer memory handling, reducing risks associated with raw pointers. Alexander S. Ricciardi October 24, 2024 The C++ programming language provides memory management through pointers. Pointers offer flexibility and precise control over memory, making them a powerful tool for applications where memory usage management and manipulation are needed. This post discusses how pointers interact with arrays, whether arrays are necessary when using pointers, how pointer arithmetic relates to arrays and the differences between the address-of operator (&) and the dereference operator (*). Additionally, the post refers to Unique pointers. Pointers and Arrays: How They Work Together In C++, pointers and arrays work well together, but arrays are not necessarily required when using pointers and vice versa. However, they are often used together due to their similar nature, as they use direct memory access; that is arrays use indexing to access elements, while pointers use memory addresses to access elements. Both are derived data types in C++ that have a lot in common (Rinkalktailor, 2024); a derived data type is a data type that is created or derived from the fundamental or built-in data types. In some cases, pointers can even replace arrays. However, if they are very closely related they are still different data types. Below is a table listing the similarities and differences between the two data types.  Table 1 Arrays vs Pointers Note: From “Pointers vs Array in C++” by Rinkalktailor (2024), modify. As mentioned earlier arrays are not strictly necessary when using pointers, and vice versa. Below are three examples that illustrate the interaction between arrays and pointers, pointer arithmetic, and how pointers can be used to access array elements, as well as an example of how to use a pointer as an array. Example 1: Accessing Array Elements Using Indexing int arr[5] = {1, 2, 3, 4, 5}; for (int i = 0; i < 5; ++i) { std::cout << arr[i] << " "; // Uses a variable as an index to access // element in the array } // Outputs 1 2 3 4 5 This is the typical method used to access elements in arrays. Example 2: Accessing Array Elements Using Pointer Arithmetic int arr[5] = {1, 2, 3, 4, 5}; int *ptr = arr; // arr acts like a pointer to the first element for (int i = 0; i < 5; ++i) { std::cout << *(ptr + i) << " "; // Dereferencing the pointer to access array // elements } // Outputs 1 2 3 4 5 This method uses pointer arithmetic ‘(*(ptr + i)) ’ to access the element in the array. Pointer arithmetic allows incrementing (++) and decrementing (--) pointers, or adding/subtracting integers to pointers to access elements in the array (Yashk, 2023). See this post section on Pointer Arithmetic for more information. In essence, in this example, the pointer is used as an array. Example 3: Pointer Arithmetic to Traverse an Array int arr[5] = {1, 2, 3, 4, 5}; int *ptr = arr; std::cout << *ptr << " "; // Outputs 1 ptr++; std::cout << *ptr << " "; // Outputs 2 ptr += 2; std::cout << *ptr << " "; // Outputs 4 This example is similar to the previous one, it also used the pointer is used as an array and pointer arithmetic. Note that, the code line ‘i nt *ptr = arr; ’ the pointer ‘ ptr ’ points to the first element of the array. In C++, when assigning an array to a pointer, the pointer points to the first element of that array. Pointers and arrays in C++ work well together. While similar, they diverge in how they handle memory allocation and how elements are accessed. Additionally, pointers can be directly manipulated using pointer arithmetic. Pointer Arithmetic In C++, pointer arithmetic is a powerful tool for memory management. "Pointer arithmetic refers to the operations that are valid to perform on pointers" (Sagar, 2024, p.1). Below is a list of valid pointer arithmetic operations: 1- Incrementing and Decrementing Pointers When a pointer is incremented ‘ptr++’, it points to the value of the derived data type which is in the next memory location. For instance, if ‘ ptr ’ is an ‘ int* ’, incrementing it moves it to the next integer in memory, which is typically 4 bytes ahead, as integers usually have a 4-byte size. Similarly, decrementing a pointer ‘ ptr-- ’ moves it to the previous element in memory. int arr[3] = {10, 20, 30}; int* ptr = arr; ptr++; // Now points to arr[1] (20) std::cout << *ptr << std::endl; // Outputs 20 ptr--; // Now points back to arr[0] (10) std::cout << *ptr << std::endl; // Outputs 10 2- Addition of Constant to Pointers For example, if adding the constant ‘ 2 ’ to a pointer, it will move forward the pointer by ‘ 2 * size of int bytes ’ int arr[5] = {10, 20, 30, 40, 50}; int* ptr = arr; ptr = ptr + 2; // Moves the pointer to arr[2] (30) std::cout << *ptr << std::endl; // Outputs 30 3- Subtraction of Constant from Pointers Similar to adding a constant to a pointer, For example, if subtracting constant ‘ 2 ’ to a pointer, it will move backward the pointer by ‘ 2 * size of int bytes ’ int arr[5] = {10, 20, 30, 40, 50}; int* ptr = &arr[4]; // Points to arr[4] (50) ptr = ptr - 3; // Moves back 3 elements, now pointing to arr[1] (20) std::cout << *ptr << std::endl; // Outputs 20 4- Subtraction of Two Pointers of the Same Type Subtracting of the same type will find the number of elements between them.  For example: int arr[5] = {10, 20, 30, 40, 50}; int* ptr1 = &arr[1]; // Points to arr[1] (20) int* ptr2 = &arr[4]; // Points to arr[4] (50) int difference = ptr2 - ptr1; // Difference in elements std::cout << "Difference: " << difference << std::endl; // Outputs 3 5- Comparison of Pointers Tow pointers can be compared using relational operators ( >, <, >=, <=, ==, != ). This can tell if the two pointers are pointing to the same memory location or not.  For example: int num1 = 10, num2 = 20; int* ptr1 = &num1; int* ptr2 = &num2; if (ptr1 != ptr2) { std::cout << "The pointers point to different locations." << std::endl; } else { std::cout << "The pointers point to the same location." << std::endl; } // The pointers point to different locations Pointer arithmetic is powerful for manipulating and comparing pointers and the value they point to. This is possible through the use of the address-of and dereference operators (*) and (&) as shown in the prior example. Address-of and Dereference Operators (*) and (&) Understanding how pointers handle addresses and value stores in those addresses is crucial for coding without generating errors. At the center of the pointer’s functionality are the address-of operator (&) and the indirection operator/Dereference operator (*). The diagram below shows the makeup of a pointer in C++. Figure 1 Pointer Note: From “C++ Pointer Operators” by Raj (2022).  - The address-of operator (&) is a unary operator. It returns the memory address of the variable. For example: int x = 10; int *ptr = &x; // ptr now holds the address of x - The dereference operator (*) is a unary operator. It returns the value of the variable present at the given address.For example: std::cout << *ptr; // Outputs the value of x (10) *ptr = 20; // Modifies the value of x through the pointer std::cout << x; // Outputs the updated value of x (20) The address-of and dereference operators allow developers to manage and manipulate memory addresses and the value stored in them through the pointer functionality. However, pointers can create significant safety risks if used improperly, they can cause undefined behavior, crashes, or security vulnerabilities such as buffer overflows. A good practice is to use smart pointers, called unique pointers in C++, rather than raw pointers, to ensure safer memory management. Smart Pointers Smart pointers in C++ are also referred to as unique pointers (‘unique_ptr’). They automatically manage the dynamically allocated resources on the heap. They are just wrappers around regular raw pointers preventing errors such as forgetting to delete a pointer and causing a memory leak or accidentally deleting a pointer twice or in the wrong way (Pritamauddy, 2023). Additionally, a ‘unique_ptr’ does not share its raw pointer, meaning that it cannot be copied to another ‘unique_ptr’ (Whitney, 2021). Instead, it can only moved. This means that the ownership of the memory address of the values stored can be transferred from one ‘unique_ptr’ to another. For example: #include #include   // std::unique_ptr #include // std::move int main() { std::unique_ptr ptr1 = std::make_unique(10); std::cout << "Value pointed by ptr1: " << *ptr1 << std::endl; // Moving ownership from ptr1 to ptr2 std::unique_ptr ptr2 = std::move(ptr1); // ptr1 is now empty if (!ptr1) { std::cout << "ptr1 is now empty."<< std::endl; } // ptr2 now owns the address std::cout << "Value pointed by ptr2: " << *ptr2 << std::endl; // No need to delete manually; unique_ptr automatically deletes the // resource when // it goes out of scope return 0; } As shown in the code example the ‘ unique_ptr ’ is safer to use than a raw pointer, it automatically manages access to the heap, protecting it from errors by wrapping around (managing) a raw pointer. To summarize, pointers offer flexibility and precise control over memory, making them a powerful tool for applications where memory usage management and manipulation are needed. Pointers and arrays work well together; arrays are not necessarily required when using pointers and vice versa. Nonetheless, they are similar as both are derived data types and use direct memory access. However, they handle memory allocation and how elements are accessed differently. Pointers and the values they point to can be manipulated using pointer arithmetic and the operators (*) and (&). This comes with risks that can be mitigated by using the smart pointers (‘ unique_ptr ’), which manage access to the heap, protecting it from errors. References: Pritamauddy (2023, October 5). Unique_ptr in C++. GeeksforGeeks. https://www.geeksforgeeks.org/unique_ptr-in-cpp/ Raj (2022, December 20). C++ Pointer operators. GeeksforGeeks. https://www.geeksforgeeks.org/cpp-pointer-operators/ Rinkalktailor (2024, May 15). Pointers vs array in C++. GeeksforGeeks. https://www.geeksforgeeks.org/pointers-vs-array-in-cpp/ Sagar (2024, May 1). C++ Pointer arithmetic. GeeksforGeeks. https://www.geeksforgeeks.org/cpp-pointer-arithmetic/?ref=oin_asr26 Whitney, T., Junshen, T., Sharkey, K., Voss, J., Jones, M., Blome, M., Hogenson, G., & Cai, S. (2021, November 12). How to: Create and use unique_ptr instances. Microsoft Learn. https://learn.microsoft.com/en-us/cpp/cpp/how-to-create-and-use-unique-ptr-instances?view=msvc-170&viewFallbackFrom=vs-2019 Yashk. (2023, January 16). C++ Program to access elements of an array using pointer. GeeksforGeeks. https://www.geeksforgeeks.org/cpp-program-to-access-elements-of-an-array-using-pointer/

  • Inheritance and Polymorphism in Java: Using Superclasses and Subclasses

    This article explains how Java’s inheritance has an “is-a” relationship between superclasses and subclasses, allowing subclasses to inherit and customize superclass functionality. By using polymorphism, subclasses can define unique behaviors, allowing code reuse and flexibility in object-oriented programming. Alexander S. Ricciardi June 10, 2024 In Java, the relationship between super-classes (parent class) and subclasses (child class or derived class) in inheritance is a is-a relationship implying that the subclass is a specialized version of the superclass inheriting the functionality (restrictions can be applied) of a class that it is derived from (CSU Global, n.d). In other words, if class B inherits from class A, then class B "is a" type of class A. This relationship allows class B to use all the functionalities (restrictions can be applied) provided by class A, while also adding its own specific functionalities or/and by overriding some or all of the functionalities of class A. The ability of the child class to override functionality is a form of polymorphism. “The dictionary definition of  polymorphism  refers to a principle in biology in which an organism or species can have many different forms or stages. This principle can also be applied to object-oriented programming and languages like the Java language. Subclasses of a class can define their own unique behaviors and yet share some of the same functionality of the parent class” (The Java™ Tutorials, n.d.)This is especially beneficial when dealing with multiple objects from different subclasses that share a common superclass type. For example: dogs, cats, and owls are animals: Superclass public class Animal { public void makeSound() { System.out.println("Makes a Sound"); } } Subclass of Animals public class Domesticated extends Animal { public void friendly() { System.out.println("This animal is friendly."); } } Subclass of Animals public class Undomesticated extends Animal { public void notFriendly() { System.out.println("This animal is not friendly."); }} Subclass of Domesticated public class Undomesticated extends Animal { public void notFriendly() { System.out.println("This animal is not friendly."); } } Subclass of Domesticated public class Cat extends Domesticated { @Override public void makeSound() { System.out.println("Meow"); } } Subclass of Undomesticated public class Owl extends Undomesticated { @Override public void makeSound() { System.out.println("Hoots"); } } Main class to output the result public class inheritanceExample { public static void main(String[] args) { Dog myDog = new Dog(); Cat myCat = new Cat(); Owl redOwl = new Owl(); System.out.println("MY Dog:"); myDog.makeSound(); // Outputs: Bark myDog.friendly(); // Outputs: This animal is friendly. System.out.println(); System.out.println("My Cat:"); myCat.makeSound(); // Outputs: Meow myCat.friendly(); // Outputs: This animal is friendly. System.out.println(); System.out.println("Red Owl:"); redOwl.makeSound(); // Outputs: Hoot redOwl.notFriendly(); // Outputs: This animal is not friendly. } } Note: The makeSound() methods in the Dog and Cat classes override the makeSound() method in the Animal class. Additionally, the Dog class is a subclass of the Domesticated class which is a subclass of the Animal class. Child of   In Java, a subclass can only have one superclass, for example, the Dog class cannot have a superclass Domesticated and a superclass Animal , the following is not allowed. In other words, a subclass can have one parent class, one grandparent class, one great-grandparent class, … and they are not allowed to have multiple parents, grandparents, or great-grandparents. In conclusion, Java's inheritance allows subclasses to utilize and extend the functionality of superclasses, embodying the "is-a" relationship and facilitating polymorphism. This enhances code reusability, flexibility, and consistency by enabling specific behaviors in subclasses while maintaining shared characteristics across a common superclass. References: CUS Global (n.d.). Module 1: Working with inheritance [Interactive lecture]. In Colorado State University Global, CSC372: Programming II, Computer Science Department. Canvas. Retrieved June 8, 2024, from https://csuglobal.instructure.com/courses/94948/pages/module-1-overview?module_item_id=4868813 The Java™ Tutorials (n.d.). Learning the Java language: Interfaces and inheritance . Oracle. Retrieved June 8, 2024, fromhttps:// docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html

bottom of page