Exercices sur les structures de contrôle, les structures de données, les structures itératives #
Questions/Réponses #
Veuillez répondre mentalement, sur papier ou bien en créant le code nécessaire pour répondre à ces questions avant de regarder la réponse. Certaines questions sont difficiles, et il est normal de ne pas toutes les réussir.
N’oubliez pas d’utiliser notre pense-bête Java au besoin. Pour les mathématiques, vous pouvez faire référence à notre rappel sur les principales notions mathématiques du cours.
Quand on vous demande de produire du code, vous devez le tester. C'est une erreur commune chez les étudiants: ils produisent rapidement du code en supposant qu'il est fonctionnel. Prenez le temps de vous relire, d'être attentif. Et testez votre code. Encore et encore.
Il est permis d’utiliser l'intelligence artificielle pour aider à comprendre les solutions. Cependant vous devriez vous entraîner à produire vos propres réponses.
Réponses uniques ? #
Les exercices comportent une solution vous permettant de comparer votre approche avec la nôtre. Il n'y a pas de solution unique aux problèmes en général. Vous pouvez arriver avec une solution qui est préférable ou moins bonne que celle que nous offrons. Pour faire ces questions, vous devez avoir fait toutes les lectures préalables. Vous disposez alors toujours des fondements nécessaires pour faire les exercices. Nous vous encourageons tout de même à faire vos propres recherches en complément de vos lectures. Dans certains cas, au sein de la solution que nous offrons, nous pouvons utiliser des notions techniques qui n'ont pas été vues directement dans le cours, mais qui devraient vous être facilement accessibles.
Question 1 #
Si x == true et que l'on utilise le code suivant, quel sera la sortie en ligne de commande?
if(x != true && x == true) {
System.out.println("AAA");
} else {
System.out.println("BBB");
}
Réponse
Le résultat affiché sera BBB, car x != true donnera Faux et x == true donnera Vrai. Par définition, Faux ET Vrai = FAUX
Question 2 #
Le code suivant contient 5 erreurs, veuillez les identifier :
int x = 5;
if((x > 1) && (x =< 10) {
x = "5";
} if (x !!= 5) {
x = 100;
}
Réponse
Voici les erreurs :
- l'opérateur =< n'existe pas, c'est plutôt <=
- il manque une parenthèse ")" à la première ligne if (avant le })
- on ne peut assigner une String à un int
- en supposant que l'indentation est correcte, il manque le else pour } else if (
- l'opérateur !!= n'existe pas, c'est plutôt !=
int x = 5;
if((x > 1) && (x <= 10)) {
x = 5;
} else if (x != 5) {
x = 100;
}
Question 3 #
Quelle est la différence entre une boucle while et une boucle do-while?
Réponse
Le contrôle se fait au départ dans la boucle while alors que le contrôle se fait à la fin dans le do-while. Dans la boucle while, il y a donc un contrôle avant même de faire la première itération.
Question 4 #
Quelle sera la sortie d'affichage de ce block switch-case et pourquoi?
int index = 10;
switch (index) {
case 1:
System.out.println(11);
break;
case 2:
System.out.println(22);
break;
case 3:
System.out.println(33);
break;
default:
System.out.println("Default");
break;
}
Réponse
La sortie sera "Default", car la valeur de la variable index = 10 ne correspond à aucun des cas du switch. C'est donc le sous-bloc "Default" qui est exécuté.
Question 5 #
Vrai ou faux: un tableau de type int[10] occupera 40 octets en mémoire parce qu'il y a 10 entiers (int) et que chaque entier occupe 4 octets (32 bits).
Réponse
Faux. Il est vrai qu'il y a 10 entiers (int) et que chaque entier occupe 4 octets. Cependant, Java doit aussi garder une trace de la longueur du tableau (10 éléments). Il y a aussi d'autres contraintes. Il serait plus juste de dire que le tableau va utiliser plus de 40 octets.
Question 6 #
Supposons que dans un code, nous avons créé un tableau d'une dimension de 10 éléments (int[] tableau = new int[10]) et que nous accédons, suite à son instanciation, au 20e élément du tableau ( int x = tableau[20]), qu'adviendra-t-il?
Réponse
À l'exécution l'opération int x = tableau[20] va générer une erreur du type : java.lang.ArrayIndexOutOfBoundsException: 20, car nous tentons d'accéder à des index du tableau qui n'existe pas.
Question 7 #
Est-ce possible de créer une structure de données de type ArrayList contenant des entiers en utilisant l’expression suivante : ArrayList<int> = new ArrayList(); ?
Réponse
Le type int n’est pas une classe. Il faut plutôt utiliser la syntaxe ArrayList<Integer> = new ArrayList<Integer>();. Une mécanique de conversion automatique permet d’assigner des types à leur objet équivalent. Par exemple Integer i = 5; ou bien :
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(5);
Question 8 #
Votre employeur vous demande créer une fonction en Java qui vous permettra de trouver dans un tableau à deux dimensions, un couple {clé,valeur}. Pour démarrer, vous avez le code suivant, veuillez créer une fonction trouver qui reçoit en paramètre la clé et retourne la valeur :
public class Dictionnaire {
int[][] tableau2D = {{5,3},{2,5},{4,3},{8,10},{105,32},{55,34},{12,21},
{15,51},{44,1},{6,3},{19,3},{65,13},{1235,1353},{51515,32143},
{15155,3555}};
public static void main(String[] args) {
Dictionnaire dict = new Dictionnaire();
//Trouver la valeur associée à la clé
//System.out.println("Valeur =" + dict.trouver(12));
}
}
Réponse
Voici le code
public class Dictionnaire {
int[][] tableau2D = {{5,3},{2,5},{4,3},{8,10},{105,32},{55,34},{12,21},
{15,51},{4,1},{6,3},{19,3},{65,13},{1235,1353},{51515,32143},
{15155,3555}};
/**
* Fonction permettant de trouver une valeur à partir d'une clé
* @param clef
* @return valeur
*/
int trouver(int clef) {
// Initialiation de la variable valeur. Si la clef n'est pas trouvé alors on retourne -1.
int valeur = -1;
// Itération dans le tableau pour trouver le bon
for(int i = 0; i < tableau2D.length; i++) {
// Vérification de la cle avec l'index courant;
if (tableau2D[i][0] == clef) {
// Si oui, arrêter l'exécution et retourner la valeur.
return tableau2D[i][1];
// ou valeur = tableau2D[i][1]; break;
}
}
return valeur;
}
public static void main(String[] args) {
Dictionnaire dict = new Dictionnaire();
//Trouver la valeur associée à la clé
System.out.println("Valeur =" + dict.trouver(12));
}
}
Question 9 #
Montrer comment on peut trier un tableau d'entiers en Java.
Réponse
Le plus simple est d'utiliser la méthode Arrays.sort.
import java.util.Arrays;
class Main {
public static void main(String[] args) {
int[] tableauTest = {4, 6, 9, 1, 2, 76, 222, 5, 22};
Arrays.sort(tableauTest);
for (int i : tableauTest) {
System.out.print(i + " ");
}
System.out.println();
}
}
Question 10 #
Veuillez créer une fonction, recevant un ArrayList en entrée, et qui retourne une nouvelle ArrayList avec les éléments inversés, i.e. le premier élément à la fin, le dernier élément en premier et ainsi de suite.
Réponse
Voici une solution possible. Elle est grossièrement inefficace sur le plus du temps de calcul: vous pouvez sans doute faire mieux.
import java.util.ArrayList;
public class ArrayListUtils {
public static ArrayList inverser(ArrayList liste) {
ArrayList inverse = new ArrayList();
for(Object o : liste) {
inverse.add(0, o);
}
return inverse;
}
public static void main(String[] args) {
ArrayList<String> test = new ArrayList<String>();
test.add("1");
test.add("2");
test.add("3");
test.add("4");
test.add("5");
ArrayList<String> testInverse = ArrayListUtils.inverser(test);
for(String s : testInverse) {
System.out.print(s + " ");
}
}
}
Voici une autre solution (n'oubliez pas 'import java.util.Collections').
ArrayList<String> test = new ArrayList<String>();
test.add("1");
test.add("2");
test.add("3");
test.add("4");
test.add("5");
Collections.reverse(test);
for(String s : test) {
System.out.print(s + " ");
}
Question 11 #
Vous devez créer une méthode supplémentaire au code de la Question #10, permettant de fusionner deux ArrayList ensemble. Cette méthode prend deux ArrayLists en paramètre et retourne le nouvel ArrayList fusionné.
Réponse
La méthode addAll de la classe ArrayList permet de rapidement faire cette fusion :
import java.util.ArrayList;
public class ArrayListUtils {
public static ArrayList inverser(ArrayList liste) {
ArrayList inverse = new ArrayList();
for(Object o : liste) {
inverse.add(0, o);
}
return inverse;
}
public static ArrayList fusion(ArrayList a, ArrayList b) {
ArrayList liste = (ArrayList) a.clone();
liste.addAll(b);
return liste;
}
public static void main(String[] args) {
ArrayList<String> test = new ArrayList<String>();
test.add("1");
test.add("2");
test.add("3");
test.add("4");
test.add("5");
ArrayList<String> listFusion = ArrayListUtils.fusion(test, test);
for(String s : listFusion) {
System.out.print(s + " ");
}
}
}
Question 12 #
Veuillez écrire le code (classe et méthode(s)) permettant de faire afficher le texte suivant à la console. Vous ne pouvez utiliser qu'une seule fois la méthode System.out.println.
###?###?###?###?###?###?###?###
???#???#???#???#???#???#???#???
??###?###?###?###?###?###?###??
?????#???#???#???#???#???#?????
????###?###?###?###?###?###????
???????#???#???#???#???#???????
??????###?###?###?###?###??????
?????????#???#???#???#?????????
????????###?###?###?###????????
???????????#???#???#???#???????
??????????###?###?###??????????
?????????????#???#?????????????
????????????###?###????????????
???????????????#???????????????
??????????????###??????????????
Réponse
import java.util.ArrayList;
// solution par Francis Beauchemin-Côté
public class Main {
public static String trioMotif(String caractere1, String caractere2, int nombreCaractere1) {
String motif = "";
for (int i = 1; i <= nombreCaractere1; i++) { // On veut mettre le nombre de trio demandé.
motif += caractere1 + caractere1 + caractere1; // Le premier argument est un String pour ne pas faire la somme en
// int (si un char avait été utilisé). Donc + est une
// concaténation.
if (i != nombreCaractere1) { // On n’ajoute pas de motif de séparation après le dernier trio.
motif += caractere2; // Comme motif est un String, la somme avec un char devient en fait une
// concaténation. On utilise ici un String dans le but d'uniformiser et
// simplifier l'utilisation.
}
}
return motif;
}
public static String nbInter(String carac, int nombreInterrogation) { // Crée le String entourant
// les motifs de trio.
String chaine = "";
for (int i = 1; i <= nombreInterrogation; i++) {
chaine += carac;
}
return chaine;
}
public static void pyramide(String premierChar, String deuxiemeChar, int nombreDeLignes) {
ArrayList < String > texte = new ArrayList < String > (); // Notre texte sera un vecteur de String.
for (int i = 0; i < (nombreDeLignes + 1) / 2; i++) { // Comme c'est une division entière, la division ne prend que
// le quotient entier.
texte.add(nbInter(deuxiemeChar, 2 * i) + trioMotif(premierChar, deuxiemeChar, (nombreDeLignes + 1) / 2 - i) + nbInter(deuxiemeChar, 2 * i));
texte.add(nbInter(deuxiemeChar, 2 * i) + trioMotif(deuxiemeChar, premierChar, (nombreDeLignes + 1) / 2 - i) + nbInter(deuxiemeChar, 2 * i));
} // On imprime deux lignes à la fois! On ajuste le nombreDeLignes par + 1. Il y a
// un nombre pair de motifs qui entourent les motifs avec les trios
if (nombreDeLignes % 2 == 1) { // Comme on imprime un nombre pair de lignes, il faut retirer
// la dernière dans le cas où le nombre de lignes est impair.
texte.remove(nombreDeLignes); // Le rang du n+1 ième élément est n.
}
for (String e: texte) { // Affiche le texte à l'écran ligne par ligne.
System.out.println(e);
}
}
public static void main(String[] args) {
pyramide("#", "?", 100);
}
}
Question 13 #
Écrivez un programme efficace Java qui prend la chaîne de caractère « 124213 » et en extrait les nombres 1, 2, 4, 2, 1, 3.
Réponse
class Exemple {
public static int[] extrait(String s) {
int[] answer = new int[s.length()];
for(int index = 0; index < s.length(); index++) {
answer[index] = Character.digit(s.charAt(index), 10);
}
return answer;
}
public static void main(String[] args) {
for(int x: extrait("124213")) {
System.out.println(x);
}
}
}
Question 14 #
Qu’est-ce qui s’affiche à l’exécution du bout de code suivant :
public class Main {
public static void main(String args[]) {
if (true)
if (false)
if (true)
System.out.println("avant le premier else");
else
System.out.println("après le premier else");
if (true)
System.out.println("avant le second else");
else
System.out.println("après le second else");
}
}
Expliquer pourquoi.
Réponse
Le code affiche « avant le second else ».
En effet le second if est évalué à false, et toute l’instruction qui suit (y compris l’évaluation du else qui se rapporte au if de cette instruction) n’est pas exécutée ; l’instruction suivante c’est le tout dernier if, qui est évalué à true, et donc le else qui s’y rapporte n’est pas évalué.
Question 15 #
Soit les deux bouts de code suivants :
a)
for (int j=0; j < 10; j++) {
if (j>=5) {
continue;
}
Instruction
}
b)
for (int j=0; j < 10;j++) {
if (j>=5) {
break;
}
Instruction
}
Que se passe-t-il dans chacun des cas quant à l’exécution de Instruction quand j vaut 5?
Réponse
En a) Instruction n’est pas exécutée et on passe au tour suivant.
En b) on sort de la boucle, Instruction ne sera plus jamais exécutée.
Question 16 #
Soit les deux bouts de code suivants :
a)
label:
for (int i=0; i < 10;i++) {
for (int j=0; j < 6; j++) {
if (condition) {
continue label;
}
Instruction1
}
Instruction2
}
b)
for (int i=0; i < 10; i++) {
for(int j=0; j < 6; j++) {
if(condition) {
continue ;
}
Instruction1
}
Instruction2
}
Dans quel cas (quel bout de code et quelle condition) Instruction1 n'est pas exécuté? Y'a-t-il une situation où Instruction2 n'est pas exécuté? Si oui laquelle?
Réponse
Instruction1 n’est pas exécuté, dans tous les cas lorsque condition est vraie.
Instruction2 est toujours exécuté.
Question 17 #
Soit le code suivant :
public class Main {
public static void main(String[] args) {
final int NBRE = 10;
int i;
double harmonique;
harmonique = 0;
System.out.println("somme des " + NBRE + " premiers termes de la série harmonique");
for (i = 0; i < NBRE; i++) {
harmonique += (double)1/(i+1);
}
System.out.println("Somme : " + harmonique);
}
}
Réécrire le même code en utilisant les syntaxes de l’instruction while et de l’instruction do…while.
Réponse
Instruction while.
public class Main {
public static void main(String[] args) {
final int NBRE = 10;
int i=0;
double harmonique;
harmonique = 0;
System.out.println("somme des " + NBRE + " premiers termes de la série harmonique");
while (i < NBRE) {
harmonique += (double)1/(i+1);
i++;
}
System.out.println("Somme : " + harmonique);
}
}
Instruction do…while
public class Main {
public static void main(String[] args) {
final int NBRE = 10;
int i = 0;
double harmonique;
harmonique = 0;
System.out.println("somme des " + NBRE + " premiers termes de la série harmonique");
do {
harmonique += (double)1/(i+1);
i++;
} while (i < NBRE);
System.out.println("Somme : " + harmonique);
}
}
Question 18 #
Soit le code suivant :
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
System.out.println("Donnez un nombre entier");
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
if (n>=10) {
System.out.println("Le nombre vaut 10");
} else if (n >= 100) {
System.out.println("Le nombre vaut 100");
} else if (n >= 1000) {
System.out.println("Le nombre vaut 1000");
} else {
System.out.println("Le nombre n'est pas une puissance de 10 ou il est plus grand que 1000");
}
}
}
Réécrire le même programme avec l'instruction switch.
Réponse
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
System.out.println("Donnez un nombre entier");
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
switch(n)
{
case 10 :
System.out.println("Le nombre vaut 10");
break;
case 100 :
System.out.println("Le nombre vaut 100");
break;
case 1000 :
System.out.println("Le nombre vaut 1000");
break;
default:
System.out.println("Le nombre n'est pas une puissance de 10 ou il est plus grand que 1000");
break;
}
}
}
Question 19 #
Étant donné le tableau String[] x = new String[10], que vaut x[0].
Réponse
En Java, les tableaux sont initialisées à une valeur nulle par défaut. Dans ce cas, x[0] prendra donc la valeur null. Cette valeur spéciale ne correspond pas à une instance de la classe String. On peut, par contre, assigner une valeur (x[0]="123").
Question 20 #
Énumérer toutes les paires d'entiers entre 0 et 1000 en ordre lexicographique.
Réponse
public class Liste {
public static void main(String[] args) {
for(int i = 0; i <= 1000; i++) {
for(int j = 0; j <= 1000; j++) {
System.out.println(i+" "+j);
}
}
}
}
Question 21 #
Écrivez un programme Java qui trie le tableau de chaînes de caractères suivant : {"Pomme", "banane", "Cerise", "abricot", "Datte"}, en ignorant la casse (par exemple, "Pomme" et "pomme" doivent être traités de manière identique). Utilisez la méthode Arrays.sort avec une expression lambda pour effectuer un tri alphabétique insensible à la casse.
Une expression lambda est une fonctionnalité introduite dans Java 8 qui permet d'écrire du code plus concis pour représenter des fonctions anonymes. Elle est particulièrement utile lorsqu'une méthode attend une interface fonctionnelle (une interface avec une seule méthode abstraite). Dans le contexte du tri avec Arrays.sort, la lambda remplace une implémentation explicite d'un Comparator.Une lambda a la forme générale suivante : (paramètres) -> expression La lambda (s1, s2) -> s1.compareToIgnoreCase(s2) signifie : s1 et s2 sont deux chaînes de caractères à comparer, la méthode compareToIgnoreCase est appelée pour déterminer l'ordre alphabétique sans tenir compte de la casse, le résultat de cette comparaison est retourné directement.
Réponse
Question 22 #
Écrivez un programme Java qui affiche la somme des éléments d’un tableau d’entiers donné.
Réponse
int[] t = {1, 2, 3, 4, 5};
int somme = 0;
for (int v : t) {
somme += v;
}
System.out.println("Somme : " + somme);
Question 23 #
Expliquez la différence entre une boucle for et une boucle while en Java, et donnez un exemple pour chacune.
Réponse
La boucle for est généralement utilisée quand on connaît le nombre d’itérations à l’avance ; la boucle while est utilisée quand on ne le connaît pas.
// Boucle for
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
// Boucle while
int j = 0;
while (j < 5) {
System.out.println(j);
j++;
}
Question 24 #
Quelle est la différence entre un tableau et une ArrayList en Java ?
Réponse
Un tableau a une taille fixe et ne peut pas être redimensionné après sa création. Une ArrayList est une structure de données dynamique qui peut changer de taille et offre des méthodes pratiques pour ajouter, supprimer ou rechercher des éléments.
Question 25 #
Écrivez un programme Java qui affiche tous les éléments d’un tableau d’entiers dans l’ordre inverse.
Réponse
int[] t = {1, 2, 3, 4, 5};
for (int i = t.length - 1; i >= 0; i--) {
System.out.println(t[i]);
}
Question 26 #
Qu'est-ce qu'une HashMap en Java ? Donnez un exemple simple d'utilisation pour associer des noms d'étudiants à leurs notes.
Réponse
Une HashMap est une structure de données qui associe des clés à des valeurs. Elle permet de retrouver rapidement une valeur à partir de sa clé. Exemple :
import java.util.HashMap;
HashMap<String, Integer> notes = new HashMap<>();
notes.put("Alice", 85);
notes.put("Bob", 92);
System.out.println(notes.get("Alice")); // Affiche 85
Question 27 #
Que se passe-t-il si on ajoute deux fois la même clé dans une HashMap en Java ? Par exemple :
HashMap<String, Integer> map = new HashMap<>();
map.put("clé", 1);
map.put("clé", 2);
Quelle sera la valeur associée à la clé “clé” ?
Réponse
La deuxième insertion écrase la première : la valeur associée à la clé "clé" sera 2. Une HashMap ne peut contenir qu'une seule valeur par clé ; si on ajoute une clé déjà existante, la nouvelle valeur remplace l'ancienne.
Question 28 #
Comment utiliser une Stack en Java pour inverser l'ordre des éléments d'une liste ? Donnez un exemple de code qui empile les éléments d'un tableau d'entiers, puis les dépile pour les afficher dans l'ordre inverse.
Réponse
On peut utiliser une Stack pour inverser l'ordre des éléments : on empile chaque élément, puis on les dépile un à un (LIFO : Last In, First Out).
import java.util.Stack;
int[] t = {1, 2, 3, 4, 5};
Stack<Integer> pile = new Stack<>();
for (int x : t) {
pile.push(x);
}
while (!pile.isEmpty()) {
System.out.println(pile.pop());
}
Question 29 #
Qu’est-ce que l’interface CharSequence en Java ? Donnez un exemple d’utilisation de la méthode subSequence() sur une chaîne de caractères.
Réponse
L’interface CharSequence représente une séquence de caractères et est implémentée par String, StringBuilder, etc. La méthode subSequence(start, end) permet d’obtenir une sous-séquence de caractères.
Exemple :
String texte = "Bonjour le monde";
CharSequence sousTexte = texte.subSequence(8, 14); // "le mon"
System.out.println(sousTexte);
On peut ainsi éviter de faire des copies.
Question 30 #
Pourquoi est-il préférable d’utiliser StringBuilder plutôt que l’opérateur + pour concaténer des chaînes dans une boucle ? Donnez un exemple.
Réponse
StringBuilder permet de modifier une chaîne sans créer de nouveaux objets à chaque opération, ce qui améliore la performance, surtout dans les boucles. L’opérateur + de la classe String peut créer une nouvelle chaîne à chaque concaténation, ce qui est coûteux en mémoire et en temps.
Exemple :
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 5; i++) {
sb.append("Ligne ").append(i).append("\n");
}
System.out.println(sb.toString());
Question 31 #
Quels sont les types et classes immuables en Java ? Donnez quelques exemples et expliquez pourquoi l’immuabilité peut être utile.
Réponse
Un type ou une classe est dit immuable si son état ne peut pas être modifié après sa création. En Java, les principaux types et classes immuables sont :
String- Les classes enveloppes des types primitifs :
Integer,Double,Boolean,Long,Short,Byte,Character,Float
L’immuabilité facilite la programmation (pas de risque de modification imprévue), rend les objets sûrs à partager, et simplifie le raisonnement sur le code.
Question 32 #
Considérez le code suivant :
public class Main {
public static void main(String[] args) {
String str1 = "Java";
String str2 = "Java";
String str3 = new String("Java");
System.out.println(str1 == str2);
System.out.println(str1 == str3);
}
}
Qu’affichera ce programme ? Expliquez pourquoi.
Réponse
Le premier affichage (str1 == str2) retournera true car les deux variables pointent vers la même chaîne littérale dans le pool de chaînes de Java. Le second (str1 == str3) retournera false car str3 est un nouvel objet créé explicitement, différent de la chaîne littérale, même si leur contenu est identique. Pour comparer le contenu des chaînes, il faut utiliser equals() :
System.out.println(str1.equals(str3)); // true
En Java, les chaînes de caractères sont des objets particuliers : les chaînes littérales (comme “Java”) sont stockées dans une zone spéciale appelée « pool de chaînes ». Quand on écrit deux fois la même chaîne littérale dans le code, Java réutilise le même objet pour économiser de la mémoire. C’est pourquoi str1 == str2 retourne true : les deux variables pointent vers le même objet en mémoire.
En revanche, lorsque l’on crée une chaîne avec new String(“Java”), Java crée explicitement un nouvel objet, même si le contenu est identique à une chaîne littérale existante. Ainsi, str1 == str3 retourne false car les deux variables pointent vers des objets différents.
Pour comparer le contenu de deux chaînes (et non leur emplacement en mémoire), il faut utiliser la méthode equals(). Cette méthode vérifie caractère par caractère si les deux chaînes ont le même contenu, ce qui est généralement ce que l’on souhaite en pratique.
Question 33 #
Nous savons que la somme 1/2 + 1/4 + 1/8 +... donne la valeur unitaire.
Sachant qu'un type double en Java a une mantisse comportant 53 bits, que va afficher le programme suivant?
class Main {
public static void main(String[] args) {
double x = 0;
double f = 1.0;
for (int i = 0; i < 53; i++) {
f /= 2;
x += f;
}
System.out.println(x==1.0);
f /= 2;
x += f;
System.out.println(x==1.0);
}
}
Réponse
false, true
Question 34 #
Que va afficher le programme suivant ? La chaîne contient un émoji.
public class Main {
public static void main(String[] args) {
String s = "héllo 😀";
System.out.println(s.length());
System.out.println((int) s.charAt(6));
System.out.println(s.codePointAt(6));
System.out.println(s.codePointCount(0, s.length()));
}
}
Réponse
Le programme affiche 8, 55357, 128512 puis 7.
En Java, une String est une suite d'unités de code UTF-16, c'est-à-dire de valeurs de type char sur 16 bits. Les six premiers caractères (h, é, l, l, o et l'espace) occupent chacun un seul char, mais l'émoji 😀 (point de code U+1F600) n'entre pas dans 16 bits : il est représenté par une paire de substitution, soit deux char. La longueur est donc 6 + 2 = 8, alors que la chaîne ne contient que 7 caractères au sens de l'utilisateur.
charAt(6) retourne le premier élément de la paire (55357, soit U+D83D) : ce n'est pas un caractère utilisable seul. Pour obtenir le vrai point de code, il faut codePointAt(6), qui retourne 128512 (soit U+1F600). Enfin, codePointCount(0, s.length()) compte les points de code plutôt que les char et retourne 7.
Retenez que length() ne donne pas toujours le nombre de caractères perçus, et qu'une boucle for (int i = 0; i < s.length(); i++) s.charAt(i) peut couper un émoji en deux.
Question 35 #
Soit String s = "Bonjour";. Que retournent les appels suivants ? L'un d'eux pose problème : lequel et pourquoi ?
s.substring(3)
s.substring(2, 5)
s.substring(7)
s.substring(8)
Réponse
s.substring(3)retourne"jour": tout ce qui suit l'indice 3, celui-ci compris.s.substring(2, 5)retourne"njo": les indices 2, 3 et 4. La borne de gauche est incluse, celle de droite est exclue. La longueur du résultat est donc simplement 5 - 2 = 3.s.substring(7)retourne la chaîne vide"". Ce n'est pas une erreur : demander la sous-chaîne qui commence juste après le dernier caractère est permis.s.substring(8)lève une exceptionStringIndexOutOfBoundsException, car 8 dépasse la longueur de la chaîne (7).
Notez que substring copie les caractères depuis Java 7 : son coût est proportionnel à la longueur de la sous-chaîne extraite, et non constant.
Question 36 #
Écrivez une méthode qui compte le nombre d'occurrences d'une sous-chaîne dans un texte, en utilisant indexOf. Combien d'occurrences de "aa" votre méthode trouve-t-elle dans "aaaa" ?
Réponse
L'idée est de relancer la recherche à partir de la position qui suit l'occurrence trouvée, grâce à la variante indexOf(String, int depart).
static int compter(String texte, String motif) {
int compte = 0;
int position = texte.indexOf(motif);
while (position != -1) {
compte++;
position = texte.indexOf(motif, position + motif.length());
}
return compte;
}
Avec compter("aaaa", "aa"), cette version retourne 2 : elle trouve l'occurrence en 0, puis repart de l'indice 2 et trouve celle en 2. Elle compte donc les occurrences sans chevauchement.
Si l'on souhaite plutôt compter les occurrences qui peuvent se chevaucher, il suffit de repartir de position + 1 :
position = texte.indexOf(motif, position + 1);
Cette seconde version retourne 3 pour "aaaa" : les occurrences commencent aux indices 0, 1 et 2. Il n'y a pas de bonne réponse dans l'absolu : il faut savoir laquelle des deux on veut. Attention aussi au cas où motif est la chaîne vide : la première version boucle indéfiniment, puisque motif.length() vaut alors 0.
Question 37 #
Que retournent les expressions suivantes ? Il n'est pas demandé de donner la valeur exacte pour chacune, mais bien son signe et sa signification.
"pomme".compareTo("poire")
"Zèbre".compareTo("abeille")
"abc".compareTo("abcd")
Ensuite, qu'affiche ce programme ?
String[] mots = {"banane", "Ananas", "cerise", "Abricot"};
java.util.Arrays.sort(mots);
System.out.println(java.util.Arrays.toString(mots));
Réponse
"pomme".compareTo("poire")retourne 4, une valeur positive : les deux chaînes commencent parpo, puis on compare'm'(109) et'i'(105), d'où 109 - 105 = 4. La chaîne"pomme"vient donc après"poire"."Zèbre".compareTo("abeille")retourne -7, une valeur négative : on compare'Z'(90) et'a'(97). Les majuscules ont un code inférieur à celui des minuscules, donc"Zèbre"vient avant"abeille"."abc".compareTo("abcd")retourne -1 : quand une chaîne est le préfixe de l'autre, la méthode retourne la différence des longueurs, ici 3 - 4.
Le programme affiche [Abricot, Ananas, banane, cerise]. Ce n'est pas l'ordre du dictionnaire : compareTo compare les valeurs Unicode des caractères, si bien que toutes les majuscules précèdent toutes les minuscules. Les accents posent le même genre de problème. Pour un tri réellement alphabétique en français, il faut utiliser un Collator ou, plus simplement, trier avec String.CASE_INSENSITIVE_ORDER :
java.util.Arrays.sort(mots, String.CASE_INSENSITIVE_ORDER);
Question 38 #
Que va afficher le programme suivant ?
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
String[] t = "a,b,,c,,".split(",");
System.out.println(t.length);
System.out.println(Arrays.toString(t));
}
}
Réponse
Le programme affiche 4 puis [a, b, , c].
On pourrait s'attendre à six éléments, puisque la chaîne contient cinq virgules. Mais split supprime les chaînes vides en fin de tableau. La chaîne vide entre b et c est conservée, car elle n'est pas en fin de tableau, mais les deux dernières disparaissent.
Pour conserver tous les champs, il faut passer une limite négative :
String[] t = "a,b,,c,,".split(",", -1);
System.out.println(t.length); // 6
System.out.println(Arrays.toString(t)); // [a, b, , c, , ]
C'est un piège classique lorsqu'on lit un fichier au format CSV : les colonnes vides de la fin d'une ligne disparaissent silencieusement.
Question 39 #
Que va afficher le programme suivant ?
public class Main {
public static void main(String[] args) {
String s = "3.14.15";
System.out.println(s.replace(".", "-"));
System.out.println(s.replaceAll(".", "-"));
System.out.println(s.replaceAll("\\.", "-"));
}
}
Réponse
Le programme affiche :
3-14-15
-------
3-14-15
La différence est essentielle : replace travaille sur du texte littéral, alors que replaceAll attend une expression régulière. Or, dans une expression régulière, le point signifie « n'importe quel caractère ». Le deuxième appel remplace donc chacun des sept caractères de la chaîne par un tiret.
Pour désigner un vrai point dans une expression régulière, il faut le protéger avec une barre oblique inverse, qui doit elle-même être doublée dans une chaîne Java : on écrit "\\.".
En pratique, si vous n'avez pas besoin d'une expression régulière, préférez replace : c'est plus simple, plus rapide, et cela évite ce genre de surprise.
Question 40 #
Que va afficher le programme suivant ? La séquence d'échappement \u2003 désigne un « cadratin », une espace typographique large.
public class Main {
public static void main(String[] args) {
String a = " bonjour ";
String b = "\u2003bonjour\u2003";
System.out.println("[" + a.trim() + "]");
System.out.println(b.trim().equals(b));
System.out.println(b.strip().length());
System.out.println(" ".isEmpty());
System.out.println(" ".isBlank());
}
}
Réponse
Le programme affiche [bonjour], true, 7, false puis true.
trim()ne retire que les caractères dont le code est inférieur ou égal à celui de l'espace ordinaire (32). Il nettoie donca, mais laissebintact :b.trim().equals(b)vauttrue.strip(), ajouté en Java 11, reconnaît les espaces au sens d'Unicode. Il retire les cadratins etb.strip()vaut"bonjour", de longueur 7.isEmpty()ne teste que la longueur : une chaîne de trois espaces n'est pas vide, doncfalse.isBlank(), également ajouté en Java 11, teste si la chaîne est vide ou ne contient que des espaces : il retournetrue.
Il faut donc préférer strip() à trim() dès que le texte peut provenir de l'extérieur (page web, fichier, saisie de l'utilisateur).
Question 41 #
Écrivez une méthode estPalindrome(String s) qui indique si une phrase se lit de la même façon dans les deux sens, en ignorant la casse, les espaces et la ponctuation. Testez-la sur « Engage le jeu que je le gagne ».
Réponse
On commence par nettoyer la chaîne, puis on la parcourt avec deux indices qui se rapprochent l'un de l'autre.
Le programme affiche true, false puis... false, ce qui est surprenant, car « Ésope reste ici et se repose » est un palindrome célèbre. L'explication tient à l'accent : notre filtre [^a-zA-Z0-9] supprime le É, qui n'est pas dans l'intervalle a-z. La chaîne nettoyée devient "soperesteicietserepose", qui n'est effectivement plus un palindrome.
Une solution consiste à conserver toutes les lettres au sens d'Unicode, avec Character.isLetterOrDigit, et à retirer les accents. C'est un bon rappel : dès qu'on traite du texte en français, les intervalles comme a-z sont insuffisants.
Notez enfin qu'il aurait été plus court d'écrire propre.equals(new StringBuilder(propre).reverse().toString()), mais cette version alloue une chaîne supplémentaire, alors que la boucle ci-dessus n'en alloue aucune et s'arrête dès le premier désaccord.
Question 42 #
On dispose d'un tableau de noms de villes et d'un tableau de populations. Produisez, en une seule chaîne, un tableau aligné où chaque ville occupe une ligne. Utilisez String.format et String.join.
String[] noms = {"Montréal", "Québec", "Trois-Rivières"};
int[] population = {1762949, 549459, 139163};
Réponse
String[] lignes = new String[noms.length];
for (int i = 0; i < noms.length; i++) {
lignes[i] = String.format("%-16s %8d", noms[i], population[i]);
}
System.out.println(String.join("\n", lignes));
Le résultat est :
Montréal 1762949
Québec 549459
Trois-Rivières 139163
Dans String.format, %-16s insère une chaîne sur 16 colonnes, alignée à gauche à cause du signe moins ; %8d insère un entier sur 8 colonnes, aligné à droite par défaut. La méthode String.join assemble ensuite les lignes en insérant un saut de ligne entre elles, en une seule allocation.
On aurait pu concaténer les lignes avec + dans la boucle, mais cela crée une nouvelle chaîne à chaque tour. Avec String.join — ou avec un StringBuilder — on ne construit la chaîne finale qu'une seule fois.
Question 43 (pour les experts !) #
Considérons la décroissance radioactive d’un isotope. Supposons que nous ayons une quantité initiale de matière radioactive, et nous voulons modéliser comment sa masse diminue avec le temps en raison de la désintégration. Ce phénomène est courant en physique nucléaire, en médecine (pour les traitements par radiothérapie) et en datation au carbone.
La décroissance radioactive suit une loi où la vitesse de diminution de la masse \( N \) (nombre de particules ou masse restante) est proportionnelle à la masse actuelle. Mathématiquement, cela s’exprime comme :
\[ \frac{dN}{dt} = -\lambda N \]- \( N(t) \) : masse (ou nombre de particules) restante à l’instant \( t \).
- \( \lambda \) : constante de désintégration (spécifique à l’isotope, en \( \text{s}^{-1} \)).
- Le signe négatif indique que la masse diminue.
Cette équation différentielle décrit un taux de changement proportionnel à la quantité présente. Par exemple, pour le carbone-14, \( \lambda \approx 3.839 \times 10^{-12} \, \text{s}^{-1} \), mais pour simplifier, nous utiliserons une constante plus grande, comme \( \lambda = 0.1 \, \text{s}^{-1} \), pour observer une décroissance significative sur un intervalle court.
- Condition initiale : À \( t = 0 \), la masse initiale est \( N_0 = 100 \, \text{g} \).
- Intervalle : Nous modélisons sur \( t \) de 0 à 20 secondes.
- Méthode : Nous utiliserons la méthode d’Euler pour résoudre numériquement cette équation. La méthode d’Euler est une technique numérique simple pour résoudre des équations différentielles ordinaires en approximant la solution par des pas discrets. Elle repose sur l’idée que la dérivée d’une fonction donne la pente locale, permettant d’estimer la valeur suivante à partir de la valeur actuelle et d’un pas de temps \( h \). Pour une équations différentielles ordinaires de la forme \( \frac{dy}{dt} = f(t, y) \), la méthode calcule \( y_{n+1} = y_n + h \cdot f(t_n, y_n) \).
Voici le pseudocode de la méthode d’Euler.
Entrée :
- fonction \( f(x, y) \) (dérivée)
- \( x_0 \), \( y_0 \) (conditions initiales)
- \( x_f \) (valeur finale de \( x \))
- \( h \) (taille du pas)
Sortie :
- liste des points \((x_i, y_i)\) approximant la solution
- Initialiser \( x = x_0 \), \( y = y_0 \)
- Initialiser une liste vide pour stocker les points \((x_i, y_i)\)
- Ajouter le point \((x, y)\) à la liste
- Tant que \( x < x_f \):
- Calculer la pente : \( m = f(x, y) \)
- Mettre à jour \( y \): \( y = y + h \cdot m \)
- Mettre à jour \( x \): \( x = x + h \)
- Ajouter le nouveau point \((x, y)\) à la liste
- Retourner la liste des points
Écrivez un programme qui utilise la méthode d’Euler pour résoudre l’équation \( \frac{dN}{dt} = -0.1 N \), avec \( N(0) = 100 \), et affiche la masse restante à chaque pas de temps.
Réponse
Question 44 (pour les experts !) #
Décrivez le fonctionnement de l’algorithme de tri par insertion. Implémentez cet algorithme en Java pour trier un tableau d’entiers en ordre croissant. Votre programme doit afficher le tableau avant et après le tri.
Réponse
L’algorithme de tri par insertion parcourt un tableau élément par élément, en insérant chaque élément à sa position correcte dans la partie déjà triée. Pour chaque élément, il le compare avec les éléments précédents et le déplace vers la gauche jusqu’à ce qu’il soit à sa place.Le programme Java ci-dessus implémente le tri par insertion. La méthode insertionSort prend un tableau d’entiers, utilise une boucle pour parcourir les éléments, et insère chaque élément à la bonne position en décalant les éléments plus grands. Le programme affiche le tableau avant et après le tri.
Question 45 (pour les experts !) #
Décrivez le fonctionnement de l’algorithme de tri à bulle. Implémentez cet algorithme en Java pour trier un tableau d’entiers en ordre croissant. Votre programme doit afficher le tableau avant et après le tri.
Réponse
Le tri à bulle parcourt répétitivement un tableau, comparant des éléments adjacents et les échangeant s’ils sont dans le mauvais ordre. À chaque itération, l’élément le plus grand de la portion non triée est placé à la fin, comme une bulle qui remonte.Le programme Java ci-dessus implémente le tri à bulle. La méthode bubbleSort utilise deux boucles imbriquées pour comparer et échanger les éléments adjacents. Le programme affiche le tableau avant et après le tri.
Question 46 (pour les experts !) #
Décrivez le principe de la recherche binaire dans un tableau trié. Implémentez cet algorithme en Java pour trouver l’indice d’un élément dans un tableau d’entiers trié en ordre croissant. Votre programme doit retourner l’indice de l’élément s’il est trouvé, ou -1 sinon, et afficher un message indiquant le résultat.
Réponse
La recherche binaire trouve un élément dans un tableau trié en divisant itérativement l’intervalle de recherche par deux. Elle compare l’élément cible avec l’élément central, puis restreint la recherche à la moitié inférieure ou supérieure selon le résultat. Le tableau doit être trié au préalable.Le programme Java implémente la recherche binaire. La méthode binarySearch prend un tableau trié et un élément cible, utilise une boucle pour ajuster les indices debut et fin, et retourne l’indice de l’élément ou -1 s’il n’est pas trouvé. Le programme affiche un message indiquant le résultat.