Blog

  • Java Strings

    Strings are the backbone of many Java applications, used for everything from logging to data processing. However, Java’s String class is immutable, meaning every concatenation with the + operator creates a new object, potentially leading to performance bottlenecks. Have you ever noticed your application slowing down when handling large strings? In this post, we’ll compare three ways to concatenate strings—using the + operator, StringBuilder, and StringBuffer—and measure their impact on time and memory. By the end, you’ll know how to optimise string operations for low-latency, high-throughput systems. Let’s dive in

    Lets create Strings class with 3 static methods

    • concatenateBasic
    • concatenateStringBuilder
    • concatenateStringBuffer
    public class Strings {
        public static void concatenateBasic(int iterations) {
            String result = "";
            for (int i = 0; i < iterations; i++) {
                result = result + "word ";
            }
        }
    
        public static void concatenateStringBuilder(int iterations) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < iterations; i++) {
                sb.append("word ");
            }
            String result = sb.toString();
        }
    
        public static void concatenateStringBuffer(int iterations) {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < iterations; i++) {
                sb.append("word ");
            }
            String result = sb.toString();
        }
    }

    All three methods concatenate a string by appending ‘word ’ for a specified number of iterations.

    The difference is minimal when this is done with a small number of iterations. But as the count of iterations grows, both the memory & time required to do the same functionality grows exponentially with the ‘+’ operator. Below is a sample code to test this

    public void testStringConcatenation() throws InterruptedException {
        // Get runtime
        Runtime runtime = Runtime.getRuntime();
        long startMemory, endMemory, startTime, endTime, duration, memoryUsed;
        for (int i = 10; i <= 1000000; i = i * 10) {
            System.out.println("With iterations: " + i);
            runtime.gc();
            startMemory = runtime.totalMemory() - runtime.freeMemory();
            startTime = System.nanoTime();
            Strings.concatenateBasic(i);
            endTime = System.nanoTime();
            endMemory = runtime.totalMemory() - runtime.freeMemory();
            duration = (endTime - startTime) / 1_000_000; // Convert to milliseconds
            memoryUsed = (endMemory - startMemory) / 1024; // in KB
            System.out.println("Time taken using '+': " + duration + " ms, Memory used: " + memoryUsed + " KB");
    
            runtime.gc();
            startMemory = runtime.totalMemory() - runtime.freeMemory();
            startTime = System.nanoTime();
            Strings.concatenateStringBuilder(i);
            endTime = System.nanoTime();
            endMemory = runtime.totalMemory() - runtime.freeMemory();
            duration = (endTime - startTime) / 1_000_000; // Convert to milliseconds
            memoryUsed = (endMemory - startMemory) / 1024; // in KB
            System.out.println("Time taken using StringBuilder: " + duration + " ms, Memory used: " + memoryUsed + " KB");
    
            runtime.gc();
            startMemory = runtime.totalMemory() - runtime.freeMemory();
            startTime = System.nanoTime();
            Strings.concatenateStringBuffer(i);
            endTime = System.nanoTime();
            endMemory = runtime.totalMemory() - runtime.freeMemory();
            duration = (endTime - startTime) / 1_000_000; // Convert to milliseconds
            memoryUsed = (endMemory - startMemory) / 1024; // in KB
            System.out.println("Time taken using StringBuffer: " + duration + " ms, Memory used: " + memoryUsed + " KB");
    
            Thread.sleep(1000); // Sleep for 1 second between iterations
        }
    }

    The results are as below.

    Iterations‘+’ Time (ms)‘+’ Memory (KB)StringBuilder Time (ms)StringBuilder Memory (KB)StringBuffer Time (ms)StringBuffer Memory (KB)
    1029010163022
    1000580906
    10003769028027
    10000673171010240199
    10000034522562882208831668
    10000004141057383207267072416426

    Note: Runtime.gc() is used to hint at garbage collection, but results may vary depending on the JVM’s behaviour.

    As you can see, while the initial difference is negligible, the performance of the + operator degrades dramatically as the number of concatenations grows, leading to significant increases in both execution time and memory consumption.

    For 1M iterations, StringBuilder is up to 59,157 times faster. StringBuffer is slightly slower than StringBuilder as it uses synchronized (Thread safe) methods.

    Why This Matters

    The performance differences highlighted above might seem trivial for a small number of string concatenations.

    Examples

    1. Imagine a high-throughput web server handling thousands of requests per second. Each request generates a log entry with details like the timestamp, user ID, and endpoint. Using the + operator to build log messages, such as

    log = timestamp + " " + userId + " " + endpoint

    creates multiple String objects per log entry. Use of StringBuilder will significantly improve the performance

    2. In a data processing pipeline, such as one generating CSV reports from a database, you might concatenate fields like

    row = id + "," + name + "," + value // for each record

    For a dataset with millions of rows, using + in a loop results in quadratic time complexity, causing delays in report generation.

    Low-Latency and High-Throughput Systems

    In low-latency systems like financial trading platforms, every millisecond counts. Concatenating strings to format trade messages using + can introduce unacceptable delays due to object creation. Similarly, high-throughput systems, such as streaming data processors, handle massive data volumes. Inefficient string operations can bottleneck these systems, reducing throughput. By using StringBuilder (or StringBuffer in thread-safe contexts), developers ensure these systems remain responsive and scalable, meeting stringent performance requirements.

    Conclusion

    Choosing the right string concatenation method can significantly impact your Java application’s performance. For single-threaded applications, StringBuilder is the go-to choice for its speed and efficiency. Use StringBuffer in multi-threaded environments requiring thread safety. Avoid + in loops to prevent performance degradation. Try running the test code yourself and share your results in the comments!

    The code is available at https://github.com/dcurioustech/java-samples/blob/master/java-samples/src/main/java/com/dcurioustech/strings/Strings.java Tests – https://github.com/dcurioustech/java-samples/blob/master/java-samples/src/test/java/com/dcurioustech/strings/StringsTest.java

    #Java #StringConcatenation #Performance

  • Comparison of Gen AI providers

    Generative AI agents are transforming how we interact with technology, offering powerful tools for creativity, productivity, and research. Let us explore the free tier offerings of four leading AI agents – ChatGPT, Google Gemini, Grok, and Claude – highlighting their core features and recent updates available to users without a paid subscription.

    ChatGPT (OpenAI)

    What It Offers: ChatGPT, powered by the GPT-4o model, is a versatile conversational AI accessible for free with a registered account. It excels in tasks like casual conversation, creative writing, coding assistance, and answering complex questions. Its clean interface and conversational memory (when enabled) allow for personalized, context-aware interactions, making it ideal for writers, students, and casual users. The free tier supports text generation, basic reasoning, and limited image description capabilities.

    Recent Updates: As of April 2025, free users can access GPT-4o, which offers improved speed and reasoning compared to GPT-3.5. However, usage is capped at approximately 15 messages every three hours, reverting to GPT-3.5 during peak times or after limits are reached. OpenAI has also introduced limited access to “Operators,” AI agents that can perform tasks like booking or shopping, though these are more restricted in the free tier.

    Why It Stands Out: ChatGPT’s user-friendly design and broad task versatility make it a go-to for general-purpose AI needs, with a proven track record of refinement based on millions of users’ feedback.

    Google Gemini

    What It Offers: Gemini, Google’s multimodal AI, is deeply integrated with Google’s ecosystem (Search, Gmail, Docs) and shines in real-time web access, research, and creative tasks. The free tier, capped at around 500 interactions per month, supports text generation, image analysis, and basic image generation via Imagen 3. Gemini’s ability to provide multiple response drafts and its conversational tone make it great for brainstorming and research.

    Recent Updates: In March 2025, Google made Gemini 2.5 Pro experimental available to free users, boosting performance in reasoning and coding tasks. The Deep Research feature, offering comprehensive, citation-rich reports, is now free with a limit of 10 queries per month. Additionally, free users can create limited “Gems” (custom AI personas) for tasks like fitness coaching or resume editing, enhancing personalisation.

    Why It Stands Out: Gemini’s seamless Google integration and free access to advanced features like Deep Research give it an edge for users already in the Google ecosystem or those needing robust research tools.

    Grok (xAI)

    What It Offers: Grok, developed by xAI, is designed for witty, less-filtered conversations and integrates with the X platform for real-time insights. The free tier, available temporarily as of February 2025, supports text generation, image analysis, and basic image generation. Grok’s “workspaces” feature allows users to organize thoughts, share related material, and collaborate, making it ideal for dynamic, social-media-driven workflows.

    Recent Updates: Launched on February 18, 2025, Grok 3 has shown strong performance in benchmarks, excelling in reasoning, coding, and creative writing. The recent introduction of Grok Studio (April 2025) enables free users to generate websites, papers, and games with real-time editing, similar to OpenAI’s Canvas. Integration with Google Drive further enhances its utility for collaborative projects.

    Why It Stands Out: Grok’s workspaces and Studio features offer a unique, interactive approach to organising and creating content, appealing to users who value humour and real-time social context.

    Claude (Anthropic)

    What It Offers: Claude, powered by Claude 3.5 Sonnet, is a text-focused AI emphasizing ethical responses and strong contextual understanding. The free tier supports basic text generation, long-document processing (up to 100K tokens), and image analysis (up to 5 images per prompt). Its “Projects” space, similar to Grok’s workspaces, allows users to organize documents and prompts for focused tasks, making it suitable for researchers and writers.

    Recent Updates: In late 2024, Claude added vision capabilities to its free tier, enabling image analysis for tasks like chart interpretation or text extraction. The Projects feature has been enhanced to support better document management, offering a structured environment for summarising or comparing large texts.

    Why It Stands Out: Claude’s ability to handle lengthy documents and its Projects space make it a top choice for users needing deep text analysis or organized workflows, with a focus on safe, moderated responses.

    Below is a tabular comparison

    CriteriaChatGPT (OpenAI)Gemini (Google)Grok (xAI)Claude (Anthropic)
    Natural LanguageConversational, creative, great for writing & Q&AAdvanced research & brainstorming, nuanced drafts Witty, creative dialogue, less filtered Ethical, contextual, excels in text analysis
    Languages~100 (English, Spanish, Mandarin, etc.).150+ with Google Translate.~50, English-focused, expanding.~30, mainly English.
    Tone & PersonalityFriendly, neutral, adaptable.Approachable, customizable via Gems.Humorous, edgy, JARVIS-like.Safe, formal, ethical.
    Real-Time InfoLimited, no web access.Strong, Google Search integration.Strong, X platform news & social.None, internal knowledge only.
    Chat OrganizationBasic history with search.Google account, no workspaces.Workspaces for collaboration.Projects for structured docs.
    Context Window~128K tokens.~1M tokens.~128K tokens.~200K tokens.
    Deep Search/ThinkDeep ResearchDeep Research (10/mo).Think mode via UI.None in free tier.
    Coding SupportStrong (Python, JS, debugging).Excellent (multi-language).Strong (Grok Studio for websites/games).Moderate, basic coding.
    Custom ModelsLimited GPTs (e.g., tutor).Gems (1-2, e.g., chef).None, default personality.None, Project-based workflows.
    Daily Limits~15 msgs/3hr (GPT-4o), then GPT-3.5.~500/mo, throttled at peak.Temporarily unlimited (Feb 2025).~50 msgs/day, varies.
    Top ModelGPT-4o (text, image).Gemini 2.5 Pro (text, image).Grok 3 (text, image).Claude 3.7 Sonnet (text, image).
    Response SpeedFast (1-2s), slows at peak.Very fast (0.5-1s).Fast (1-2s), varies with X.Moderate (2-3s), some delays.
    Recent HighlightsLookout app, Operators for tasks.Imagen 3, Spotify extension.Grok Studio, Google Drive.Vision for images, enhanced Projects.
    Daily active users122.5M35M16.5M3.3M

    Key Takeaways

    • ChatGPT: Versatile, great for general tasks, limited by message caps.
    • Gemini: Research powerhouse with Google integration.
    • Grok: Creative, social-media-driven with workspaces.
    • Claude: Ethical, text-heavy tasks with Projects.

    Which AI fits your workflow? Share your thoughts! #AI #Tech #GenAI

  • AI Coding Agent

    What is a Coding Agent?

    An AI coding agent is a software tool powered by an LLM (Large Language Model) or SLM (Small Language Model) that assists with software development tasks. These agents understand goals, generate full functions or apps, refactor code, fix bugs, write tests, and even collaborate across multiple files or repositories.

    Key Capabilities include

    • Code generation
    • Error detection and debugging
    • Code explanation and documentation
    • Automated refactoring
    • Multi-step planning and tool use

    A brief about IDEs

    Historically, Integrated Development Environments (IDEs) have been great in helping developers achieve their day to day activities with Highlighting Syntax errors, assist with Auto-completions, help with Code organising, Refactoring, Improve defect identification with Debugging & Running tools, Version control integration. All of these activities are useful but are limited to the list of frameworks and/or languages that the IDE support.

    Different IDEs were created to support different languages or frameworks. As an example JetBrains has different IDEs for Java (IntelliJ),Python (PyCharm), Data (DataGrip), Ruby (RubyMine).

    Similarly different companies created different IDEs such as Eclipse, VisualStudio, NetBeans with different capabilities

    How is it different from IDE support?

    With the support of Coding Agents, you can get all the capabilities that were previously provided by the specific IDEs across all (prominent) programming languages.

    This paved way for the IDE to be very light weight and different language support is obtained through plug-ins. Visual Studio Code is the most widely used IDE post-AI coding agents due to its versatility, robust AI integration (e.g., Copilot), and broad community support.

    How does it work?

    The Coding Agents took a great leap when the chat capability is introduced as Copilot Chat when the developers could provide a prompt and the Copilot agent generated the code. A simplified view of the interaction is as depicted below.

    What are available?

    Coding Agents can be categorised into types based on the interface they provide, LLMs they use in the background and their ability to iterate independently. This is greatly evolving space

    Interface based

    • Github Copilot – Plugin to Visual Studio Code
    • Windsurf – IDE
    • Claude Code
    • Cursor
    • Cline

    LLM based

    • Open AI
    • Claude
    • Gemini
    • DeepSeek

    Browser/Desktop based

    • Claude Code (Desktop)
    • Replit (Browser)
    • Devin (Browser)
    • Cursor (Desktop)

    Other ways to categorise the agents is based on deployment model (cloud vs local vs hybrid), Open Source vs Proprietary, Cost and accessibility based (Free vs Subscription)

    My experience with Coding Agents

    • Tools: Github Copilot, Windsurf, Claude Code, Replit
      • Github Copilot is the default and first coding agent I used. It has evolved in the last few months significantly and is very useful from prompt to auto corrections to agent coding
      • Windsurf is a forked version of VS Code repo with custom AI enhancements to make it more developer friendly to avoid VS Code’s limitations.
      • Claude Code is very useful desktop tool to work with entire projects. Though it is very useful in providing End to End solutions, it seemed very costly
      • Replit is a powerful agentic development environment where I could create an application with frontend, backend and a database with a clear description of problem statement. The fault tolerance is built into Replit to iteratively check the target state and the development continues.
    • Different LLMs that I used with Copilot
      • GPT-4o: Very useful in chat & edit mode.
      • Claude 3.5: Comparable to GPT-4o and excelled at refactoring/improving
      • Gemini 2.0: Great with ideas, structuring and crisp solutions. Better modular structure with in a class
      • GPT-4.1: Found better modular structuring and better coding results (Available for free till 30-Apr)
    • Different programming languages that I took AI Agent’s support
      • Tested with Java, Python for backend
      • React & Next JS for the front end
      • Postgres for the database
      • AWS CDK & Terraform for infra: Could only get the expected outputs on atleast the 3rd attempt
    • Different SDLC aspects that I covered
      • Unit tests
      • CI/CD via AWS Amplify/ AWS CDK and Vercel

    My learnings

    • Usability
      • Github Copilot stands out as the ease of access and zero cost to start with
    • Technical Usefulness
      • Claude Code is useful with a monthly membership to build applications with clearly defined requirements
      • Copilot with Claude 3.7 topped the code recommendations along with useful unit tests rather than generic tests
    • Concerns
      • Performance is not catered to by default. But when prompted, improvements are definitely provided.
      • Security – especially for the front end applications
      • Minimal reuse – As AI can generate code, every time new code is created though can be better
      • Outdated knowledge – As there is a cutoff date, code suggestions may not be upto date. In my case, Next JS had a vulnerability which wasn’t found in the code recommendations
    • Recommendations
      • Create clear context via the project requirement documents (PRDs)
      • Make sure relevant tools are accessible for better context and usage
      • Iterate over the results for better solutions. Considering Agent option is rolled out to wider users via Copilot or Replit, this can be easily achieved
      • Be careful while using open source MCP servers for tools for security constraints as you will be sharing API KEYs via external sites or tolls

  • Ollama – The power of local LLMs

    Ollama – What is it?

    Ollama is a tool for running, managing, and interacting with large language models (LLMs) on local machines. It provides an easy way to download, run, and fine-tune open-source models like Llama, Mistral, and Gemma without requiring cloud-based APIs.

    Key Features:

    • Runs Locally: No need for cloud services—everything runs on your computer.
    • Supports Multiple Models: Works with models like Meta’s Llama, Mistral, and others.
    • Simple Interface: You can interact with models via a CLI or programmatically in Python/Node.js.
    • Fine-tuning & Customization: Allows you to fine-tune models on your own data.
    • Efficient Execution: Optimized for fast performance on local hardware.

    How to get started?

    Download the ollama tool navigating to the official website ollama.com. The installation is straight forward just like any other software tool

    Once installed, you are ready to run LLMs locally

    Download and run the model using the below command

    ollama run <Model:Parameter>
    
    Ex: ollama run gemma3:1b

    You can find the list of models available and their memory requirements at model library

    How to use?

    Once running, interaction with ollama can be through command line or through APIs.

    In the command line, you can interact with the LLM by providing prompts. Sample Prompt: “What is the capital of Australia?”

    You can also set system message, show the current settings. Available options can be found by typing “/?”

    What is the capital of Australia?

    The other way to interact is to use APIs. Ollama by default runs on port 11434. You can test the APIs using Postman tool

    Below are some of the APIs to try

    Generate API:

    POST http://localhost:11434/api/generate
    Content-Type: application/json
    
    {
        "model": "gemma3:1b",
        "prompt": "What is the capital of France?",
        "stream": false
    }

    Chat completion API:

    POST http://localhost:11434/api/chat
    Content-Type: application/json
    
    {
      "model": "gemma3:1b",
      "messages": [
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Tell me a joke"}
      ]
    }

    Lets take a quick look at the differences between the two APIs

    /api/generate/api/chat/completion
    Used for single promptUsed for prompts with multiple interactions
    Request has only one “prompt”Request has an array of “messages”
    Doesn’t hold contextPrevious messages can be added to maintain context in subsequent requests
    Resposne contains one “response” Response contains an array of “messages” along with context, token count etc
    Usecase: Random text generationUsecase: Chatbot

    Other APIs to try include the below

    GET /api/tags: Lists the installed models
    
    
    POST /api/pull: Pulls and installs the model
    { "model": "gemma3:1b" }
    
    
    POST /api/create: Create a custom model
    {
      "name": "custom-mistral",
      "modelfile": "FROM mistral\nPARAMETER temperature=0.7\n"
    }
    
    
    POST /api/embeddings: Generate embeddings
    {
      "model": "mistral",
      "prompt": "Generate embeddings for this text"
    }

    The Postman collection can be found in my github repo at https://github.com/dcurioustech/ollama-local