Статьи

Создание учебного приложения с помощью Java Hashmap

В наши дни идея физического словаря довольно старомодна. Проконсультироваться с физической книгой, чтобы узнать значение слова или фразы, не имеет смысла, когда простой поиск в Google даст вам всю необходимую информацию.

Но словарь — очень полезная метафора для важной концепции программирования: пары ключ-значение. Словари работают, связывая слово («ключ») с его значением («значение»). Эта пара ключ-значение является фундаментальной моделью данных в программировании.

Большинство языков программирования используют хеши для обеспечения уникальности ключей и повышения эффективности хранения и извлечения значений. HashMap в Java объединяет все это, чтобы позволить программистам хранить пары ключ-значение, хешируя свои ключи при вставке и извлечении.

В этом уроке мы узнаем, как создать программу учебного пособия, сохраняя концепции Java и их соответствующие значения в HashMap для извлечения во время обучения. Исходный код для этого урока можно найти здесь

HashMap

Карты Java делают программы более эффективными, сохраняя пары ключ-значение в одной структуре данных. В качестве альтернативы, то же самое может быть достигнуто с помощью двух синхронизированных вручную структур данных — одна содержит ключи, а другая содержит значения — но это не лучший подход к сопоставлению ключей со значениями; для этого и нужна Map . HashMap , пожалуй, один из самых мощных из них. Все ключи карты должны быть того же типа, что и их значения.

На заднем плане класс Java HashMap хранит пары в массивах, но нам не нужно сильно беспокоиться о реализации, потому что большая часть тяжелой работы инкапсулирована.

Интерфейс Map предоставляет методы для размещения пар на карте, замены значений пар, удаления пар по ключу, проверки существования пары по ключу и очистки карты. Мы будем использовать почти все эти функции сегодня.

Приложение

Наше приложение будет простым учебным приложением. Мы HashMap нашу HashMap для хранения наших концепций и будем использовать операторы потока управления Java для циклов, пока не предоставим правильное значение для каждой концепции. Давайте строить!

Классы

Нам понадобятся два класса для запуска нашего учебного приложения. Первый класс будет нашим основным классом для запуска нашей программы, а второй будет другим классом для моделирования смысла концепций Java и инкапсуляции пары полей и методов для упрощения интерфейса учебного пособия.

Первое, что нам нужно сделать, это создать новый Java-проект с основным классом. Как только мы это настроим, давайте продолжим и создадим наш класс Meaning который будет использоваться в нашем Main .

исследование

Значение класса

Создайте новый класс под названием «Значение». Мы не будем погружаться в этот класс, но давайте рассмотрим важность этого. Этот класс будет моделировать значение изучаемых понятий Java, сохраняя их значение и токены этого значения. Вот его важные части:

 public class Meaning { /*if a user's guess at the meaning matches at least 65% of the actual meaning of a Meaning, the user is correct.*/ private static final double CORRECTNESS_THRESHOLD_PERCENTAGE = .65; //the string representation of this meaning private String meaning; //whether or not the user's input matches this meaning private boolean gotCorrect; //holds the tokens of the meaning private String[] meaningTokens; //the constructor public Meaning(String meaning) { this.meaning = meaning; tokenizeMeaning(); } //omitted ... /** * This is a naive lexical analyzer * that counts how many words match * between the user input and this meaning. * * There are many ways to improve this. * @param userInput the user's guess at what this concept means * @return true if the user's input matches * this meaning. */ public boolean userInputMatches(String userInput) { //make sure the user's input is not empty if(userInput != null && !userInput.isEmpty()) { int numMatchedTokens = 0; userInput = removeAllPunctuation(userInput); String[] userInputTokens = userInput.split(" "); //total tokens. The greater of the two int totalTokens = Math.max(userInputTokens.length, meaningTokens.length); /*get the number of matched tokens between the two sets of tokens.*/ numMatchedTokens = getNumMatchedTokens(userInputTokens); //cast to a double to get floating point result double matchPercentage = ((double)numMatchedTokens / totalTokens); //return whether or not the matched percentage meets the threshold return matchPercentage >= CORRECTNESS_THRESHOLD_PERCENTAGE; } //return false because the user's input is empty return false; } //omitted ... } 

Чтобы создать новый экземпляр Meaning , мы передаем значение понятия конструктору. Оттуда значение будет размечено и сохранено в массиве, чтобы мы могли использовать его при проверке, соответствует ли введенный пользователем смысл концепции.

Когда программа запустится, пользователю будет предложено ввести предположение о значении концепции, и мы будем использовать этот ввод, чтобы проверить правильность пользователя, вызвав метод экземпляра userInputMatches() « Meaning . Этот метод будет токенизировать пользовательский ввод и перебирать токены значения и входные токены пользователя, чтобы посчитать их совпадения по порядку. Мы вернем true, если 65% токенов значения совпадают с входными токенами пользователя; это означает, что пользователь ответит правильно, и поэтому он получает очко.

Это все, что нам нужно для понимания, но вы можете просмотреть полный класс во вспомогательном коде для этого урока.

Основной класс

Наше учебное пособие будет работать в основном классе. Не стесняйтесь называть этот класс как хотите, но я назову мой «StudyGuide».

 public class StudyGuide { //omitted ... //initialize a HashMap to hold String-Meaning (key-value) pairs. private static final HashMap<String, Meaning> studyGuide = new HashMap<>(); /*The total possible points the user can obtain. Equal to the number of concepts in the study guide.*/ private static int possiblePoints; //our main method to start our app public static void main(String[] args) { populateStudyGuide(); printInstructions(); study(); } //omitted ... } 

Наш класс StudyGuide начинается довольно скромно. Мы начнем с инициализации final static HashMap называемого studyGuide качестве учебного пособия. Все ключи studyGuide , представляющие наши концепции, будут иметь тип String то время как значения, значения наших концепций будут иметь наш собственный тип Meaning .

Мы начинаем наше приложение с вызова studyGuide чтобы заполнить studyGuide концептуального значения:

 /** * Populates the study guide with Java concepts. */ private static void populateStudyGuide() { //add a bunch of studyGuide.put("Variable", new Meaning( "Used to store a single value for later use.")); studyGuide.put("String", new Meaning( "A class for representing character strings.")); studyGuide.put("double", new Meaning( "A primitive datatype for representing floating point numbers.")); studyGuide.put("Double", new Meaning( "A class for wrapping a double in an Object with convenient methods.")); studyGuide.put("zero", new Meaning( "The number zero. The first index in arrays and the first position of lists.")); //set the possible points updatePossiblePoints(); } 

Там мы вызываем метод экземпляра put() нашего HashMap пять раз, каждый раз помещая концепцию и ее значение в studyGuide . Этот метод заканчивается вызовом updatePossiblePoints() для установки возможных точек, static possiblePoints точек, доступных пользователю:

 private static void updatePossiblePoints() { possiblePoints = studyGuide.size(); System.out.println("There are " + possiblePoints + " concepts to study."); } 

Обратите внимание, что possiblePoints всегда должны быть равны количеству элементов в studyGuide которое мы можем собрать, вызвав метод экземпляра size() .

Затем мы вызываем printInstructions() для вывода инструкций нашего приложения на консоль для пользователя. После этого пора учиться:

 /** * Starts the study session */ private static void study() { //for scanning the user's keyboard for input Scanner userInputScanner = new Scanner(System.in); //to store the user's input String userInput; /*the points obtained by the user for getting the meanings of concepts correct.*/ int userPoints = 0; /*how many rounds the user takes to get all of the concept meanings correct*/ int rounds = 1; //set the default user input because the while loop needs to check it userInput = "startInConsole"; /*We'll let the user quit at any point in the app, so we must make sure they haven't quit yet*/ while(userPoints < possiblePoints && !userQuit(userInput, userPoints)) { //store the keys of the study guide in a immutable Set final Set<String> concepts = studyGuide.keySet(); for (String concept : concepts) { Meaning currentConceptMeaning = studyGuide.get(concept); //make sure currentConceptMeaning is not null if(currentConceptMeaning == null) { studyGuide.remove(concept); //move to next iteration continue; } if(!currentConceptMeaning.userGotCorrect()) { System.out.printf("\n\t\t" + OUTPUT_SEPERATOR_LINE + "\n\n> What is %s?\n\n\n", concept); if(userInputScanner.hasNextLine()) { userInput = userInputScanner.nextLine(); } if (!userQuit(userInput, userPoints)) { if (currentConceptMeaning.userInputMatches(userInput)) { currentConceptMeaning.markUserGotCorrect(); userPoints++; System.out.printf("\n> CORRECT! %s means: %s", concept, currentConceptMeaning.getMeaning()); } else { System.out.printf("\n> WRONG! %s means: %s", concept, currentConceptMeaning.getMeaning()); } } } } System.out.println(OUTPUT_SEPERATOR_LINE); System.out.printf("\n> You have %d of %d possible points at the end of round %d. ", userPoints, possiblePoints, rounds); //make sure the user hasn't scored all of the possible points if(userPoints < possiblePoints) { System.out.println("\n> Type anything to continue " + "OR remove to remove a concept \"x\" or \"quit\" to quit?"); if(userInputScanner.hasNextLine()) { userInput = userInputScanner.nextLine(); if(userInput.toLowerCase().equals("remove")) { System.out.println("\n> Remove which concept?"); if(userInputScanner.hasNextLine()) { removeConcept(userInputScanner.nextLine()); } } } } else break; //break out of the loop because the user is done } System.out.println(OUTPUT_SEPERATOR_LINE); System.out.println("Congrats! You got all the meanings correct."); } 

Мы начинаем учиться, вызывая метод study() . Этот метод объявляет пару переменных для хранения информации об учебной сессии пользователя. Мы инициализируем Scanner как userInputScanner чтобы захватить ввод пользователя, который будет сохранен в userInput . Накопленные пользователем баллы будут сохраняться в userPoints и увеличиваться на 1 каждый раз, когда пользователь предоставляет правильное значение для концепции.

Мы используем цикл while, чтобы продолжать предлагать пользователю концепции, если они не получили возможные баллы и не вводят «x» или «quit» для выхода.

В верхней части цикла while мы должны хранить Set ключей в studyGuide в константе, вызывая его keySet() экземпляра keySet() чтобы мы могли выполнять итерации по нему. Для каждого ключа мы получаем его значение, вызывая метод экземпляра get() studyGuide и передавая ему ключ. Значение, возвращаемое и сохраняемое в currentConceptMeaning является экземпляром Meaning представляющим смысл концепции. До тех пор, пока currentConceptMeaning не равен null , мы даем пользователю возможность угадать, что он думает. Если метод экземпляра userInputMatches() объекта currentConceptMeaning возвращает значение true , мы вызываем его markUserGotCorrect() экземпляра markUserGotCorrect() чтобы установить для его gotCorrect экземпляра gotCorrect значение true, и увеличиваем userPoints поскольку пользователь предоставил правильное значение для концепции.

В конце каждого раунда мы распечатываем, сколько очков набрал пользователь, и позволяем ему удалить концепцию, от которой он отказывается при изучении, если они еще не закончили учебное пособие; мы делаем это, вызывая наш метод removeConcept() :

 /** * This allows the user to remove a concept they give up on from the study guide. * Removes the pair from the HashMap with a key that matches the concept. * @param concept */ private static void removeConcept(String concept) { Meaning conceptMeaning = studyGuide.get(concept); if(conceptMeaning != null) { //make sure the user hasn't already gotten the meaning correct if(!conceptMeaning.userGotCorrect()){ //remove the concept's key-value pair from the study guide studyGuide.remove(concept); System.out.println("Removed \"" + concept + "\" from your study guide."); /*update the possible points so that it matches the number of concepts left in the study guide*/ updatePossiblePoints(); } else { //don't let the user remove a concept they already know System.out.println("You know \"" + concept + "\", silly. Can't remove."); } } else { System.out.println("\"" + concept + "\" isn't in your study guide."); } } 

Когда мы вызываем removeConcept() , он получает значение Meaning соответствующее концепту в studyGuide как conceptMeaning . Если концепция найдена, мы вызываем метод экземпляра userGotCorrect() для conceptMeaning чтобы проверить, правильно ли пользователь понял смысл концепции, и если он возвращает false , мы вызываем метод экземпляра studyGuide() чтобы удалить пара ключ-значение из HashMap по концепции в качестве ключа. Мы не позволяем пользователю удалить концепцию, которую он уже правильно определил, потому что он уже получил очки за это.

Наш userQuit() проверяет, просил ли пользователь в любой момент завершить программу и распечатать баллы пользователя. С другой стороны, если пользователь доходит до конца учебного пособия, мы вызываем break чтобы выйти из цикла while. В этот момент приложение поздравит пользователя с успешной учебной сессией, и программа завершится.

Вывод

Хорошо посмотри на это, мы все сделали! Теперь у нас есть учебное пособие, созданное с помощью единственной HashMap чтобы помочь нам изучить наши концепции Java. Теперь мы также знаем, как использовать HashMap для эффективного хранения и обработки пар ключ-значение в Java. Вы уже на пути к тому, чтобы стать мастером карт! Поиграйте с исходным кодом и посмотрите, как вы можете улучшить учебное пособие. Есть вопросы? Оставьте комментарий, и я сделаю все возможное, чтобы ответить.

Ссылки:
Документация Oracle на картах
Документация Oracle по HashMaps