Programming Fundamentals

Chapter 6
Strings and Data Collections

Certificate in Java-Python-Programming Languages
Programming Fundamentals   •   Certificate

Strings and Data Collections

Introduction

Programs rarely operate on one value at a time. They process names, messages, measurements, transaction records, search results, and groups of related objects. Strings and data collections provide the fundamental structures for representing and manipulating such information.

A string is an ordered sequence of characters. Collections group multiple values so that a program can traverse, select, aggregate, organize, and update data. Python provides lists, tuples, sets, and dictionaries; Java provides arrays, the ArrayList, HashSet, and HashMap classes, together with interfaces such as List, Set, and Map.

Although Python and Java use different syntax and libraries, the underlying programming ideas are similar: indexing, traversal, membership testing, aggregation, mutability, and choosing an appropriate data structure.

Learning Objectives

By the end of this chapter, you should be able to:

  • create, compare, concatenate, search, index, and slice strings;
  • explain the difference between mutable and immutable data;
  • create and traverse arrays and common collection types;
  • distinguish lists, tuples, sets, dictionaries, and maps;
  • perform aggregation and selection operations;
  • choose a collection type based on ordering, duplication, mutability, and lookup requirements;
  • write clear Python and Java code for practical data-processing tasks;
  • identify common errors involving indexes, aliases, keys, and collection modification.

1. Strings as Ordered Text

A string stores text as an ordered sequence of characters. In Python, strings are represented by the str type. In Java, ordinary text is represented by the String class. Both support indexing-like access conceptually, but their syntax and capabilities differ.

name = "Ada Lovelace"
message = 'Welcome, ' + name
print(message)
String name = "Ada Lovelace";
String message = "Welcome, " + name;
System.out.println(message);

String literals may use single or double quotation marks in Python. Java uses double quotation marks for String literals; single quotation marks represent a single char value.

1.1 Indexing

Indexing retrieves one character from a sequence. Python indexes from zero and also permits negative indexes. Java uses the charAt method and does not support negative indexes.

word = "computer"
print(word[0])    # c
print(word[-1])   # r
String word = "computer";
System.out.println(word.charAt(0));
System.out.println(word.charAt(word.length() - 1));

For a string of length n, valid Python indexes are 0 through n − 1, or −n through −1. An index outside that range causes an error. In Java, charAt throws StringIndexOutOfBoundsException when the index is invalid.

1.2 Slicing and Substrings

Python slicing extracts a portion of a sequence using the form sequence[start:stop:step]. The stop position is excluded.

text = "programming"
print(text[0:4])    # prog
print(text[4:])     # ramming
print(text[:6])     # progra
print(text[::2])    # pormig

The general number of selected positions for a positive step is determined by the range of indexes from start up to, but not including, stop, moving by step. A zero step is invalid.

Java provides substring(beginIndex, endIndex), where the ending index is also exclusive:

String text = "programming";
System.out.println(text.substring(0, 4));
System.out.println(text.substring(4));

Python slicing generally returns a new string. Java substring returns a new string value, although implementation details may vary by Java version. Neither operation changes the original string.

1.3 Immutability

Strings are immutable: after creation, their character contents cannot be changed in place. An operation that appears to modify a string actually creates another string.

word = "cat"
word = word + "er"       # a new string is assigned to word
String word = "cat";
word = word + "er";

Immutability improves safety because a string shared by several parts of a program cannot be altered unexpectedly. However, repeatedly concatenating strings in a large loop may create many intermediate objects. Python programmers can collect pieces in a list and use join; Java programmers can use StringBuilder.

parts = ["data", "structures", "matter"]
result = " ".join(parts)
StringBuilder builder = new StringBuilder();
builder.append("data").append(" ");
builder.append("structures").append(" ");
builder.append("matter");
String result = builder.toString();

1.4 Common String Operations

OperationPythonJava
Lengthlen(s)s.length()
Convert to lowercases.lower()s.toLowerCase()
Convert to uppercases.upper()s.toUpperCase()
Remove outer whitespaces.strip()s.trim() or strip()
Find a substrings.find(x)s.indexOf(x)
Test containmentx in ss.contains(x)
Replace texts.replace(a, b)s.replace(a, b)
Split texts.split(delimiter)s.split(delimiter)

String comparisons should be chosen carefully. Python’s == compares string contents. In Java, == compares object references, so content comparison normally requires .equals():

if (username.equals("admin")) {
    System.out.println(" privileged user");
}

A safer form when a variable could be null is "admin".equals(username).

1.5 Traversing Strings

Traversal means visiting each element in sequence. A for loop is usually clearer than manual index management.

for character in "hello":
    print(character)
for (int i = 0; i < word.length(); i++) {
    System.out.println(word.charAt(i));
}

A common application is character classification. For example, a program may count vowels:

text = "Education"
count = 0
for ch in text.lower():
    if ch in "aeiou":
        count += 1
print(count)   # 5

The result is 5 because the vowels are e, u, a, i, and o. The loop examines each character once, so its running time grows approximately in proportion to the string length.

2. Arrays and Indexed Sequences

An array stores a fixed number of elements of a specified type in indexed positions. Arrays are especially useful when the size is known and direct positional access is important.

2.1 Java Arrays

int[] scores = {82, 91, 76, 88};
System.out.println(scores[1]);  // 91
scores[2] = 79;

The array length is fixed after creation:

int[] values = new int[5];
System.out.println(values.length);

Java arrays are zero-indexed. An array of length 5 has valid indexes 0, 1, 2, 3, and 4. Java arrays contain one declared element type, although reference-type arrays may hold objects belonging to compatible subclasses.

2.2 Python Lists as Dynamic Indexed Sequences

Python does not normally use a separate built-in array type for general programming. Lists provide dynamically sized indexed sequences and may contain values of different types.

scores = [82, 91, 76, 88]
print(scores[1])
scores[2] = 79
scores.append(95)

Python lists support slicing, insertion, deletion, and growth. Their flexibility is convenient, but a list should not contain unrelated types merely because the language permits it. Consistent element types make programs easier to understand and test.

2.3 Traversal and Enumerated Traversal

for score in scores:
    print(score)

for index, score in enumerate(scores):
    print(index, score)
for (int score : scores) {
    System.out.println(score);
}

Use an index when the position is required; otherwise, direct traversal communicates intent more clearly.

3. Python Lists and Java Lists

A list is an ordered collection that normally permits duplicate values. Python’s list and Java’s List abstraction serve similar purposes.

items = ["pen", "book", "pen"]
items.append("lamp")
items.remove("pen")       # removes the first matching value
import java.util.ArrayList;
import java.util.List;

List<String> items = new ArrayList<>();
items.add("pen");
items.add("book");
items.add("pen");
items.remove("pen");

Java generics, such as List<String>, specify the intended element type at compile time. Python checks types at runtime and relies more heavily on testing and programming discipline.

Important list operations include:

  • append or add an element;
  • insert at a position;
  • remove by value or position;
  • search for membership;
  • sort or reverse;
  • traverse every element;
  • compute an aggregate such as a total or maximum.

A list is appropriate when order matters, duplicates are meaningful, and the collection may change.

4. Tuples and Immutable Records

A Python tuple is an ordered, immutable collection:

point = (3, 7)
x, y = point

Tuples are useful for fixed groups of related values, such as coordinates, database rows, or configuration records. They communicate that the collection itself should not be changed.

coordinates = [(0, 0), (2, 5), (4, 1)]

A tuple may contain a mutable object, so immutability applies to the tuple’s references, not necessarily to every object reachable through them. For example, a tuple containing a list still contains a list that can be changed.

Java has no direct built-in tuple type. Common alternatives include a small class, a record, or a library type:

record Point(int x, int y) {}
Point point = new Point(3, 7);

A record is a concise way to represent a data carrier with named components. It is not identical to a Python tuple, but it often serves a similar design purpose.

5. Sets: Uniqueness and Membership

A set stores distinct elements. Sets are suitable when duplicate values have no meaning and fast membership testing is more important than positional access.

languages = {"Python", "Java", "Python"}
print(languages)          # contains Python and Java once each
print("Java" in languages)

Sets are unordered in the conceptual data-model sense. A program should not depend on a particular iteration order unless the implementation and language documentation explicitly guarantee one.

Common set operations include union, intersection, difference, and symmetric difference:

required = {"loops", "strings", "functions"}
completed = {"strings", "functions", "testing"}

print(required & completed)  # intersection
print(required - completed)  # still required
print(required | completed)  # union

Java provides Set and implementations such as HashSet, LinkedHashSet, and TreeSet:

Set<String> languages = new HashSet<>();
languages.add("Python");
languages.add("Java");
languages.add("Python");

HashSet is optimized for general membership operations but does not promise sorted iteration. LinkedHashSet preserves insertion order, while TreeSet maintains sorted order and requires elements to be comparable or supplied with a comparator.

Set elements must support reliable equality and hashing. If an object changes in a way that affects its hash value while it is stored in a hash-based set, membership behavior can become incorrect. This is one reason immutable values are often good set elements.

6. Dictionaries and Maps: Key–Value Association

A dictionary or map associates each key with a value. It is appropriate when a program must look up information by an identifier rather than by numeric position.

prices = {"book": 12.50, "pen": 1.75}
print(prices["book"])
prices["lamp"] = 18.00

Keys are unique. Assigning a value to an existing key replaces the previous value. Python dictionaries preserve insertion order in modern Python implementations, but the main conceptual purpose remains key-based association.

Safe access can use get:

price = prices.get("notebook", 0.0)

This returns 0.0 if the key is absent rather than raising a KeyError.

Java uses the Map interface and implementations such as HashMap, LinkedHashMap, and TreeMap:

Map<String, Double> prices = new HashMap<>();
prices.put("book", 12.50);
prices.put("pen", 1.75);
System.out.println(prices.get("book"));

get returns null when a key is absent in a normal HashMap. Methods such as getOrDefault, containsKey, and putIfAbsent can make intent explicit.

Traversal commonly examines keys, values, or entries:

for item, price in prices.items():
    print(item, price)
for (Map.Entry<String, Double> entry : prices.entrySet()) {
    System.out.println(entry.getKey() + " " + entry.getValue());
}

A map is preferable to parallel lists. Two parallel lists can become inconsistent if one is updated without the other; a map binds each key directly to its value.

7. Traversal, Selection, and Aggregation

7.1 Selection

Selection retains elements satisfying a condition. Python offers comprehensions for concise transformations:

scores = [55, 72, 91, 64, 48]
passes = [score for score in scores if score >= 50]

The selected values are 55, 72, 91, and 64. In Java, a conventional loop is transparent at certificate level:

List<Integer> passes = new ArrayList<>();
for (int score : scores) {
    if (score >= 50) {
        passes.add(score);
    }
}

Java streams can express the same operation, but a loop may be easier for beginners to debug.

7.2 Transformation

Transformation creates a corresponding output value for each input value.

names = ["ada", "grace", "alan"]
capitalized = [name.title() for name in names]

The input is not changed. This distinction matters when several variables refer to the same mutable list.

7.3 Aggregation

Aggregation combines many values into a result such as a count, total, minimum, maximum, or average. For numeric values x₁, x₂, …, xₙ, the arithmetic mean is:

mean = (x₁ + x₂ + … + xₙ) ÷ n

Here, n is the number of values and n > 0. The values must be measured on a scale for which an arithmetic mean is meaningful, and they must have compatible units.

scores = [70, 80, 90]
total = sum(scores)
average = total / len(scores)
print(total, average)   # 240 80.0

The average is 80.0 because 240 ÷ 3 = 80. In Java:

int total = 0;
for (int score : scores) {
    total += score;
}
double average = (double) total / scores.length;

The cast to double is important when scores.length and total are integers; otherwise, integer division may discard the fractional part.

7.4 Filtering and Aggregation Together

transactions = [12.50, 8.00, 25.75, 4.25]
large = [amount for amount in transactions if amount >= 10]
large_total = sum(large)

The selected values are 12.50 and 25.75, so large_total is 38.25. A professional program should also validate currency assumptions, avoid inappropriate floating-point use for exact financial accounting, and consider decimal types when exact decimal representation is required.

8. Choosing an Appropriate Collection

Collection selection should follow the data’s meaning rather than personal preference.

RequirementSuitable Python typeSuitable Java type
Ordered, changeable sequencelistArrayList / List
Fixed ordered grouptuplerecord or immutable class
Fixed typed indexed storagelist or array-like structurearray
Unique valuessetHashSet / Set
Key–value lookupdictHashMap / Map
Sorted unique valuesset with sorting as neededTreeSet
Insertion-ordered mapdictLinkedHashMap

Ask the following questions:

  1. Does order matter?
  2. Are duplicates allowed or meaningful?
  3. Will the collection change after creation?
  4. Is access primarily by position or by key?
  5. Is sorted order required?
  6. Must elements have one declared type?
  7. Will the program frequently test membership?

A data structure should make valid operations easy and invalid interpretations difficult. For example, a set communicates uniqueness more clearly than a list followed by repeated duplicate-removal code.

9. Aliasing, Copying, and Mutation

Two variables may refer to the same mutable object. This is called aliasing.

original = [1, 2, 3]
alias = original
alias.append(4)
print(original)   # [1, 2, 3, 4]

To create a shallow copy:

copy_of_list = original.copy()

A shallow copy creates a new outer list, but nested mutable objects may still be shared. Java references behave similarly:

List<Integer> original = new ArrayList<>(List.of(1, 2, 3));
List<Integer> copy = new ArrayList<>(original);

The copy has a separate list structure. The depth of copying required depends on whether the elements themselves are mutable objects.

A frequent error is modifying a collection while traversing it. In Java, removing directly from a collection during an enhanced for loop may cause ConcurrentModificationException. Use an iterator, removeIf, or construct a filtered collection. In Python, iterate over a copy or build a new collection:

values = [1, 2, 3, 4, 5]
values = [value for value in values if value % 2 == 1]

10. Applied Case: Course Results Processor

Background

A training centre needs a small program to process learner results. Each learner has an identifier, a name, and several assessment scores. The program must calculate an average, identify pass status, and produce a set of learners who passed.

This is an illustrative case, not a production grading system.

Problem

The program must represent:

  • one learner’s scores as an ordered collection;
  • learner records associated with unique identifiers;
  • a group of passed learner identifiers without duplicates;
  • a textual report.

Python Approach

learners = {
    "L101": {"name": "Amina", "scores": [72, 81, 68]},
    "L102": {"name": "Jon", "scores": [45, 59, 51]},
    "L103": {"name": "Mei", "scores": [88, 91, 84]}
}

passed = set()

for learner_id, record in learners.items():
    scores = record["scores"]
    average = sum(scores) / len(scores)
    status = "Pass" if average >= 50 else "Fail"
    if status == "Pass":
        passed.add(learner_id)
    print(f"{record['name']}: {average:.1f} ({status})")

print("Passed learners:", passed)

The dictionary provides lookup by learner identifier. Each score list preserves assessment order and permits aggregation. The set ensures that a learner is recorded only once in the passed group.

The averages are 73.7 for Amina, 51.7 for Jon, and 87.7 for Mei, using the displayed one-decimal rounding. All three pass under the stated threshold of 50.

Analysis and Design Decisions

A list is used for scores because order and repeated values are acceptable. A dictionary is used for learner records because identifiers are keys. A set is used for passed identifiers because uniqueness is required. A tuple would be suitable if scores were deliberately fixed and immutable, but a list is more practical if scores may be corrected.

Professional Considerations

A real system would validate that scores fall within an allowed range, handle missing scores, define rounding policy, protect personal information, and record who is authorized to view or change results. A pass threshold should be a documented policy rather than an unexplained constant. If results affect employment or progression, auditability and fairness are important.

11. Errors, Testing, and Best Practices

Common errors include:

  • using index len(sequence) as the last valid index;
  • confusing a string’s character count with a byte count in systems involving encodings;
  • using Java == instead of .equals() for string contents;
  • assuming set or hash-map traversal is sorted;
  • attempting to access a missing dictionary key without handling the possibility;
  • dividing by the length of an empty collection;
  • changing a collection during traversal;
  • sharing a mutable collection unintentionally;
  • using floating-point values where exact decimal financial calculations are required.

Good practice includes descriptive names, small functions, validation at input boundaries, type-consistent collections, and tests for empty, singleton, duplicate, missing-key, and boundary-index cases. A test suite should verify both ordinary behavior and failure behavior.

12. Contemporary Development and AI-Enabled Tools

Modern integrated development environments provide collection inspection, code completion, static analysis, and debugging support. AI-assisted programming tools can suggest loops, comprehensions, and library calls, but generated code must be reviewed. Particular risks include incorrect assumptions about ordering, unsafe handling of personal data, inefficient repeated concatenation, and code that silently ignores missing values.

A responsible workflow is to specify the data model first, ask the tool for a limited implementation, test edge cases, inspect documentation, and retain human responsibility for correctness, privacy, and maintainability. An AI suggestion is not evidence that a collection operation has the desired semantics.

Chapter Summary

Strings are immutable ordered sequences used to represent text. They support indexing, slicing or substring extraction, searching, replacement, splitting, comparison, and traversal. Python and Java differ in syntax, especially for indexing and string equality, but the underlying concepts are shared.

Arrays provide fixed-size indexed storage, while lists provide flexible ordered collections that commonly allow duplicates. Tuples represent fixed ordered groups in Python, and Java records or immutable classes can serve related design purposes. Sets represent uniqueness and support membership and set operations. Dictionaries and maps associate unique keys with values and are the appropriate choice for key-based lookup.

Traversal visits collection elements; selection retains values meeting a condition; transformation creates corresponding outputs; and aggregation combines values into a result. Collection choice should be based on order, duplication, mutability, access method, typing, and sorting requirements. Careful programmers also consider aliasing, copying, invalid indexes, missing keys, concurrent modification, numerical assumptions, privacy, and testing.

Key Terms

String: An ordered sequence of characters.

Character: A single textual symbol.

Index: A position used to access an element in a sequence.

Slice: A selected portion of a sequence.

Immutable: Unable to be changed after creation.

Array: A fixed-size indexed collection, typically of one declared type.

List: An ordered collection that commonly permits duplicates.

Tuple: An ordered immutable collection in Python.

Set: A collection of unique elements.

Dictionary: A Python key–value collection.

Map: A Java abstraction associating keys with values.

Key: An identifier used to retrieve a mapped value.

Value: Data associated with a key or stored in a collection.

Traversal: Systematic visiting of collection elements.

Selection: Retaining elements that satisfy a condition.

Aggregation: Combining multiple values into one result.

Aliasing: Multiple references to the same mutable object.

Shallow copy: A copy of an outer collection whose nested references may remain shared.

Mutability: The ability of an object to change after creation.

Generic type: A type parameter, such as Java’s List<String>, describing permitted elements.

Review Questions

Short Answer Questions

  1. What is the difference between indexing and slicing a string?
  2. Why are strings described as immutable in Python and Java?
  3. When is a set more suitable than a list?
  4. What is the purpose of a key in a dictionary or map?
  5. Why can aliasing cause unexpected changes to a collection?

Long Answer Questions

  1. Compare Python lists and tuples, including their mutability, typical uses, and limitations.
  2. Explain the similarities and differences between a Python dictionary and a Java Map.
  3. Describe how traversal, selection, transformation, and aggregation work together in data processing.
  4. Explain the principal factors that should guide the choice between an array, list, set, and map.
  5. Discuss common errors in string and collection programming and propose preventive practices.

Analytical / Application Questions

  1. Design a collection structure for an inventory system that stores unique product codes, product descriptions, and quantities. Justify each chosen type in Python and Java.
  2. A program stores learner scores in a list and must report the average, highest score, and all scores at least 70. Describe the algorithm and identify edge cases that must be tested.
  3. A developer uses a list to store usernames and repeatedly checks whether a username exists. Analyze the design and recommend an alternative if membership tests are frequent and duplicate usernames are not permitted.

Multiple Choice Questions with Answers

Question 1

Which Python expression selects characters from index 1 up to, but not including, index 4?

A. text[1:4]

B. text[1:5]

C. text[0:4]

D. text[4:1]

Correct Answer: A. text[1:4]

Question 2

How should Java programmatically compare the contents of two String objects?

A. Using ==

B. Using .equals()

C. Using sameReference()

D. Using .compareAddress()

Correct Answer: B. Using .equals()

Question 3

Which collection is most appropriate when duplicate values must be eliminated and membership testing is central?

A. List

B. Tuple

C. Set

D. Array

Correct Answer: C. Set

Question 4

What is the average of the values 70, 80, and 90?

A. 70

B. 75

C. 80

D. 90

Correct Answer: C. 80

Question 5

What happens when a new value is assigned to an existing key in a Python dictionary?

A. A duplicate key is created

B. The existing value is replaced

C. The dictionary becomes a set

D. The assignment is always rejected

Correct Answer: B. The existing value is replaced

Question 6

Which Python collection is ordered and immutable?

A. List

B. Set

C. Dictionary

D. Tuple

Correct Answer: D. Tuple

Question 7

Why might Java code use StringBuilder in a loop that constructs a large string?

A. It converts strings into sets

B. It avoids all character encoding

C. It can reduce repeated intermediate string creation

D. It makes strings mutable everywhere

Correct Answer: C. It can reduce repeated intermediate string creation

Question 8

Which structure is most directly suited to associating learner IDs with learner records?

A. Map or dictionary

B. Set only

C. Character array only

D. Tuple without keys

Correct Answer: A. Map or dictionary

Question 9

What is the result of sum([12.50, 25.75]) in Python?

A. 28.25

B. 37.25

C. 38.25

D. 40.75

Correct Answer: C. 38.25

Question 10

What does aliasing mean in the context of mutable collections?

A. A collection has no elements

B. Two references point to the same object

C. A list is automatically sorted

D. A dictionary has duplicate keys

Correct Answer: B. Two references point to the same object

Programming Fundamentals
Chapter 6 of 14