quarta-feira, 20 de junho de 2012

Ordenando dados em Java

Oi Pessoal,

Depois de um longo e tenebroso inverno ;-) voltei a postar aqui para vocês. Estou fazendo um curso que está me tomando um pouco de tempo, mas vamos lá.

Estive precisando de fazer umas ordenações de dados em java recentemente. Vejam que coisa legal esta Interface Comparator.

/*
  Este programa ordena um Vector em ordem decrescente/crescente
  utilizando a Interface Comparator
 
  os dados de entrada são obtidos de um arquivo de texto cujos dados estão
  inseridos um por linha
*/

import java.util.Vector;
import java.util.Collections;
import java.util.Comparator;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.lang.NumberFormatException;

public class ExemploOrdenaVector {

  public static void main(String[] args) {
  
    // cria o vetor
    Vector<Integer> v = new Vector<Integer>();
 
   // entrada de dados a partir do arquivo de texto
   // utilizei a leitura com BufferedReader
   // o loop preenche o vetor com os dados do arquivo
   // utilizo método valueOf para converter a string em inteiro
    String nomeArquivo = "dados.txt";
    try {
      BufferedReader in = new BufferedReader(new FileReader(nomeArquivo));
      String linha = null;
      while ((linha = in.readLine()) != null) {
        try {
          v.add(Integer.valueOf(linha));
        } catch (NumberFormatException e1) {
          // somente não insere
        }
      }
      in.close();
    }
    catch (IOException e) {
      System.out.println( "erro não tratado - somente indicação ");
    } 

    System.out.println("Vetor não ordenado : " + v);
 
    // Para ordenar um vetor devemos utilizar:
    //  static void sort(List list, Comparator c)
    //
    // como queremos na ordem inversa usamos Collections.reverseOrder()  
    Comparator comparator = Collections.reverseOrder();
    Collections.sort(v,comparator);
    System.out.println("Vetor ordenado (DESC) : " + v);

    Collections.sort(v); // chamada igual sem o reverseOrder
    System.out.println("Vetor ordenado (ASC) : " + v);
  
  }
}

Vamos ver este exemplo rodando:
Como esperado!!

O bacana deste código é que ele pode ser facilmente adaptado para qualquer tipo de dado. Vamos ver por exemplo para string. Temos que trocar somente 3 linhas:

  • trocar o tipo do vetor para string
    Vector<String> v = new Vector<String>();



  • trocar o arquivo de dados. só porque eu coloquei fixo no código. vejam que não muda nada no algoritmo de leitura...
    String nomeArquivo = "dados2.txt";

  •  exceto a adição da linha ao vetor. agora não preciso fazer conversão
    v.add(linha);



Fantástico, não é?

PS: para quem ficou curioso com o texto... É Lusíadas de Camões.

terça-feira, 15 de maio de 2012

Executar um comando no sistema operacional do host

Você já deve ter pensado em utilizar algum comando o sistema operacional para seus programas, não é? No meu caso, eu precisava usar o mysqldump para gerar automaticamente a exportação do meu banco de dados para backup.

A solução é simples, já que o Java nos dá todas as ferramentas para executar este comando. Não queremos somente executar o comando, mas gravar um log do que aconteceu. Podemos até mesmo interpretar este log e tomar alguma outra ação. No meu caso, mandar um email falando que o backup não funcionou.

O exemplo que eu vou apresentar abaixo não é do mysqldump, mas é simplesmente a saída de comando diskfree (df) no linux. Vamos lá!

/**
* Executa um comando simples no linux
* @author H3dema
*
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

// classe principal para execucao do comando
public class DiskFree {
  public BufferedReader executa() {
    BufferedReader saidaComando = null;
    String[] linhaComando = null;
    // usamos a classe System para retornar o sistema operacional
    if (System.getProperty("os.name").equals("Linux")) {
      linhaComando = new String[2];
      linhaComando[0] = "df";
      linhaComando[1] = "-h"; // mostra "human readable"
    } else if (System.getProperty("os.name").equals("Solaris")) {
      linhaComando = new String[2];
      linhaComando[0] = "df";
      linhaComando[1] = "-k";
    }
    if (linhaComando == null) {
      return saidaComando; // não é um dos dois sistemas operacionais --> sai
    }

    Process fs = null;
    try {
      fs = Runtime.getRuntime().exec(linhaComando);
    } catch (IOException e) {
      e.printStackTrace();
      return saidaComando;
    }

    saidaComando = new BufferedReader(new InputStreamReader (fs.getInputStream()));
    return saidaComando;
  }

  public static void main(String[] args) {
    DiskFree df = new DiskFree();
    BufferedReader br = df.executa(); 
    String linhaDoBuffer = null;

    try {
      while ((linhaDoBuffer = br.readLine()) != null) {
        System.out.println(linhaDoBuffer);
      }
    } catch (IOException e) {
      System.out.println(e.getMessage());      
    }
  }
}

Neste programa utilizando algumas classes interessantes:
  • System.getProperty("os name") retorna uma String com o tipo do sistema operacional 
  • Para executar o programa usando uma classe Process que recebe o resultado de Runtime.getRuntime().exec(linhaComando)
  • A classe Process retorna um InputStream que permite receber os resultados do comando executado. No nosso exemplo, guardamos este resultado em um BufferedReader que nos permite apresentar o resultado do comando. Mas poderíamos ler estas linhas para poder executar alguma outra ação.

O resultado do comando executado é mostrado abaixo:

quarta-feira, 4 de abril de 2012

Uso da classe Scanner

Um exercício que todas as pessoas que estão aprendendo a utilizar o Java se deparam é com a entrada e saída a partir de uma tela de texto. Vamos mostrar um exemplo simples utilizando a classe Scanner.

/*
 * Funcao: Le uma sequencia de números inteiros positivos da entrada,

 * imprime o menor e o maior
 * Condicao de final de programa: entrada negativa
 * Autor: Henrique
 */

import java.util.Scanner;

public class MostraMinMax {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int n = 1;      // numero da entrada de dados. começa em um
    int num;        // guarda a entrada de dados
    int nMin = 0;   // guarda o valor minimo da sequencia
    int nMax = 0;   // guarda o valor máximo da sequencia

    do {
        System.out.print("Entre com o inteiro # " + n + ": ");
        num = sc.nextInt();
        if (num >= 0) {
            if (n == 1) { // guarda a primeira entrada
              nMin = num;
              nMax = num;
            } else {
              if (num < nMin) nMin = num;
              if (num > nMax) nMax = num;
            }
              n++; // incrementa o contador
        }
    } while (num >= 0); // sai se digitamos um numero negativo

    n--;
    if (n == 0) {
          System.out.println("Nao foi feita entrada valida...");
    } else {
          System.out.println("O numero de entradas digitadas: " + num);
          System.out.println("O menor numero                : " + nMin);
          System.out.println("O maior numero                : " + nMax);
    }
  }
}


Notem alguns pontos bacanas deste programinha:
  • usamos a estrutura de programação
    • do {comandos} while {condição de parada}
  • o comando if quando tem somente um comando na condição then pode ser escrito sem as chaves usando o formato
    • if (testes) comando;
  • a classe Scanner lê um inteiro usando nextInt, mas possui ainda diversas outras variações como nextFloat, nextLine, nextByte, nextBoolean etc. Vale a pena dar uma olhada na definição da classe na documentação do Java (veja aqui).
  • reparem na criação do objeto sc da classe Scanner:
    • new Scanner(System.in);
      • a criação é feita utilizando uma InputStream, no nosso caso é um System.in que é a InputStream padrão.
      • O interessante desta solução é que a InputStream poderia ser uma porta serial, um arquivo local ou remoto, um site ...
Vamos ver os resultados:

Existem algumas variações que podem ser feitas no programa, como por exemplo eliminar o teste "if (n == 1) { ...". Como fazer isto? Veja abaixo o pedaço do código alterado:

    // usamos o subterfúgio de inicializar as variáveis com os valores minimo e
    // máximo para uma variável inteira.
    // note que nMin recebe o valor MÁXIMO e vice versa
    int nMin = Integer.MAX_VALUE;   // guarda o valor minimo da sequencia
    int nMax =
Integer.MIN_VALUE;   // guarda o valor máximo da sequencia

    do {
        System.out.print("Entre com o inteiro # " + n + ": ");
        num = sc.nextInt();
        if (num >= 0) {
           if (num < nMin) nMin = num;
           if (num > nMax) nMax = num;
           n++; // incrementa o contador
        }