Every language gives strings a large toolbox. The tools are easy to use and easy to misuse: a single call can hide a full pass over the string, and inside a loop that turns O(n) into O(n²). Here is what each everyday operation is called in the four languages, and what it costs. n is the length of the string.
s.length · len(s) · s.length() · s.size() — O(1): stored with the string.s[i] · s[i] · s.charAt(i) · s[i] — O(1).for (const ch of s) · for ch in s · for (char ch : s.toCharArray()) · for (char ch : s) — O(n). (Java's toCharArray() makes a copy; a plain index loop with charAt doesn't.)All of these return a new string (except in C++ where noted), so each one costs at least the length of what it produces.
s.slice(a, b) · s[a:b] · s.substring(a, b) · s.substr(a, len) — O(b - a): it copies those characters.s + t in all four — O(n + m).toUpperCase() · upper() · toUpperCase() · toupper(c) per character — O(n).trim() · strip() · trim() · (no built-in in C++) — O(n).replaceAll(a, b) · replace(a, b) · replace(a, b) · loop with — for simple text.s.indexOf(t) · s.find(t) · s.indexOf(t) · s.find(t) — returns the first index or -1 (std::string::npos in C++). O(n · m) in the worst case for a pattern of length m; Pattern 8 shows how to do it in O(n + m).s.includes(t) · t in s · s.contains(t) · s.find(t) != npos — same cost as find.startsWith(t) · startswith(t) · startsWith(t) · s.rfind(t, 0) == 0 — O(m).s.split(" ") · s.split() · s.split(" ") · read with a stringstream — O(n), and it builds a list of new strings.parts.join(" ") · " ".join(parts) · String.join(" ", parts) · append in a loop — O(total length).s === t · s == t · s.equals(t) · s == t — O(n): characters are compared until one differs. In Java, == only checks whether two variables point at the same object — always use equals.s < t · s < t · s.compareTo(t) < 0 · s < t — lexicographic order, like a dictionary: compare character by character, and a prefix comes before the longer word ("app" < "apple"). Uppercase letters come before lowercase, because their codes are smaller.[...s].sort().join("") · "".join(sorted(s)) · Arrays.sort(chars) · sort(s.begin(), s.end()) — O(n log n)./[a-z0-9]/i.test(ch) · ch.isalnum() · Character.isLetterOrDigit(ch) · isalnum(ch).ch - '0' in Java and C++, Number(ch) in JavaScript, int(ch) in Python.ch.toLowerCase() · ch.lower() · Character.toLowerCase(ch) · tolower(ch).A typical small task uses several tools at once. This one tidies a sentence: split it into words on any run of spaces, make each word start with a capital and continue in lower case, and join them with single spaces.
function titleCase(s) { const words = s.trim().split(/\s+/); // split on runs of whitespace: O(n) const out = []; for (const w of words) { if (w === "") continue; // an all-space input gives one empty word out.push(w[0].toUpperCase() + w.slice(1).toLowerCase()); } return out.join(" "); // one O(n) join at the end}Time O(n), space O(n). Every character is read a constant number of times: once by the split, once by the case changes, once by the join. Notice that none of these tools was called inside a loop over the whole string — that's the habit that keeps string code linear.
find[...s].reverse().join("") · s[::-1] · new StringBuilder(s).reverse().toString() · reverse(s.begin(), s.end()) (in place) — O(n).