Como implementar LFU e LRU no Java?

Boa noite prezados, sou programadora Abap, concurseira e mestranda tentando aprender Java para implementar os algoritmos de substituição de cache LRU e LFU.

Prezados, consegui fazer a solução para FIFO, pois pego o mais antigo e substituo. Mas e quanto a LFU e LRU? Eu entendo os conceitos na teoria mas como sou NOOB em Java meio que está nebuloso implementar isso, visto que pouco sei sobre as estruturas de dados no Java.

Gostaria de ajuda… Como posso criar pilha deixando no topo o menos usado para solucionar o LFU?

Deixarei código do FIFO para ver se vocês me ajudam a pensar aqui…

A classe que tem MAIN:

//Classe Factory que deixa as subclasses decidirem qual o tipo de implementação será usada
 public class CacheFactory 
 {
    private static Scanner teclado = new Scanner(System.in);
    private static int GetTipoCache() {

      //tipoCache recebe valor do teclado sobre qual implementação será executada
      int tipoCache = 1; //recebe valor padrao 1 para FIFO, mas poderia ser qualquer outro algoritmo
      do {
        System.out.println(" --- Simulador de Substituição de Páginas ---");
        System.out.println("1: FIFO");
        System.out.println("Escolha um tipo de Implementação:");
        tipoCache = teclado.nextInt();

        //
              return tipoCache;
    }
    private static int GetCacheSizeInput() {
      System.out.println("");
      System.out.println("Informe o tamanho do cache:");
      return  teclado.nextInt();
    }
    private static String GetNameFile() 
        System.out.println("");
        System.out.println("Informe o caminho do arquivo:");
        return  teclado.next();
      }
    public static void main (String[] args) throws IOException
     {
        Cache cache = null;
        FIFO fifo = null;
        int intAlgo = GetTipoCache();
        int intSize = GetCacheSizeInput();
        String nameFile = GetNameFile();

        teclado.close();

        switch(intAlgo) {
            case 1:
              fifo = new FIFO(intSize);
              break;                                                                   
            default:
              cache = null;
              System.out.println("Invalido");
              break;
        }
        
        System.out.println("");

        FileArrayReader fr = new FileArrayReader();
        String[] data = fr.readLines(nameFile);                  
        
        if (intAlgo == 1) {
        for (String line : data) {
          System.out.println("Acesso do endereço " + line);
          //String dt = fifo.Read(line);
          fifo.Read(line);
          System.out.println(java.util.Arrays.toString(fifo.GetCache()));
          System.out.println("Acessos: " + fifo.accessCounter + " - Hits: " + fifo.hitCounter + " - Misses: " + fifo.missCounter);
          System.out.println();
        }
        } 


}

Agora, classe FIFO

public class FIFO {
	//public class FIFO extends Cache {

protected int index;
protected String[] buffer; 
public int size;
public int missCounter = 0;
public int hitCounter = 0;
public int accessCounter = 0;

// Cache(int cacheSize) {
// size = cacheSize;
// buffer = new String[cacheSize];
// }

public String[] GetCache(){
    return buffer;
}

// public abstract String Read(String pos);`

// pra cima 21/09

FIFO(int bufSize) {
    //super(bufSize);
    size = bufSize;
    buffer = new String[bufSize];
    System.out.println("Iniciando cache FIFO com tamanho " + bufSize + "...");
} 

//@Override

public String Read(String pos) {

    String ioBuff;
    //Temos um acesso
    accessCounter++;
    for(int i = 0; i < size; i++){
        if(buffer[i] != null && buffer[i].equals(pos)) {
            //Temos um hit
            hitCounter++;
            return buffer[i];
        }
    }
    //Temos um miss
    missCounter++;

    //Em uma implementação real, faríamos um acesso à memória principal
    ioBuff = pos;
    buffer[index] = ioBuff;
    if(index == size - 1)
        index = 0;
    else
        index ++;

    return ioBuff;
}

}

Classe FILEARRAYREADER

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class FileArrayReader {

public String[] readLines(String filename) throws IOException {
    FileReader fileReader = new FileReader(filename);
    BufferedReader bufferedReader = new BufferedReader(fileReader);
    List<String> lines = new ArrayList<String>();
    String line = null;
    while ((line = bufferedReader.readLine()) != null) {
        lines.add(line);
    }
    bufferedReader.close();
    return lines.toArray(new String[lines.size()]);
}

}

Para testar, crie arquivo txt, coloque varias letras ou palavras em casa linha… Meu codigo tem que mostrar como está a cache em cada iteração… Contar os Hits e Miss…

Alguém pode me judar no LRU e LFU?

Obrigada!

Com LRU e LFU:

Com LRU:

https://www.programcreek.com/2013/03/leetcode-lru-cache-java/

Não sei de nada dos dois. :joy:

Disso eu sei:

Basta lembrar que, em Java, uma variável nunca é um objeto. Não há, no Java, uma maneira de criarmos o que é conhecido como “objeto pilha” ou “objeto local”, pois todo objeto em Java, sem exceção, é acessado por uma variável referência (lembra o uso de ponteiros).
Fonte: https://www.caelum.com.br/apostila-java-orientacao-objetos/orientacao-a-objetos-basica/#4-7-objetos-sao-acessados-por-referencias

Adendo:
https://docs.oracle.com/javase/7/docs/api/java/util/Deque.html

1 curtida

Obrigada addler pela resposta!

Só que como sou NOOB em java, eu não sei que estrutura é essa do link de LRU… Nem sei como faço para preencher a estrutura com o arquivo txt que li na classe FileArrayReader…

Ou seja, sei nada do restante do código… :frowning:

No link referenciado, somente a estrutura Node(nó) é “nova”, sendo uma implementação do desenvolvedor.

Assim, é bem simples perceber que o Node(Nó), tem:
uma chave;
um valor;
Uma referência para Próximo Node;
Uma referência para Anterior Node;

Esta sem getter e setter, por que o modificador de acesso da classe é package, ou seja, as classe Node, provavelmente está no mesmo pacote da classe LRUCache, outra implementação do desenvolvedor.

Diferente da classe Node, a classe LRUCache tem o modificador públic, mas seus atributos possuem o modificador package, ou seja, seus atributos só são visíveis dentro do pacote em que a classe se encontra.

Obs.: se não constar modificador de acesso, considera-se que o mesmo é package, com excessão de interface, onde a ausência indica que o modificador é public.

A classe LRU, não apresenta estruturas complexas, basta estudar a api collections, está tudo lá.
Veja: https://www.youtube.com/watch?v=U68S9rdJpPs&list=PLxQNfKs8YwvGhXHbHtxtoB-tRRv6r3Rlr&index=86
Assista as demais aulas, pois tratam de collections.

É mais elegante, argumentar que esta aprendendo java.
Veja, você conhece teoricamente o funcionamento de LRU e FLU, portanto, ao meu ver, isto é o mais importante, pois a linguagem de implementação é outra história.

Use serialização, pois da menos trabalho, ou procure por bufferedReader e BufferedWriter (mais trabalhoso).

Eu não posso ajudar muito pois não busquei entender o funcionamento da LRU e LFU.

Té+

1 curtida

Gostei do seu problema e da forma como vc o apresentou.

Eu não conhecia LRU ou LFU, mas tirei um tempo para pesquisar e achei uma possível solução.

Embora vc já tenha feito o FIFO, eu o refiz usando uma estrutura de dados já presente no Java, o ArrayDeque.

Essa estrura também pôde ser usada pra implementar o LRU.

O LFU foi mais complicado pra mim; tive que criar uma classe separada só pra representar os dados que o algoritmo usa.

Não pude testar muito, mas acredito que o código está funcional o suficiente. De qualquer forma, acredito que já será o bastante para lhe dar uma direção.

O código em funcionamento pode ser visto aqui: https://ideone.com/7f6jdt

import java.util.*;

public class Program {
    public static void main(String[] args) {
        String[] data = {"red", "green", "blue", "red", "yellow", "red", "black", "red", "blue", "white", "green"};
        final int size = 4;

        System.out.println("\n##### Teste com FIFO #####");
        fifo(size, data);

        System.out.println("\n##### Teste com LRU #####");
        lru(size, data);

        System.out.println("\n##### Teste com LFU #####");
        lfu(size, data);
    }

    static void fifo(final int size, final String[] data) {
        ArrayDeque<String> cache = new ArrayDeque<>();

        int misses = 0, hit = 0, access = 0;

        for (String d : data) {
            if (!cache.contains(d)) {
                if (cache.size() < size) {
                    cache.add(d);
                } else {
                    cache.remove();
                    cache.add(d);
                }

                misses++;
            } else {
                hit++;
            }

            access++;

            System.out.printf("access: %d, hit: %d, misses: %d >>> %s\n[ ", access, hit, misses, d);
            cache.forEach((x) -> System.out.print(x + " "));
            System.out.println("]");
        }
    }

    static void lru(final int size, final String[] data) {
        ArrayDeque<String> cache = new ArrayDeque<>();

        int misses = 0, hit = 0, access = 0;

        for (String d : data) {
            if (!cache.contains(d)) {
                if (cache.size() < size) {
                    cache.add(d);
                } else {
                    cache.remove();
                    cache.add(d);
                }

                misses++;
            } else {
                cache.remove(d);
                cache.add(d);

                hit++;
            }

            access++;

            System.out.printf("access: %d, hit: %d, misses: %d >>> %s\n[ ", access, hit, misses, d);
            cache.forEach((x) -> System.out.print(x + " "));
            System.out.println("]");
        }
    }

    static void lfu(final int size, final String[] data) {
        class DataWrapper implements Comparable<DataWrapper> {
            private final String data;
            private int frequency;

            DataWrapper(String data) {
                this.data = data;
                incrementFrequency();
            }

            void incrementFrequency() {
                this.frequency++;
            }

            @Override
            public boolean equals(Object o) {
                DataWrapper dw = (DataWrapper) o;
                return dw.data.equals(this.data);
            }

            @Override
            public int compareTo(DataWrapper o) {
                if (this.frequency > o.frequency)
                    return 1;
                else if (this.frequency < o.frequency)
                    return -1;
                else
                    return 0;
            }

            @Override
            public String toString() {
                return data;
            }
        }

        List<DataWrapper> cache = new ArrayList<>();
        List<DataWrapper> temp = new ArrayList<>();

        int misses = 0, hit = 0, access = 0;

        for (String d : data) temp.add(new DataWrapper(d));

        for (DataWrapper d : temp) {
            if (!cache.contains(d)) {
                if (cache.size() < size) {
                    cache.add(d);
                } else {
                    cache.remove(0);
                    cache.add(d);
                }

                misses++;
            } else {
                cache.get(cache.indexOf(d)).incrementFrequency();
                hit++;
            }

            Collections.sort(cache);
            access++;

            System.out.printf("access: %d, hit: %d, misses: %d >>> %s\n[ ", access, hit, misses, d);
            cache.forEach((x) -> System.out.printf("%s(%d) ", x, x.frequency));
            System.out.println("]");
        }
    }
}
1 curtida

Agradeço muito sua resposta! :smiley:

Olha fiquei de quinta para sexta programando, hahahaha. Até 1h da manhã e depois fui dormir pra acordar 6h pra continuar no código e depois fui para a minha aula de 9h às 13h! Durante a aula, veio uma solução! :scream:

Olha, eu postarei tudo aqui em breve para ficar no histórico do grupo, nunca aprendi tanto Java na marra quanto essa madrugada hahahahha

Em tempo, o LRU está funcionando direitinho de acordo com o vídeo que explica teoricamente como é o seu funcionamento:

LRU

Tem também o LFU que vi agora há pouco…

O código está bem feio, mas funcional! Só conheço razoavel o Array mesmo no Java e apanhei muito que não entrava o IF meu… Eu estava comparando duas strings pra saber se elas eram iguais… Nunca entrava… Aí fiquei sabendo da existência do EQUALS hahahhaha Eu estava usando ==

Confesso que hoje de manhã meus olhos estavam cheios de lágrimas… Mas agora a tarde sinto-me mais leve e comecei a fazer o LFU, espero ir bem melhor e mais rápido dessa vez!

Abraços! :smiley:

2 curtidas

Muito bom que não tenha desistido mesmo com lágrimas! Aprender assim na prática é muito bom.

Isso mesmo, não deixe de postar sua resposta depois.

1 curtida

Essa parte do codigo do seu LFU, o que é isso? fica dando erro aqui hahahah
Erro na variável x e nesse ->

É só nessa linha que deu erro? Na linha dos outros 2 métodos que são parecidas com essa não deu?

Eu usei um recurso do Java 8, as Lambda Expressions.

Tente substituir as linhas que começam com cache.forEach dos métodos fifo e lru pela linha abaixo:

for (String x : cache) System.out.print(x + " ");

Já nessa mesma linha do método lfu, substitua por essa:

for (DataWrapper x : cache) System.out.printf("%s(%d) ", x, x.frequency);

A versão modificada ficou assim: https://ideone.com/9MZbI9

Se o problema persistir, cole aqui a mensagem de erro que apareceu pra vc.

1 curtida

Desculpe a demora, tinha ido jantar. Seu LFU rodou aqui!
Interessante que você ordena sempre o vetor que vai mostrar no print, né? Ainda mostra a quantidade de hits em cada index do vetor :slight_smile:

Você consegue modificar de tal forma que deixe o vetor sem estar ordenado?

Fiz na mão o LFU e ficaria o ultimo frame assim:

[ red(4) green(1) blue(2) white(1) ]

O seu LFU faz perfeitamente o desempate :heart_eyes:
Mas sempre ordena o vetor do menos usado para o mais usado rs

O meu problema no meu LFU é saber qual pegar quanto tem empate, sei que preciso usar o FIFO para desempatar, mas não tô conseguindo raciocinar… Vou deixar o meu LFU aqui:

import java.io.*;

import java.util.Scanner;

class Lfu {

private static Scanner teclado = new Scanner(System.in);

public static void main(String args[]) throws IOException {

	BufferedReader obj = new BufferedReader(
			new InputStreamReader(System.in));
	int f, ch, k = 0, pgf = 0, n, miss = 0, hit = 0, chn = 0;
	String page = null;
	boolean flag;
	String pages[];

	System.out.println("Menu");
	System.out.println("3.LFU");
	System.out.println("4.SAIR");
	System.out.println("ENTRE COM A SUA ESCOLHA: ");
	ch = Integer.parseInt(obj.readLine());

	switch (ch) {

	case 3:

		k = 0;
		pgf = 0;
		int sml = 0;
		System.out.println("enter no. of frames: ");
		f = Integer.parseInt(obj.readLine());
		int cnt[] = new int[f];
		String frame2[] = new String[f];
		flag = true;

		for (int i = 0; i < f; i++) {

			frame2[i] = null;
			cnt[i] = 0;
		}

		System.out.println();
		System.out.println("Informe o caminho do arquivo:");
		String path = teclado.next();
		teclado.close();
		FileArrayReader fr = new FileArrayReader();
		String[] data = fr.readLines(path);
		n = data.length;
		pages = new String[n];

		for (int j = 0; j < n; j++)
			pages[j] = data[j];

		do {

			int pg = 0;

			for (pg = 0; pg < n; pg++) {

				page = pages[pg];
				flag = true;
				pgf++;

				for (int j = 0; j < f; j++) {

					if (page.equals(frame2[j])) {

						flag = false;
						cnt[j]++;
						break;
					}
				}

				if (flag) {

					// pegar menor valor do vetor
					//deveria ter algum tipo de FIFO para o desempate
					for (int l = 0; l <= cnt.length - 1; l++) {
						for (int ll = 1; ll <= cnt.length - ll; ll++) {
							if (cnt[l] < cnt[ll]) {
								sml = cnt[l];
							} else {
								sml = cnt[ll];
							}
						}
					}

					// sml = cnt[0];

					for (int j = 0; j < f; j++) {

						if (cnt[j] < sml) {

							sml = cnt[j];
							break;
						}
					}

					for (int j = 0; j < f; j++) {

						if (sml == cnt[j]) {

							frame2[j] = page;
							k = j;
							break;
						}
					}

					cnt[k] = 1;
					System.out.print("frame : ");

					for (int j = 0; j < f; j++) {

						System.out.print(frame2[j] + "  ");

					}

					System.out.println();

				} else {

					System.out.print("frame : ");

					for (int j = 0; j < f; j++)
						System.out.print(frame2[j] + "  ");

					System.out.println();

				}

				chn++;
			}
		} while (chn != n);

		System.out.println("Page fault: " + pgf);
		break;

	case 4:
		break;
	}

}

}

Minha classe FileArrayReader

import java.io.BufferedReader;

import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class FileArrayReader {

public String[] readLines(String filename) throws IOException {
    FileReader fileReader = new FileReader(filename);
    BufferedReader bufferedReader = new BufferedReader(fileReader);
    List<String> lines = new ArrayList<String>();
    String line = null;
    while ((line = bufferedReader.readLine()) != null) {
        lines.add(line);
    }
    bufferedReader.close();
    return lines.toArray(new String[lines.size()]);
}

}

Parametros do meu LFU
A escolha é 3 para 3.LFU
Tamanho 4.
Conteúdo do meu txt c:\lfu.txt usado para teste, no input que pede o caminho para o arquivo:

5
5
8
1
7
7
6
3
1
2
7
8
7
8
2

O resultado do LFU deveria ser esse ao final:

5 8 2 7

Mas eu ainda não consegui implementar o desempate. :frowning:

É que para o professor, a ordem é importante de cada valor no frame que vai printar…

[ 1 2 3 4 ] não seria a mesma coisa que [ 4 3 2 1 ]. :frowning:

É que quando o cache tá cheio e um novo dado tem que entrar, esse novo dado entra na posição do que tá saindo, né?

O único jeito que consegui pensar foi usando um array auxiliar. Com certeza há uma forma melhor de fazer, mas tem que pensar mais sobre o problema. O código modificado é esse: https://ideone.com/dX2EvF.

Quero te fazer uma sugestão.

Não testei esse seu último código, pois quando tentei rodar o primeiro código que vc postou lá em cima, tive problemas fazendo as modificações necessárias pra ele funcionar, inclusive criando o TXT com as linhas.

Quando for compartilhar código, tente deixá-lo o mais simples e prático possivel pra que pra executar seja tão simples quando copiar daqui, colar no editor, compilar e executar. Sabe, né? Sem ter que fazer modificações e criar arquivos adicionais.

Nos exemplos que fiz, usei apenas um array de String simples. Isso deixou o código menor e menos propenso a erros. Afinal, pra vc pegar o código do jeito que eu mostrei e adequar as suas necessidades, tudo o que vc teria que fazer é criar um método que lê um arquivo e retorna um array de String contendo as linhas desse arquivo.

Outro detalhe é sobre linhas desnecessárias como linhas vazias e linhas com trechos de código comentados. Tudo isso polui muito e dificulta para terceiros lerem. É isso ^^

Outra coisa: Vc conseguiu entender o código que mandei? Saberia explicar linha a linha? Conseguiu pegar como ArrayList e ArrayDeque funcionam? Essa é a parte mais importante.

1 curtida

Ah sim, obrigada pela dica do código!
Entrei aqui no fórum recentemente, acho que na quinta passada!
Realmente, complicado de testar esse meu código, quando eu colocá-lo aqui, o deixarei mais limpo e mais prático somente para o pessoal colar e copiar pra rodar. :slight_smile:

Olha, rodei o seu codigo para ver como o LFU se comportou e estamos iguais! :smile:
Nosso output está igual, uhuuuuuu!

Acordei cedaço pra ver mais um pouco essa minha implementação e também vou agora para a 2a fase de um concurso.

(Escrevi essa mensagem era quse 6h da manhã e esqueci de clicar em responder e agora são 16h da tarde)

Mais uma vez, obrigada por toda a sua ajuda e dicas de convivência daqui do GUJ :smiley:
Em breve posto meu código completo aqui! o/

1 curtida