TP sur les tableaux

Page Home (contact) Retour TPs

Support de cours


Pour se chauffer...

Dans cet exercice, utilisez une boucle "for-each" à chaque fois que vous le pouvez.

Écrivez une méthode main d'une classe Main qui

  1. Initialise les valeurs d'un tableau de nombres entiers, de taille 10, avec des valeurs aléatoires comprises entre 1 et 6. Aide.
  2. Ensuite (après avoir initialisé tout le tableau), parcourt le tableau à partir du début et jusqu'au premier 6 (au premier 6 on sort de la boucle). Tous les nombres rencontrés qui sont inférieurs à 3 sont additionnés.
  3. Finalement, affiche les valeurs du tableau sur une seule ligne (utilisez une boucle for pour cela) et sur la ligne suivante la somme des nombres inférieurs à 3 rencontrés (calculée à l'étape 2).

Écrivez un code similaire, mais cette fois-ci en utilisant des méthodes de la classe Main :

Correction :

Classe Main, version 1
Classe Main, version 2

Codes correcteurs, tableaux d'octets

Cet exercice fait manipuler des tableaux. Il introduit la notion de code correcteur.

Un code correcteur code des données de telle sorte que des erreurs dans la transmission des données (transmission de bits 0 ou 1) pourront être automatiquement détectées, et éventuellement corrigées, par le receveur. Un code correcteur est nécessairement redondant : il ajoute des informations aux données intiales pour pouvoir détecter et corriger les erreurs de transmission.

Lorsqu'on utilise un code correcteur, les données à transmettre sont envoyées dans des blocs. Un bloc est constitué des bits à transmettre + les bits ajoutées par le code correcteur pour détecter les erreurs de transmissions.

Dans chaque bloc, si k est la taille des données à transmettre et n la taille du bloc (donc n - k bits ont été ajoutés par le code correcteur), le rendement du code est k / n. Le rendement ne peut dépasser 1 ; plus il est proche de 1, mieux c'est.

Un exemple très simple de codage : bit de parité

Par exemple on peut coder les octets (8 bits ; k=8) en leur ajoutant un bit "de parité" (n=9, 8 + 1 bit de parité) : si le total des 1 est pair le bit de parité est 0, sinon il est égal à 1.

Par exemple, l'octet "11100111" est transmis après avoir été codé en "111001110" ; le bit de parité est 0 car il y a 6 "1". Si le destinataire reçoit la valeur "111001010" il saura qu'il y a eu une erreur de transmission car le bit de parité est 0 alors qu'il y a 5 "1".

Le rendement pour ce codage est égal à 8/9. Il est bon mais, en fait, ce codage ne peut pas détecter des erreurs trop importantes ; en effet, si le destinataire avait reçu "111011010" (avec 2 erreurs), il n'aurait pas pu savoir que la donnée reçue était fausse.

De plus ce code ne permet pas de corriger les erreurs pour rétablir la bonne valeur.

Dans cet exercice vous allez étudier un autre code qui détecte plus d'erreurs et permet de corriger certaines erreurs, mais au prix d'une plus grande redondance.

Qualité d'un code correcteur

Pour juger de la qualité d'un code correcteur, 3 nombres sont importants ; le rendement (k / n) et les 2 nombres suivants :

Le meilleur code correcteur sera celui qui aura le meilleur rendement avec des nombres n1 et n2 les plus grands possibles. Pour trouver les meilleurs codes correcteurs, la théorie des codes correcteurs nécessite des connaissances mathématiques solides qui permettent de corriger le plus d'erreurs possible sans ajouter trop de redondance.

Vous allez utiliser dans cet exercice un code correcteur simpliste qui ne demande aucune connaissance particulière en mathématique mais qui permet tout de même de comprendre la notion. Pour les curieux un peu mathématiciens, un cours en français (la version PDF) sur les codes correcteurs (la version HTML) et une référence Wikipédia.

Le code correcteur que vous allez utiliser

Ce code travaille sur des blocs de données de longueur n = 24 bits (3 octets ; un octet est représenté en Java par le type primitif byte). Le code correcteur est le suivant : un bloc de données est composé d'un octet initial (k = 8 bits) qu'on veut transmettre, et de 2 copies de cet octet; chaque octet est donc envoyé en triple exemplaire.

Les 2 copies seront utilisées pour tenter de détecter des erreurs de transmissions, et si possible les corriger : celui qui reçoit les données compare les 3 exemplaires. Si les 3 octets sont identiques il suppose qu'il n'y a pas eu d'erreur de transmission. Sinon, si au moins 2 exemplaires sont identiques il suppose que la valeur envoyée est donnée par ces 2 exemplaires. Sinon, il ne peut rien dire et il choisit un des 3 octets (le 1er) comme valeur supposée. Des exemples concrets sont donnés dans le lien de la question 1. suivante.

  1. Ce lien donne le rendement (=1/3) et les 2 nombres n1 (=2) et n2 (=1) décrits ci-dessus pour ce code. La réponse tient compte de la pire situation ; par exemple, si le nombre d'erreurs est supérieur à n1, on n'est pas certain à 100 % de pouvoir détecter une erreur.
    Dans beaucoup de cas, cependant, on ne tombera pas sur la pire situation et ce code permettra de détecter et de corriger davantage d'erreurs que ne le laisseraient penser les nombres n1 et n2. Un exemple est donné dans le lien.
  2. Écrivez une classe Bloc qui représente un bloc composé de 3 octets (le bloc qui sera transmis) conservés dans un tableau de byte. Cette classe comprendra :
  3. Testez avec une classe TestBloc : créez un bloc et affichez-le puis simulez une erreur de transmission dans le bloc et affichez à nouveau le bloc.
  4. Complétez ensuite la classe Bloc en ajoutant
  5. Testez votre code avec cette classe TestCode. L'exécution devra ressembler à ceci :
    Valeurs à transmettre :
    [1, 2, 3, 4, 5, 6]
    Blocs avant la transmission :
    Bloc [data=[1, 1, 1]]
    Bloc [data=[2, 2, 2]]
    Bloc [data=[3, 3, 3]]
    Bloc [data=[4, 4, 4]]
    Bloc [data=[5, 5, 5]]
    Bloc [data=[6, 6, 6]]
    Blocs après la transmission :
    Bloc [data=[1, 1, 1]]
    Bloc [data=[25, 2, 2]]
    Bloc [data=[3, 3, 3]]
    Bloc [data=[44, 55, 66]]
    Bloc [data=[5, 45, 45]]
    Bloc [data=[58, 58, 58]]
    Valeurs décodées
    [1, 2, 3, 44, 45, 58]
    
  6. (Optionnel : ne faites cette question qu'après avoir fait les 2 exercices suivants sur les étagères et qu'il vous reste du temps). Améliorez les informations données par le décodage avec une classe BlocAvecRapportErreurs. Cette classe ressemble à la classe Bloc mais contient en plus une variable d'instance dans laquelle on peut ranger ce qui s'est passé au moment du décodage. Le type de la variable sera une énumération nommée TypeErreur pour les différents cas qui peuvent arriver lors du décodage :
  7. Ajoutez un accesseur pour cette variable afin de savoir si le décodage a corrigé des erreurs de transmission.
    Testez votre code avec cette classe TestBlocAvecRapportErreurs. L'affichage d'une telle méthode main pourrait ressembler à ceci :
    Valeurs à transmettre :
    [1, 2, 3, 4, 5, 6]
    Blocs avant la transmission :
    Bloc [data=[1, 1, 1]]
    Bloc [data=[2, 2, 2]]
    Bloc [data=[3, 3, 3]]
    Bloc [data=[4, 4, 4]]
    Bloc [data=[5, 5, 5]]
    Bloc [data=[6, 6, 6]]
    Blocs après la transmission :
    Bloc [data=[1, 1, 1]]
    Bloc [data=[25, 2, 2]]
    Bloc [data=[3, 3, 3]]
    Bloc [data=[44, 55, 66]]
    Bloc [data=[5, 45, 45]]
    Bloc [data=[58, 58, 58]]
    Valeurs décodées
    [1, 2, 3, 44, 45, 58]
    Bloc 0 : CORRECT
    Bloc 1 : CORRECTION
    Bloc 2 : CORRECT
    Bloc 3 : ERREUR
    Bloc 4 : CORRECTION
    Bloc 5 : CORRECT

Correction :

Classe Bloc sans rapport sur les erreurs
Classe BlocAvecRapportErreurs avec rapport sur les erreurs et utilisation d'une énumération


Etagères de livres, tableaux d'objets

Vous allez créer une classe Etagere pour représenter une étagère qui peut contenir un certain nombre de livres (fixe pour chaque étagère). Vous utiliserez un tableau pour ranger les livres.

Vous pouvez utiliser cette classe Livre minimale.

Pour tester votre code vous utiliserez cette méthode main. Adaptez votre code pour avoir les mêmes noms de méthode.

  1. Commencez par écrire un constructeur qui prend en paramètre le nombre de livres que pourra contenir l'étagère.
  2. Ajoutez des méthodes qui retournent le nombre de livres que peut contenir l'étagère, et le nombre de livres qu'elle contient.
  3. Écrivez une méthode toString(). Elle retournera en particulier une description des livres contenus dans l'étagère (la description est donnée par la méthode toString() de Livre). Vous l'utiliserez en particulier pour tester la méthode ajouter. Vous pouvez utiliser la méthode Arrays.toString() vue dans le cours ou bien parcourir les livres de l'étagère.
  4. Écrivez une méthode qui ajoute des livres ("void ajouter(Livre)"). Vous ajouterez les livres à la suite des livres déjà ajoutés dans l'étagère ; le premier livre est ajouté au début de l'étagère (au début du tableau). Il devra être impossible d'ajouter des livres dans une étagère pleine : si l'étagère est déjà pleine, la méthode affiche un message d'erreur. Testez tout de suite cette méthode en utilisant la méthode toString() de la question précédente.
  5. Écrivez une méthode qui retourne un livre dont on donne la position sur l'étagère (le livre reste sur l'étagère, on récupère simplement une référence sur le livre). La méthode renverra une instance de Livre. La position du premier livre d'une étagère devra être 1 (et pas 0, bien que le livre soit rangé dans la première position du tableau, qui est d'indice 0). La signature de la méthode sera "Livre getLivre(int)".
  6. Écrivez une méthode qui retourne la position d'un livre qui a un titre et un auteur donné en paramètre. La méthode renverra la position du livre dans l'étagère (ou 0 si le livre n'y est pas). Le profil de la méthode sera "int chercher(String, String)". S'il y a plusieurs livres avec le même titre et le même auteur, la méthode retourne celui qui a le plus petit indice.
  7. Testez en utilisant la méthode main donnée au début.

2 questions un peu plus difficiles :

  1. Avoir une fonctionnalité semblable à la précédente, mais la méthode retourne un tableau de positions s'il y a plusieurs livres qui ont ce titre et cet auteur. On aimerait appeler cette méthode "chercher" mais 2 méthodes d'une classe ne peuvent avoir la même signature, même si elles n'ont pas le même type retour. Appelez donc cette méthode chercherLivres. Le tableau aura pour taille le nombre de livres trouvés (0 si aucun livre n'a été trouvé). Si vous avez besoin de faire une copie de tableau, utilisez la méthode System.arraycopy pour voir...
  2. Écrivez aussi une méthode chercherAuteur pour rechercher tous les livres d'un auteur. Cette fois-ci, la méthode retourne un tableau de livres.
  3. Testez en utilisant la méthode main donnée au début, en ajoutant du code pour les 2 questions précédentes.

Correction :

Etagere.java

Paquetage bibliotheque

Support de cours sur les paquetages

  1. Mettez toutes les classes liées aux livres dans un paquetage "eg.ufe.toto.bibliotheque" (vous remplacerez toto par votre nom).
  2. Compilez, testez avec une classe qui n'appartient pas à ce paquetage. Par exemple, créez dans le paquetage "eg.ufe.toto.bibliotheque.test" une classe Librairie avec une méthode main qui créera quelques étagères et y rangera des livres.

Le plus simple pour ne pas vous tromper est de suivre exactement ce qui est dit dans le support de cours sur les paquetages, sur les placements des fichiers et sur les commandes à lancer dans la fenêtre "cmd".

IMPORTANT : à partir de cet exercice toutes les classes que vous allez écrire dans les TPs devront appartenir à un paquetage.

Maintenant que vous savez compiler et exécuter des classes qui appartiennent à des paquetages vous pouvez utiliser l'IDE NetBeans qui sera utilisé pendant les TPs (ou bien un autre IDE si vous y tenez vraiment). Les IDE sont très puissants et facilitent grandement l'écriture d'applications en Java.

Voici une aide pour démarrer avec NetBeans. On vous guide pour résoudre cet exercice avec NetBeans.

Vous devrez vous auto-former à l'IDE NetBeans. Vous n'aurez pas besoin d'être un expert de NetBeans pour faire le travail qui vous sera demandé ; un minimum suffira amplement. Ne passez donc pas trop de temps sur NetBeans ; le plus important est d'apprendre le langage Java et le concepts essentiels de la programmation objet. N'oubliez pas aussi que vous ne pourrez bien comprendre le contenu du cours qu'en passant beaucoup de temps à coder pour résoudre des exercices.

Correction :

Livre.java
Etagere.java
Librairie.java

Modules

Cet exercice est optionnel.

Java 9 a introduit les modules qui regroupent plusieurs paquetages. Le JDK a ainsi été découpé en plusieurs modules. Si vous écrivez un gros projet vous pouvez envisager de le découper en plusieurs modules pour expliciter les dépendances et pour minimiser la taille de votre exécutable.

Ce cours d'introduction n'étudie pas les modules en détails mais cet exercice vous montre comment organiser votre code pour en tenir compte. Les modules ne seront pas utilisés dans la suite du cours.

Les modules sont surtout utiles pour les gros projets. Pour cet exercice vous vous contenterez de reprendre le code du premier exercice de ce TP, en le répartissant dans 2 modules :

De plus, pour vous montrer quelques finesses des modules, le deuxième module utilisera une classe de test du premier module.

Module eg.ufe.codecorrecteur

Vous allez écrire un module eg.ufe.codecorrecteur qui contient 2 paquetages. Chaque paquetage ne contient qu'une seule classe :

Dans module-info.java du module n'exportez que le paquetage de la classe Bloc (n'exportez pas eg.ufe.testcodecorrecteur).

Module testcodecorrecteur

Écrivez un autre module testcodecorrecteur qui ne contient qu'un seul paquetage. Ce paquetage ne contient que le code de la classe eg.ufe.test.TestCode2. Remarque : il aurait mieux valu donner le nom eg.ufe.test à ce module mais on peut choisir n'importe quel nom du moment qu'il est unique dans le projet.

Cette classe TestCode2 utilise la classe TestCode du module eg.ufe.codecorrecteur (placée dans le premier module) pour tester la classe Bloc. C'est simple : la méthode main de cette classe TestCode2 appelle la méthode main de la classe TestCode.

Question : est-ce que vous auriez pu appeler cette classe TestCode au lieu de TestCode2 ?

Dans module-info.java du module indiquez que le module dépend du module ufe.codecorrecteur (dans module-info.java).

Structure des répertoires du projet

Vous n'utiliserez pas un IDE pour cet exercice pour bien comprendre comment sont réparties les classes et comment elles sont compilées et exécutées si on utilise des modules.

Ouvrez donc un terminal de votre système d'exploitation et commencez par créer la structure des répertoires préconisée dans le cours sur les modules. Placez-y ensuite les codes des classes.

Compilation et exécution

Compilez les 2 modules en une seule commande.

Vous comprenez les messages d'erreur ? Corrigez en ajoutant ce qu'il faut dans le module eg.ufe.codecorrecteur.

Lancez l'exécution de la classe eg.ufe.test.TestCode.

Correction :

Fichiers et commandes


Javadoc

Cet exercice est optionnel.

Générez la documentation du paquetage bibliotheque avec l'outil javadoc. Vous pouvez vous aider de ce guide ou d'un article de Doug Lea qui donne des conventions de présentation d'un code Java et quelques recommandations. Ce cours, en français, peut aussi vous être utile.
Ajoutez une page pour votre travail dans votre page Web personnelle (il est temps de vous en créer une si ça n'est pas déjà fait !). Dans cette page, un lien devra conduire à la documentation javadoc. Une présentation simple est suffisante ; n'en faites pas trop.

Essayez d'utiliser le plus grand nombre de possibilités de javadoc (sans que ce soit trop artificiel) ; en particulier, glissez quelques mises en forme HTML. La documentation devra faire le lien avec la documentation des API standard (par exemple, pointer vers la documentation de la classe String) ; voir option -link de la commande javadoc. Important : documentez aussi le paquetage (cherchez comment faire dans les cours indiqués ci-dessus).

Pour cet exercice vous ne devez pas utiliser un IDE pour générer votre javadoc ; utilisez directement la commande javadoc.


Pour ceux qui ont déjà fini...


  1. Faites la question 5 de l'exercice sur les codes correcteurs d'erreurs.
  2. Dans la classe Etagere ajoutez une méthode pour enlever des livres :

Retour TPs