The 2014 AP CS A Course Description includes 25 multiple choice questions. The questions start on page 23 (according to the PDF file page numbering, not the numbers on the bottoms of the pages). The letter answers are on page 47.

All of the questions are good. Several are fantastic, though some might be too challenging for an actual AP CS A Exam.

Question 1

The loop runs for the even values from 2 up to but not including 20 (0, 2, 4, 6, 8, 10, 12, 14, 16, 18).

The conditional statement evaluates to true for values of k that are 1 more than a multiple of 3. The values 4, 10, and 16 make the condition true and are printed.

Question 2

The diagram below shows the list animals after each step.

After the first 3 calls to add:

animals -> ["dog", "cat", "snake"]
             0      1      2

After the call to set:

animals -> ["dog", "cat", "lizard"]
             0      1      2

After the last call to add:

animals -> ["dog", "fish", "cat", "lizard"]
             0      1       2      3

After the call to remove:

animals -> ["dog", "fish", "cat"]
             0      1       2

Question 3

The code segment initially appears to remove all of the zeros from nums. The segment contains the common error of failing to account for the shift when an element is removed.

When the element at position 0 is removed, all subsequent elements shift one position to the left. The code segment increments k every time the loop runs. The code segment will remove 1 of the 2 zeros at the beginning of nums.

The code segment does not go out of bounds, so the only possible answer is D.

See ArrayList practice for related exercises.

Question 4

The code in Case I uses independent if statments, each of which explicitly checks both ends of the desired range. Although silly, this works.

Case II contains code that is syntactically incorrect. The expression 84 <= score <= 92 is not legal in Java.

Case III uses an else if chain to avoid explicitly checking for something that is already known. For example, if the condition score >= 84 is evaluated, score is already known to be < 93. Case III works.

Question 5

This question asks which code segment would procude the given output. The code segments all contain nested for loops. The majority of questions like this feature a single println() call at the bottom of the outer loop. This means that the outer loop controls the number of rows and the inner loop(s) controls what is printed in each row.

Being able to quickly ascertain how many times a for loop runs is an important skill.

The outer loop for each answer choice is the same. It runs 5 times, which is correct.

In choice (A), the inner loop runs 5 times each time the outer loop runs. This would result in 5 values being printed on each line, which does not match the given output.

In choice (B), the inner loop runs 1 time for the first row (when j is 1), 2 times for the second row (when j is 2) and so on. This doesn’t match the given output.

In choice (C), the inner loop runs 5 times for each row. This doesn’t match the given output.

In choice (D), the inner loop runs 5 times for the first row (when j is 1), 4 times for the second row (when j is 2) and so on. This is the correct number of times for each row. The loop prints j, which would result in the given output.

In choice (E), the inner loop runs 5 times for the first row (when j is 1), 4 times for the second row (when j is 2) and so on. Although the number of values output is correct, the loop prints k. This would result in the output below.

1 2 3 4 5
2 3 4 5
3 4 5
4 5
5

Question 6

In object oriented programming, most classes should have state (instance variables) and behavior (methods). In the context of a car dealership, numDoors, hasAir, and milesPerGallon could be appropriate instance variables of a Car class. Methods of Car could use the values of those instance variables when performing tasks such as generating a description. Choice A is correct.

Choice B makes much less sense. If Doors, AirConditioning, and MilesPerGallon are classes, they would have behavior. Although some argument could be made that AirConditioning could have behaviors such as turnOn and setTemperature, these don’t make sense in the context.

Choice C makes even less sense. When a superclass extends a subclass, an “is a” relationship is important. Choice C implies that a Doors is a Car.

Choice D is roughly equivalent to Choice C.

Choice E implies that a Car is a Doors and also that a Car is an AirConditioning.

Question 7

When a class implements an interface, it must have a public method with the same signature as each method from the interface. Only Case I meets the requirement.

It is easy to incorrectly assume that Case II works because a Circle is a Shape. Using the method header from Case II would prevent the isLargerThan method from accepting shapes other than circles.

Case III has both an incorrect parameter type and an incorrect return type.

Question 8

Choice A incorrectly ignores the possibility that adding to minutes could result in 1 or more additional hours.

Choice B fails to update hours and also updates minutes with the result of a calculation that does not make sense.

Choice C correctly calculates the number of additional hours as minutes / 60. Since both minutes and 60 are of type int, the result will also be of type int. Choice C also correctly calculates the number of remaining minutes as minutes % 60.

Choice D incorrectly switches % and /.

Choice E fails to update minutes to reflect the minutes that were converted into hours.

Question 9

The intent is to advance total, a TimeRecord, for each TimeRecord in timeCards.

Choice A incorrectly runs advance on timeCards[k]. It also fails to pass the required arguments.

Choice B incorrectly runs advance on timeCards[k]. It also fails to pass the required arguments. It also treats advance, a void method, as if it returns a value. It also treats total, a TimeRecord, as if it was a number.

Choice C incorrectly accesses private instance variables of TimeRecord in a class other than TimeRecord.

Choice D correctly runs advance on total and with correct arguments. Choice D correctly runs public methods of TimeRecord to access the hours and minutes of timeCards[k].

Choice E incorrectly runs advance on timeCards[k]

Question 10

The mystery method implements a variant of binary search. My binary search page references this specific question under the Binary search with insertion point return variant.

When num is found in arr, mystery returns the position of num in arr, as usual. When num is not found in arr, this variant returns where num would go if it was to be inserted into arr (and the sorted order maintained).

What makes this quesiton so challenging is that none of the answer choices initially appear to have anything to do with what this method does. Solving the problem requires careful consideration of the precondition.

“the elements in arr are in ascending order” is a standard precondition for binary search. “arr contains no duplicates” is not a standard precondition for binary search. If arr does not contain duplicates, the position of num in arr is equivalent to the number of elements that are less than num.

Consider the example below.

arr -> [10 15 17 20 25 30]
        0  1  2  3  4  5

mystery(0, 5, 20) returns 3 (the position of 20 in arr). There are 3 elements less than 20.

The assertion in answer choice (A) holds even if num is not in arr.

mystery(0, 5, 22) returns 4 (the position at which 22 would go in arr). There are 4 elements less than 22.

There is nothing special about a the first or last values in arr, nor values that would be inserted after the first or last values.

Question 11

Method findLongest fails to set lenCount back to 0 at the end of each run. lenCount is incremented every time val == target.

The condition lenCount > maxLen is checked at the end of each run and after the loop. Since lenCount only goes up, maxLen will end up as lenCount.

findLongest returns the number of times target is in nums (choice C).

Question 12 provides a strong hint for Question 11 if the error in findLongest is not obvious.

For additional information see Finding the minimum or maximum and the Run length increasing exercise.

Question 12

As discussed in the Question 11 explanation, findLongest fails to set lenCount back to 0 at the end of each run of target. lenCount must be set back to 0 at the end of each run, after maxLen has been updated if necessary. Choice E updates lenCount in the necessary location.

Choice A incorrectly sets lenCount to 0 each time the loop runs. This prevents counting the number of consecutive occurrences of target.

Choice B incorrectly sets lenCount to 0 before the check to see if the current run is longer than the longest run so far. This prevents updating maxLen.

Choice C incorrectly sets lenCount to 0 before actually updating maxLen.

Choice D incorrectly sets lenCount to 0 only if the length of the current run is the new maximum.

Question 13

The loop runs backwards through numbers. The first time it finds a value < num, it stops and returns the index of that value.

Every value for which the condition evaluated to false is >= num. Those are the values after the returned index. This is what choice C states.

Question 14

When I see a print statement in a recursive method on an Exam, I get excited. print statements often make recursion problems way easier to solve.

See Recursive methods with print statements for an explanation of the approach.

Answer choice (E) mentions infinite recursion. My preference is to handle that answer choice first. To determine if the method call mystery(1234) results in infinite recursion, consider the base case and the recursive call.

The recursive call is inside an if statement so the base case is the opposite of the condition. The base case is x / 10 == 0. x / 10 removes the last (rightmost) digit of x. The base case is when a digit is removed and the result is 0.

The recursive call is mystery(x / 10). The method is called with x without its last digit.

Repeatedly removing the last digit of x will eventually result in 0, so the method is not infinitely recursive.

mystery contains 2 print statements, each of which prints x % 10, the last (rightmost) digit of x. One print statement is before the recursive call. The other print statement is after the call. The value of x is not changed within mystery.

The first and last values that will ever be printed are both 4. Only answer choice (D) includes 4 as the first and last digit.

This question is not an exception. It is very common for questions that include recursive methods with print statements to be easily solved by considering the position of the print statement(s) relative to the recursive call(s).

Question 15

Although this quesiton does not include an answer choice that indicates that the act method cannot be run, I encourage my students to apply the first rule of polymorphism anyway.

The first rule of polymorphism is: The variable/reference type determines what methods CAN BE run.

fido.act() is a legal call because act is in Dog. If act was not in Dog, the call would not be legal, regardless of whether act was in UnderDog.

The second rule of polymorphism is: The object/instance type determines what method IS ACTUALLY run (and the most specific method possible is run).

The initial call to fido.act() runs the act method from UnderDog because the actual object type is UnderDog.

The initial call results in additional method calls. Once it becomes obvious that keeping track of a potentially complex sequence of method call is required, a stack based trace should be used. None of these method calls are recursive, but stack based tracing is appropriate for keeping track of any complex sequence of method calls.

The trace below shows the stack at each step as well as everything that has been printed so far, and an explanation of the changes from the previous step.

Step 1

UnderDog's act()

The initial call is to the act method of UnderDog.

Step 2

Dog's act()
UnderDog's act()

super.act() calls the act method of Dog.

Step 3

UnderDog's eat()
Dog's act()
UnderDog's act()

printed:
run

The act method of Dog prints "run" then calls eat. The call to eat runs the eat method of UnderDog. This is very commonly misunderstood. Both rules of polymorphism apply here.

eat is a legal call because the eat method is in Dog. If the eat method was not in Dog, the call would be a compile time error.

The actual object type is UnderDog, which means the eat method from UnderDog is run. This is true even though the eat method call is within a method of Dog.

Step 4

Dog's eat()
UnderDog's eat()
Dog's act()
UnderDog's act()

printed:
run

super.eat() calls the eat method of Dog.

Step 5

Dog's eat()  returns
UnderDog's eat()
Dog's act()
UnderDog's act()

printed:
run eat

The eat method of Dog prints "eat" then returns. Control returns to UnderDog's eat().

Step 6

Dog's eat()  returns
UnderDog's eat()  returns
Dog's act()
UnderDog's act()

printed:
run eat bark

The eat method of UnderDog prints "bark" then returns. Control returns to Dog's act().

Step 7

Dog's eat()  returns
UnderDog's eat()  returns
Dog's act()  returns
UnderDog's act()

printed:
run eat bark

The act method of Dog returns. Control returns to UnderDog's act().

Step 8

Dog's eat()  returns
UnderDog's eat()  returns
Dog's act()  returns
UnderDog's act()  returns

printed:
run eat bark sleep

The act method of UnderDog prints "sleep" then returns. Control returns to the method that made the initial call. (We’re done.)

Question 16

The method is called with a power of 2.

The base case is when n <= 1, in which case mystery returns 0.

The recursive call is with n / 2.

Each time the method runs, it adds 1 to the result. The method is counting how many times it runs.

The method divides n by 2 each time it runs and stops when n <= 1. The return value is equivalent to how many times n be divided by 2. Since n is a power of 2, the method returns the power (choice B).

See Recursive method analysis for an explanation of the technique.

Question 17

With the exception of the initial value of loc, this is a standard find the max. loc is used as an index throughout the code, so the method returns the index of the maximum (choice D).

When finding the min/max, it is typical to start the min/max at the first value that could be the min/max. This isn’t actually required. It is possible to start at any value that could be the min/max as long as all possible values are checked. In some cases, it is also possible to start at other values. See Find the minimum or maximum for details.

Question 18

All arguments in Java are passed by value. When a method is called with arguments, a copy of each argument is used to set the value of each parameter. A variable of primitive type stores its value directly. A variable of class type stores the memory address of an object (or null).

See Primitive types vs references exercises for a detailed demonstration of variable types. See Primitive types vs references exercises with calls for detailed examples of passing arguments to methods.

In test before the call to changer, the value of s is the memory address of "world". The value of n is 6.

The call changer(s, n) passes copies of the values of s and n to changer. x is set to memory address of the same String as s. y is set to 6. The diagram below shows the state immediately after the call to changer.

s and x point to the same box containing world. n stores 6. y stores 6.

String objects are immutable. The statement x = x + "peace"; makes a new String containing "worldpeace" and changes the value of x to the memory address of the new String. The value of s remains unchanged.

The statement y = y * 2; changes the value of y. The value of n remains unchanged.

When changer ends, x and y go away. The value of s, the value of n, and the state of the String to which s points are all unchanged.

Question 19

mat is created with 3 rows and 4 columns.

Choices A and B have the number of rows and columns reversed.

The loops visit each value in mat (in row major order).

Positions for which row < col are set to 1. These positions are from the top right corner to the top left to bottom right diagonal.

Positions for which row == col are set to 2. These positions are the top left to bottom right diagonal.

Remaining positions are set to 3.

This corresponds to choice D.

This problem can also be solved by evaluating the values for the first row.

See Intro to 2D arrays for additional discussion.

Question 20

This question is a clever variation of finding the maximum.

The loop runs while the index (k) is valid and while the current element is less than lim, a parameter. The conditional statement and the code inside are part of the standard algorithm for finding a maximum. (If the current element is greater than the maximum so far update the maximum.)

The question asks which combinations of lim and the number of executions of the labeled statements is/are valid.

v stores the value of the maximum. It is initialized to 0.

/* Statement S */ executes each time a new maximum is found and v is updated.

/* Statement T */ executes each time the loop runs.

The value of lim is not relevant for any of the cases.

In Case I, the loop runs 5 times but v is never updated. Although this would normally be possible (if all the elements were less than or equal to 0), the precondition is “arr contains only positive values”. If the loop runs 5 times, v must be updated at least once.

Case II is fine. The loop runs 9 times and v is updated 4 times.

Case III is clearly incorrect. v cannot be updated more times than the loop runs.

Question 21

Implementation 1 starts j at 0 and accesses sum[j - 1]. This results in an ArrayIndexOutOfBoundsException on the first loop execution.

Implementation 2 explicitly computes each partial sum by visting the elements in arr from 0 up to and including j for each position j in sum. The outer loop actually loops through sum, not arr, despite the condition.

If it worked, Implementation 1 would more efficiently compute each partial sum by adding arr[j] to the previous partial sum. Two modifications are required for Implementation 1 to work. j must start at 1 and sum[0] must be set to arr[0] before the loop.

Question 22

Case I is legal. Every subclass constructor must call a superclass constructor, either explicitly or implicitly. In this case, NamedPoint() does not explicitly call a superclass constructor. The call super() is implicitly (automatically) made as the first line. Class Point has a constructor that takes no parameters, so this works.

Case II incorrectly accesses private instance variables from the superclass.

Case III correctly calls the Point constructor that accepts 2 int parameters. The explicit call to the superclass constructor is correctly the first line in the sublcass constructor.

See Inheritance and polymorphism for more details.

Question 23

n is the length of both nums and result. The loop runs through each value from 0 up to but not including n / 2. Each time the loop runs, it accesses 2 elements from nums and copies them to 2 positions in result.

Assuming the 2 lines in the loop body do whey they appear to do, this approach will fail when n is odd. If the loop ran up to and including n / 2, the code would go out of bounds at the end. Since n / 2 is not included, the loop skips the last element.

In Example 1, n is 8, which is even. The loop runs when j is 0, 1, 2, and 3.

j j + n / 2 j * 2 j * 2 + 1
0 4 0 1
1 5 2 3
2 6 4 5
3 7 6 7

In Example 2, n is 7, which is odd. The loop runs when j is 0, 1, and 2.

j j + n / 2 j * 2 j * 2 + 1
0 3 0 1
1 4 2 3
2 5 4 5

Question 24

Case I correctly treats the 2D array m as an array of 1D arrays. It passes a reference to each row in m to method sum1D and sums the result.

Case II does the same thing as Case I, except it uses an enhanced for loop.

Case III uses a pair of enhanced for loops, which also works.

See Treating a 2D array as an array of 1D arrays and Enhanced for loop exercises for additional discussion, memory diagrams, and practice.

Question 25

The algorithm is a standard implementation of selection sort. See Selection sort for a detailed discussion of the algorithm.

Case I modifies the conditional statement such that it finds the maximum instead of the minimum. If the maximum is repeatedly swapped with the first element in the unchecked part, the array would be sorted in descending order.

Case II modifies which array value is stored in temp for the swap. This has no effect on the algorithm’s behavior.

Case III modifies the outer loop to traverse the array backwards. It also modifies the inner loop to traverse the left side of the array. Each minimum is swapped to the end of the array, which results in the array being sorted in descending order. The modification to the inner loop is important. The inner loop must traverse the unchecked part of the array, which would be on the left (since the outer loop goes backwards).

Comments

Comment on 2014 AP CS A Course Description MC Explanations