Every time you send structured data to an LLM, you're paying for tokens. And if that data is JSON, you're paying for a lot of curly braces, repeated keys, and quotes that add zero information. That's the problem TOON (Token-Oriented Object Notation) solves: a compact, human-readable format that carries the same data as JSON using 30-60% fewer tokens. Field names declared once, indentation instead of braces, minimal quoting. Lossless round-trips to and from JSON. I've been thinking about where things are heading. LLM integrations in enterprise applications are no longer experimental, they're becoming core infrastructure using function calling, tool use, RAG, agentic workflows. All of these involve passing structured data into model context windows, and as these systems scale, token efficiency stops being a nice-to-have and starts being an engineering concern. At the same time, a significant share of enterprise backends run on Spring and Java. That's why I decided to build toon-spring: a Spring Boot library for serializing and deserializing TOON, with the same developer experience you'd expect from Jackson. It's open-sourced, so you can get a look and use it when you want and as you want! toon-spring gives you: → ToonMapper — works like Jackson's ObjectMapper, but for TOON → @ToonField / @ToonIgnore annotations for fine-grained control → Spring Boot auto-configuration with HttpMessageConverter for text/toon → Automatic tabular format detection for uniform object arrays → Configurable delimiters (comma, tab, pipe) and encoding options → Full support for nested objects, primitive arrays, and mixed structures Drop it into your Spring Boot project and you can serialize Java objects to TOON and deserialize TOON into POJOs with a single line of code. This is an early-stage project and there’s a lot of room to grow. I’d love to hear from engineers working at the intersection of Java and AI infrastructure. I’m genuinely looking forward to feedback, discussions, and suggestions from anyone interested in this space, open to constructive ideas and contributions. 🔗 https://lnkd.in/dXQNQ5Hm #Java #SpringBoot #OpenSource #LLM #AI #TOON #SoftwareEngineering
Giovanni Secondo’s Post
More Relevant Posts
-
#Post6 In the previous post, we understood how our code runs: Code → JVM → Process → Threads (https://lnkd.in/dns348v6) Now let’s go one step deeper What actually happens inside a process when it executes? When a Java program runs, the JVM creates a process. Inside that process, memory and execution are organized into different parts. 1. Heap Memory (Shared) This is where objects created using the "new" keyword are stored. • Shared by all threads within the same process • Not shared across different processes • Threads can read and modify data Because multiple threads access it → synchronization is required 2. Code Segment (Shared) Contains the bytecode (instructions to execute). • Read-only • Shared across all threads 3. Data Segment (Shared) Stores static and global variables. • Shared across all threads • Can be modified Synchronization is required when multiple threads update data 4. Stack (Thread-specific) Each thread has its own stack. • Stores method calls • Stores local variables • Not shared between threads 5. Program Counter (Thread-specific) Each thread has its own program counter. • Points to the current instruction being executed • Moves forward as execution progresses 6. Registers (Thread-specific) Each thread uses CPU registers to store temporary/intermediate data during execution. (We will explore how registers are used during context switching in upcoming posts) Important Understanding Inside a process: • Heap + Code + Data → Shared across threads • Stack + Program Counter + Registers → Private to each thread This separation is what makes multithreading both powerful and complex. Key takeaway Threads share memory (heap), but execute independently using their own stack and execution state. In the next post, we’ll explore Registers and how CPU switches between threads (context switching). #Java #SoftwareEngineering #Multithreading #BackendDevelopment #Programming
To view or add a comment, sign in
-
🚀 Day 92 of My 100 Days LeetCode Challenge | Java Today’s challenge was a deeper dive into graph algorithms and union–find (disjoint set) concepts. The problem revolved around maximizing the stability of a network while carefully selecting edges. The tricky part was managing connections while avoiding cycles and ensuring the best possible configuration based on constraints. To solve this efficiently, I used the Disjoint Set Union (Union-Find) data structure along with sorting and priority queue logic. This allowed me to dynamically merge components while maintaining optimal edge selection. Instead of brute-forcing all combinations, the union–find structure helped efficiently determine whether two nodes were already connected and whether adding an edge would improve or break the system. ✅ Problem Solved: Maximum Stability of a Network ✔️ All test cases passed (569/569) ⏱️ Runtime: 110 ms 🧠 Approach: Union-Find (Disjoint Set) + Greedy + Priority Queue 🧩 Key Learnings: ● Union-Find is extremely powerful for dynamic connectivity problems. ● Sorting edges strategically can simplify complex graph decisions. ● Avoiding cycles is crucial when building optimal graph structures. ● Priority queues help manage candidate edges efficiently. ● Graph problems often combine multiple data structures together. This problem was a great reminder that graph algorithms are not just about traversal — they’re about managing relationships between components efficiently. 🔥 Day 92 complete — strengthening my understanding of graph optimization and DSU techniques. #LeetCode #100DaysOfCode #Java #Graphs #UnionFind #Algorithms #ProblemSolving #DSA #CodingJourney #Consistency
To view or add a comment, sign in
-
-
Most beginners think HashMap is “just a data structure.” It’s not. It’s the backbone of performance in Java. Here’s how it actually works (simple): • Stores data as key-value pairs • Uses hashing to find index • Handles collisions using chaining / tree Why it matters: Fast lookups → better performance → scalable systems If you don’t understand this, you’re just coding… not engineering. Have you ever debugged a HashMap issue?
To view or add a comment, sign in
-
🚀 Day 27 of #75DaysofLeetCode Solved LeetCode 933 — Number of Recent Calls Today I worked on a simple yet powerful problem that teaches an important concept used in real-world systems: sliding window over streaming data. 💡 Problem Insight: We need to count how many requests happened in the last 3000 milliseconds for every new request. 📌 Instead of checking all past requests every time (which is inefficient), we use a Queue to maintain only relevant data. ⚡ Approach: Store incoming timestamps in a queue Remove all outdated timestamps (< t - 3000) The remaining size of the queue gives the answer 🧠 This is a perfect example of: Sliding Window Technique Real-time Data Processing Efficient Queue Usage 💻 Java Code: class RecentCounter { Queue<Integer> queue = new LinkedList<>(); public int ping(int t) { queue.offer(t); while (queue.peek() < t - 3000) { queue.poll(); } return queue.size(); } } 📊 Complexity: Time: O(1) amortized Space: O(N) 🔥 Real-world connection: This concept is widely used in: API rate limiting Server request monitoring Streaming analytics ✅ Small problems like this build strong intuition for handling large-scale systems. #LeetCode #DataStructures #Java #Coding #100DaysOfCode #SoftwareEngineering #ProblemSolving
To view or add a comment, sign in
-
-
🚀 Day 94 of My 100 Days LeetCode Challenge | Java Today’s problem was more about data structure design and mathematical optimization than just writing loops. The challenge was to design a Fancy sequence that supports the following operations efficiently: • Append a value • Add a number to all elements • Multiply all elements by a number • Retrieve an element at a specific index At first glance, updating every element after each operation seems straightforward — but that would be too slow. The key insight was to avoid updating all elements directly and instead track the transformations using lazy math operations (multiplication and addition factors) with modular arithmetic. By maintaining global add and multiply factors, we can apply transformations mathematically and compute the actual value only when needed. ✅ Problem Solved: Fancy Sequence ✔️ All test cases passed (107/107) ⏱️ Runtime: 45 ms 🧠 Approach: Data Structure Design + Modular Arithmetic + Lazy Updates 🧩 Key Learnings: ● Sometimes the best solution is avoiding unnecessary updates. ● Lazy evaluation can drastically improve performance. ● Modular arithmetic is essential when dealing with large numbers. ● Mathematical transformations can simulate bulk operations efficiently. ● Designing scalable data structures requires thinking beyond direct computation. This problem felt closer to real-world system optimization, where efficiency comes from smart design rather than brute-force updates. 🔥 Day 94 complete — improving my data structure design thinking and mathematical optimization skills. #LeetCode #100DaysOfCode #Java #DataStructures #Algorithms #ProblemSolving #DSA #CodingJourney #Consistency
To view or add a comment, sign in
-
-
👉 Records Are Not Just Shorter Classes.Stop Writing DTOs Like It’s 2015 Most developers think records are just for reducing boilerplate. That’s only half the story.Records don’t just reduce code they reduce bugs. Example: public record User(String name, int age) {} Yes, it removes: Getters Constructor equals() / hashCode() toString() But the real value is deeper. 1️⃣ Records Are Immutable by Design All fields are: final Set only via constructor No accidental mutation. That means: Thread-safe by default Safer data flow across layers 2️⃣ Records Enforce Data Integrity You can validate inside the compact constructor: public record User(String name, int age) { public User { if (age < 0) throw new IllegalArgumentException(); } } Now invalid objects can’t exist. 3️⃣ Better Memory + JVM Optimizations Records are: Simple Predictable Transparent This helps JVM: Optimize memory layout Inline aggressively Reduce overhead vs traditional POJOs 4️⃣ Perfect for DTOs & APIs Request/Response models Immutable configs Event payloads Avoid for: Entities (JPA issues) Mutable domain models 5️⃣ Records + Modern Java = Powerful Combine with: Pattern matching Sealed classes You get: 👉 Cleaner + safer domain modeling 💡 Senior Takeaway Records are not about writing less code. They are about Making invalid states unrepresentable #Java #JVM #ModernJava #Java17 #BackendDevelopment #SoftwareEngineering #CleanCode #Immutable #LearnInPublic
To view or add a comment, sign in
-
🚀 Day 19 of #75DaysofLeetCode Solved LeetCode Problem #724 — Find Pivot Index Today I worked on an interesting array problem that focuses on prefix sums and efficient traversal. 📌 Problem: Given an integer array, find the pivot index where the sum of elements on the left is equal to the sum of elements on the right. 🔍 Key Idea: Instead of recalculating sums repeatedly, we compute the total sum of the array first and maintain a running left sum while iterating. Formula used: Right Sum = Total Sum − Left Sum − Current Element If: Left Sum == Right Sum → we found the pivot index. 💡 Complexity: Time Complexity: O(n) Space Complexity: O(1) 💻 Java Solution: class Solution { public int pivotIndex(int[] nums) { int total = 0; for(int num : nums){ total += num; } int leftSum = 0; for(int i = 0; i < nums.length; i++){ int rightSum = total - leftSum - nums[i]; if(leftSum == rightSum){ return i; } leftSum += nums[i]; } return -1; } } ✨ Key Takeaway: Using prefix sum techniques helps reduce time complexity and is very useful for many array-based problems. Consistently practicing problems like these strengthens problem-solving and algorithmic thinking. #LeetCode #DSA #Java #ProblemSolving #CodingPractice #Algorithms
To view or add a comment, sign in
-
-
I benchmarked JSON parsing across 8 languages. The results humbled me. I threw a 120 MB JSON file (50,000 deeply nested records) at every fast parser I could find — C++, Rust, Zig, Go, Java, Node.js, and Bun.js. Used the best library each ecosystem has to offer. Optimized everything. Zero-copy strings in Rust, JIT-compiled parsers in Go, SIMD acceleration wherever available. Results ? C++ (simdjson) → 84ms Rust (serde_json) → 216ms Zig (std.json) → 244ms Go (sonic) → 244ms Java (Jackson) → 262ms Bun.js → 423ms Node.js → 698ms C++ wasn't just faster. It was in a different league. simdjson's On-Demand API processes JSON at 1.4 GB/s using AVX2 SIMD instructions — and it does it lazily. It never builds a tree. Never allocates objects. It just walks forward through the bytes and hands you exactly what you ask for. Every other language — no matter how optimized — still eagerly parses everything into structs, objects, or tokens. That's the fundamental gap. I spent hours squeezing performance out of Rust alone. Fixed broken compiler flags. Switched to zero-copy borrowed strings. Eliminated over a million heap allocations. Got it from 372ms down to 216ms. Still 2.6x slower than C++. Not because Rust is slow. It's genuinely fast. But when you're operating at the hardware level — branch prediction, cache lines, SIMD lanes — C/C++ still gives you the controls that nothing else does. New languages are amazing for productivity, safety, and developer experience. But for raw parsing throughput on big data? C++ with the right library is still king. The full benchmark (all code, reproducible) is open source → https://lnkd.in/gBfP2Azb #cpp #rust #golang #java #zig #performance #benchmarks #json #systemsprogramming
To view or add a comment, sign in
-
𝗙𝗿𝗼𝗺 𝟱𝟬 𝗟𝗶𝗻𝗲𝘀 𝘁𝗼 𝟭: 𝗠𝘆 𝗝𝗼𝘂𝗿𝗻𝗲𝘆 𝘄𝗶𝘁𝗵 𝗝𝗮𝘃𝗮 𝗜𝗺𝗺𝘂𝘁𝗮𝗯𝗶𝗹𝗶𝘁𝘆 🚀 I used to think creating a safe, immutable class in Java was a chore. It turns out, I was just doing it the "old" way. Here is how I moved from complex boilerplate to clean, modern Java: Phase 1: The Traditional Way (The 5 Steps) 📝 To make a standard class truly immutable, you need to follow these strict rules: final class: Stop anyone from extending and changing your logic. private final fields: Lock your variables so they can’t be reassigned. No Setters: If you don’t provide a setX() method, the data can’t change! Defensive Copying: If you have a List, copy it in the constructor so the caller can’t change it from the outside. Boilerplate: Manually write equals(), hashCode(), and toString(). (Total: ~50 lines of code for one simple object! 😫) Phase 2: The "Record" Revolution (The 1-Line Way) ⚡ Since Java 14, we can replace all that manual work with a Record. 𝐩𝐮𝐛𝐥𝐢𝐜 𝐫𝐞𝐜𝐨𝐫𝐝 𝐔𝐬𝐞𝐫(𝐒𝐭𝐫𝐢𝐧𝐠 𝐧𝐚𝐦𝐞, 𝐋𝐢𝐬𝐭<𝐒𝐭𝐫𝐢𝐧𝐠> 𝐠𝐫𝐚𝐝𝐞𝐬) {} Why this is a game-changer: It is final by default. Fields are private final by default. equals(), hashCode(), and toString() are generated for you. Phase 3: The Final Level (Deep Immutability) ❄️ Wait! Even in a Record, a List is still mutable. To make it 100% safe, use a Compact Constructor: 𝐩𝐮𝐛𝐥𝐢𝐜 𝐫𝐞𝐜𝐨𝐫𝐝 𝐔𝐬𝐞𝐫(𝐒𝐭𝐫𝐢𝐧𝐠 𝐧𝐚𝐦𝐞, 𝐋𝐢𝐬𝐭<𝐒𝐭𝐫𝐢𝐧𝐠> 𝐠𝐫𝐚𝐝𝐞𝐬) { 𝐩𝐮𝐛𝐥𝐢𝐜 𝐔𝐬𝐞𝐫 { 𝐠𝐫𝐚𝐝𝐞𝐬 = 𝐋𝐢𝐬𝐭.𝐜𝐨𝐩𝐲𝐎𝐟(𝐠𝐫𝐚𝐝𝐞𝐬); // 𝐍𝐨𝐰 𝐢𝐭'𝐬 𝐭𝐫𝐮𝐥𝐲 𝐟𝐫𝐨𝐳𝐞𝐧! } } The Result? ✅ Thread Safety: No more race conditions. ✅ Clean Code: You focus on the data, not the "ceremony." ✅ Peace of Mind: Your objects won't change behind your back. #Java #SoftwareEngineering #CleanCode #JavaRecords #ProgrammingTips #LearningInPublic
To view or add a comment, sign in
-
-
Can a Java class have TWO main() methods? Yes. And here is why. Most developers assume main() is sacred. Untouchable. One per class. But Java does not care about the name "main" being special. It only cares about METHOD SIGNATURES. The JVM entry point is strictly: public static void main(String[] args) Any other method named "main" with a DIFFERENT parameter list is just another overloaded method. Nothing special about it. Here is an example: public class Demo { public static void main(String[] args) { System.out.println("Entry point: " + args[0]); Demo d = new Demo(); d.main(42); } public static void main(int code) { System.out.println("Overloaded main: " + code); } } Run it with: java Demo Hello Output: Entry point: Hello Overloaded main: 42 What is happening here? -> The JVM picks main(String[]) as the entry point -> main(int) is just a regular static method -> Both coexist because their parameter types differ -> This is standard method overloading, nothing exotic The key rule behind this: Java identifies methods by their SIGNATURE = method name + parameter types. Two methods with the same name but different parameters are completely valid. The compiler resolves which one to call at compile time. This applies to ANY method, including main(). When does this matter in practice? -> OCP certification exams love this pattern -> Understanding it prevents confusion with inheritance chains where multiple classes define main() -> It reinforces that method overloading is a COMPILE-TIME decision The takeaway: main() is not a keyword. It is not reserved. It is just a method name with a specific signature that the JVM looks for at startup. Everything else about it follows normal Java rules. Know your fundamentals. They show up when you least expect them. Sources: 📊 Oracle Java Tutorials - Defining Methods (2024) https://lnkd.in/etVdUdGv 📊 Oracle Community - Overloading main method (2011) https://lnkd.in/ea4ipFiD 📊 JEP 512 - Compact Source Files and Instance Main Methods (2025) https://lnkd.in/eR92ttPU #Java #MethodOverloading #SoftwareEngineering #BackendDevelopment #OCP #JavaDeveloper #Programming #TechCommunity
To view or add a comment, sign in
Explore content categories
- Career
- Productivity
- Finance
- Soft Skills & Emotional Intelligence
- Project Management
- Education
- Technology
- Leadership
- Ecommerce
- User Experience
- Recruitment & HR
- Customer Experience
- Real Estate
- Marketing
- Sales
- Retail & Merchandising
- Science
- Supply Chain Management
- Future Of Work
- Consulting
- Writing
- Economics
- Artificial Intelligence
- Employee Experience
- Workplace Trends
- Fundraising
- Networking
- Corporate Social Responsibility
- Negotiation
- Communication
- Engineering
- Hospitality & Tourism
- Business Strategy
- Change Management
- Organizational Culture
- Design
- Innovation
- Event Planning
- Training & Development