I am new to Java and I'm coding a simple "Positive Quotes" app. (using Android Studio) that lets you press on a button, that will display a positive quote (randomly selected) from a Map.
The code itself is rather basic:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Sets the right TextView / Button to each object
final TextView textView = (TextView)findViewById(R.id.textView);
final Button button1 = (Button)findViewById(R.id.button);
// Implement listener for your button so that when you click the
// button, android will listen to it.
button1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Map<Integer, String> quotes = new HashMap<Integer, String>();
// Generate your quotes Map
quotes = createQuotesMap();
Random generator = new Random();
Object[] values = quotes.values().toArray();
Object randomValue = (String) values[generator.nextInt(values.length)];
// Perform action on click
textView.setText(randomValue.toString());
} });
}
But my question comes from where I fill up my quotes map:
public Map<Integer, String> createQuotesMap() {
Map<Integer, String> quotes = new HashMap<Integer, String>();
quotes.put(1, "Correction does much, but encouragement does more.");
quotes.put(2, "Keep your face to the sunshine and you cannot see a shadow.");
quotes.put(3, "Once you replace negative thoughts with positive ones, you'll start having positive results.");
quotes.put(4, "Positive thinking will let you do everything better than negative thinking will.");
quotes.put(5, "Pessimism leads to weakness, optimism to power.");
quotes.put(6, "The thing that lies at the foundation of positive change, the way I see it, is service to a fellow human being.");
quotes.put(7, "In every day, there are 1,440 minutes. That means we have 1,440 daily opportunities to make a positive impact.");
quotes.put(8, "Attitude is a little thing that makes a big difference.");
return quotes;
}
Would there be a more efficient way of filling up my Map, or a better container for such a basic application? Could you also point out bad coding habits from the chunk of code that I have shared?
EDIT:
My app. is now complete - I create an array of quotes (only once and not at every click like before) and display one, randomly chosen.
Hint: the main performance aspects here are:
You do not want to create a new map for each new users. The quotes will be be the same all the time, right; so the one and only thing worth worrying about is: how to make sure that you create the map exactly once. So instead of using a local variable, you could create a single static class-wide map.
Why do you want to use a map? You know, when you want to have a sequence of ints to be your key; why don't you just use an List?
In other words: when "optimizing" always try think about the big picture. But your question implies that you are very much thinking from the opposite side of things. You spent your time worrying about the cost of a single operation; instead of looking at your application at a hole ...
Same thing for the map versus list thing. There is absolutely no point in using a map ... to then use it like an list.
Firstly, in terms of efficiency, instead of generating the map every time you click the button, consider making it a field of your Activity and initiating it in your onCreate(). You can then use that field in your onClickListener as such:
//Class field
private Map<Integer, String> quotes;
protected void onCreate(Bundle savedInstanceState) {
// Generate your quotes Map
quotes = createQuotesMap();
button1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Random generator = new Random();
Object[] values = quotes.values().toArray();
//etc
}
});
}
Secondly and most importantly, this really doesn't warrant the use of a Map. You are better off using a simple array or an ArrayList.
You can still access random elements from them by using array[myRandomIndex] or arrayList.get(myRandomIndex) depending on how you wish to implement it.
Just be mindful that initialising these arrays should not be done in an OnClickListener as it would run that code every time the button is clicked.
String[] quotes = {
"Correction does much, but encouragement does more.",
"Keep your face to the sunshine and you cannot see a shadow.",
"Once you replace negative thoughts with positive ones, you'll start having positive results.",
};
public void onClick(View v){
...
Random r = new Random();
selectedQuote = r.nextInt(quotes.length);
textView.setText(selectedQuite);
...
}
I hope this helps you solve yout problem
String[] stringQuotes = {
"Correction does much, but encouragement does more.",
"Keep your face to the sunshine and you cannot see a shadow.",
"Once you replace negative thoughts with positive ones, you'll start having positive results.",
"Positive thinking will let you do everything better than negative thinking will."
};
for(int i = 0; i < stringQuotes.length; i++){
quotes.put(i, stringQuotes[i]);
}
It is faster to use a for-loop, but it requires that all strings are saved in a string[] array which makes the initialization faster
You can create an array once using the method createQuotes(). And you don't need a map, because you only need to obtain a random string among the given strings. As for the key in the map, it transforms perfectly into the array's index. So I suggest you use the below :
public class YourClass {
private static final String[] QUOTES = createQuotes();
private Random generator = new Random();
protected void onCreate(Bundle savedInstanceState) {
// ...
button1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String randomValue = QUOTES[generator.nextInt(QUOTES.length)];
textView.setText(randomValue);
}
});
}
private static String[] createQuotes() {
String[] quotes = new String[] {
"Correction does much, but encouragement does more.",
"Keep your face to the sunshine and you cannot see a shadow.",
"Once you replace negative thoughts with positive ones, you'll start having positive results.",
"Positive thinking will let you do everything better than negative thinking will.",
"Pessimism leads to weakness, optimism to power.",
"The thing that lies at the foundation of positive change, the way I see it, is service to a fellow human being.",
"In every day, there are 1,440 minutes. That means we have 1,440 daily opportunities to make a positive impact.",
"Attitude is a little thing that makes a big difference."
}
return quotes;
}
}
I can't test the code. Please edit me if there's any error.
Related
I'm having doubt and a problem I tried to solve with this Note App I found on Google, which is pretty simple, so I (a beginner) went to try few things on it.
Everytime I save few notes, close app, restart app, it reorganizes the notes alphabetically, which I don't want. I know that Set and ArrayList are different in the sense a Set won't repeat elements, but ArrayList will. Also Set can't guarantee the order when called.
The question is: how is a good way to solve this instrinsecally sorting problem?
I've tried to switch what's HashSet to ArrayList, but the method putStringSet requires a Set in its parameters, which ArrayList isn't.
The following code works (doesn't crash or any other problem) but doesn't work as I want.
I have this part code in the MainActivity
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences("com.tanay.thunderbird.deathnote", Context.MODE_PRIVATE);
HashSet<String> set = (HashSet<String>) sharedPreferences.getStringSet("notes", null);
// a lot of other things here
if (set == null) {
notes.add("Example Note");
} else {
notes = new ArrayList<>(set); // to bring all the already stored data in the set to the notes ArrayList
}
On NoteEditorActivity I have
Intent intent = getIntent();
noteID = intent.getIntExtra("noteID", -1);
if (noteID != -1) {
editText.setText(MainActivity.notes.get(noteID));
} else {
MainActivity.notes.add(""); // as initially, the note is empty
noteID = MainActivity.notes.size() - 1;
MainActivity.arrayAdapter.notifyDataSetChanged();
}
//other things here
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
MainActivity.notes.set(noteID, String.valueOf(s));
MainActivity.arrayAdapter.notifyDataSetChanged();
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences("com.tanay.thunderbird.deathnote", Context.MODE_PRIVATE);
HashSet<String> set = new HashSet<>(MainActivity.notes);
sharedPreferences.edit().putStringSet("notes", set).apply();
}
If you would like to keep the order of the notes, Array or List is truly the better choice than using Set.
To store this into SharedPreferences, you can refer to Put and get String array from shared preferences
This question has answers explaining the followings:
Jsonify the list and parse it back.
Concatenate all the Strings with delimiter and split it back.
my goal is to be able to draw graphs and then save its values if required.
In the image above, I choose from the comboBox a specific type of graph to draw, except for Clear, which just clears the graph that is shown. This is already working correctly.
I have an arrayList of UserPattern(I created) objects that contains a string, a double and a double array.
I want to store the values from the graph in some variable (i'm currently using a double array) to use it later on.
With the double array I've had problems with the passing of values, since when I press "Save Pattern" it keeps the latest values of the graph (the last graph shown) and inputs it on every element of the UserPattern List that I have previously saved. So, even if I save multiple patterns, they all keep the value of the last save.
This is the code I use to store the values in the ArrayList:
private void readAndInsertPatternValues(List<UserPattern> patternLi, double[] graphValue) {
UserPattern tempUserPattern = new UserPattern(typePattern);
//extra code
tempUserPattern.setMonthlyConsump(consTemp);
tempUserPattern.setNameID(patternName);
tempUserPattern.setPatternValues(graphValue);
patternLi.add(tempUserPattern);
System.out.println("Inserted: ");
System.out.println(tempUserPattern);
}
}
I call this method within a mouse event on the button "Save Pattern":
JButton btnSaveUserPattern = new JButton("Save Pattern");
btnSaveUserPattern.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if(!((comboBoxPattern.getSelectedItem()).equals(UserPattern.PatternType.CLEAR)))
readAndInsertPatternValues(patternList, patternValue);
for(UserPattern upTemp : patternList) {
System.out.println("Inside the Pattern List:");
System.out.println(upTemp.toString());
}
}
});
btnSaveUserPattern.setFont(new Font("Tahoma", Font.PLAIN, 13));
btnSaveUserPattern.setEnabled(false);
Also, I get the values that create the graph in the event from selecting an option of the combobox:
comboBoxPattern = new JComboBox<UserPattern.PatternType>();
comboBoxPattern.setFont(new Font("Tahoma", Font.PLAIN, 13));
comboBoxPattern.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//this method creates the graphs, so I send the array "patternValue" to "get" the values.
printPatternGraph(comboBoxPattern, chartPanel, "User Pattern for Energy Consumption", "Hours", "Energy Consumption(Wh)", true, patternValue);
btnSaveUserPattern.setEnabled(true);
}
});
I initialized the patternValue array as a local variable in the method that calls and uses the above mentioned methods as such:
double[] patternValue = new double[1440];
With all this, happened the problem in transporting values. At the "insertion time", the console showed the correct values, however, when I clicked on the "Save Pattern" button, which shows all the objects in the UserPattern list, the graph values from the previously saved graphs were equal to the one I saved last. (The last one I saved corrupted all the others).
I tried to change the way of passing values and changed the method "printPatternGraph" to return a double[] array, and made like this:
patternValue=printPatternGraph(comboBoxPattern, chartPanel, "User Pattern for Energy Consumption", "Hours", "Energy Consumption(Wh)", true);
This, however, brought me the error,
Local variable patternValue defined in an enclosing scope must be final or effectively final.
Therefore, I tried making "patternValue" a global variable . This actually solved the problem, but I don't think that this is the best solution (I've read multiple times that global variables are "bad coding".
This way, which way do you think I should implement this?
Thanks for your attention and sorry for the long post,
nhekas
SOLUTION:
I understood what was wrong! Sorry to bother you guys.
My problem, was, that when I added the double array to the ArrayList, I did it like this:
patternLi.add(graphValues);
This, instead of storing the values inside the graphValue array, was storing the reference to it. Therefore, when I wanted to create a different graph, the reference to the graph was the same, but changed the values.
I had to do the following:
new double[] arrayTemp= new double[1440];
for ( int i=0; i<1440;i++) {
arrayTemp[i]=graphValues[i];
}
patternLi.add(arrayTemp);
This way, it worked! Because, since arrayTemp is local, is created everytime I call the method and passes the correct values.
Thanks for all your input guys !
nhekas
So I am supposed to make an add method for an array list which adds a new movie object to the list if it doesnt exist, or if it finds a movie object with a similar title within the list, it just increases the quantity property of that object. Here is what I've got so far.
public void add(String title, double rating, int releaseYear){
if(this.myMovies.size() < 1)
{
Movie mymovie = new Movie(title, rating, releaseYear);
this.myMovies.add(mymovie);
}
else
{
for(int i = 0; i < this.myMovies.size(); i++)
{
Movie temp = this.myMovies.get(i);
if(temp.Title.equals(title)){
this.myMovies.get(i).quantity++;
break;
}
else
{
Movie mymovie = new Movie(title, rating, releaseYear);
this.myMovies.add(mymovie);
break;
}
}
}
}
My problem is that this ends up not taking account of similar names and doesn't increase the quantity but just adds another object to the list. I have a strong feeling that the problem lies within my For loop but I just can't identify it. Can anyone see anything that I may be doing wrong? Thank you!
You're testing only for equality, not similarity here:
if(temp.Title.equals(title)){
Instead, you should write a helper method to test for similarity based on whatever criteria are appropriate. For example:
if (isSimilar(temp.Title, title)){
and the isSimilar method might look something like this (assuming you don't need any input validation):
private void isSimilar(String title1, String title2) {
return title1.equalsIgnoreCase(title2)
|| title1.toLowerCase().contains(title2.toLowerCase())
|| title2.toLowerCase().contains(title1.toLowerCase());
}
or, perhaps more appropriately, like this (if you implement it in the Movie class):
private void isSimilar(otherMovie) {
return title.equalsIgnoreCase(otherMovie.title)
|| title.toLowerCase().contains(otherMovie.title.toLowerCase())
|| otherMovie.title.toLowerCase().contains(title.toLowerCase());
}
...in which case your if statement would also change slightly.
Keep in mind that I don't know what you consider 'similar'; only that the movies are considered similar if the names are similar.
A couple more comments:
Fields and method names generally start with a lowercase letter (so the field Movie.Title should instead be Movie.title).
It's usually preferable to loop over a Collection using an Iterator instead of using the raw index--partly because the Iterator should always know how to loop over the Collection efficiently.
Learn to use your IDE's debugger (it's probably very easy). Then you can step through each line of code to see exactly where your program is doing something unexpected.
I would do something like this:
public void add(String title, double rating, int releaseYear){
for(Movie m: myMovies.size())
{
if(m.Title.equals(title)){
m.quantity++;
return;
}
}
// movie with same title not found in the list -> insert
this.myMovies.add(new Movie(title, rating, releaseYear));
}
By the way: variable names should start with a lowercase character (Title -> title).
I'm addressing your "similarity" requirement. If you really want to do this properly it could be a lot of work. Essentially you have two strings and want to get a measure of the similarity. I am doing the same thing for figure captions and I plan to tackle it by:
splitting the title into words
lowercasing them
using them as features for classifier4J (http://classifier4j.sourceforge.net/)
That will go a long way based on simple word counts. But then you have the problem of stemming
(words that differ by endings - "Alien" and "Aliens"). If you go down this road you'll need to read up about Classification and Natural Language Processing
I have some part of an android app here which crashes for no apparent reason.
RL0 happens to be some LinearLayout defined in XML which already contains some other irrelevant stuff. To be honest with you, I've mostly worked with C++ so I might not initially know much about why some things are done significantly different in android, but I'm putting effort. Any help on how I can fix that crash?
Error message states NullPointerException.
Thanks.
public class Osteoporoza extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_osteoporoza);
LinearLayout RL0=(LinearLayout)findViewById(R.id.RL0);
page[] pages=new page[10];
RL0.addView(pages[0].pageLL0);//doesn't crash without this line, yet i need to have some way of adding n objects that follow a pattern, i.e. a class.
class page
{
public LinearLayout pageLL0;
public ScrollView pageUpperScroll1;
public TextView pageTextView2;
public ScrollView pageLowerScroll1;
public LinearLayout pageAnswerButtonLL2;
public Button AnswerButton3_1;
public Button AnswerButton3_2;
public Button AnswerButton3_3;
public Button AnswerButton3_4;
page()
{
pageAnswerButtonLL2.addView(AnswerButton3_1);
pageAnswerButtonLL2.addView(AnswerButton3_2);
pageAnswerButtonLL2.addView(AnswerButton3_3);
pageAnswerButtonLL2.addView(AnswerButton3_4);
pageLowerScroll1.addView(pageAnswerButtonLL2);
pageUpperScroll1.addView(pageTextView2);
pageLL0.addView(pageUpperScroll1);
pageLL0.addView(pageLowerScroll1);
}
}
All elements in an Object array are null by default.
I.e. when you create the array:
page[] pages = new page[10];
you are only setting the size of the array but not setting any instances within the array itself so every element will be null. To instantiate each element you need to use:
for (int i=0; i < pages.length; i++) {
pages[i] = new page();
}
Note Java naming conventions show that class names start with an uppercase letter, for example
Page[] pages = new Page[10];
- You have declared the Array but didn't initialize it.
Eg:
page[] pages = new page[10]; // Tell that this is an Array of page of length 10
- You will need to Initialize it,
Eg:
for (page p : pages){
p = new page();
}
- Please use the Collection like ArrayList instead of Array, as its far more flexible than using an Array.
- ArrayList can hold null values, and unlike Array, its size can increased.
ArrayList<page> p = new ArrayList<page>();
- Always make the 1st letter of a class, enum , interface as Capital.
Eg:
It should Not page but Page
I have some events, where each of them has a probability to happen, and a weight if they do. I want to create all possible combinations of probabilities of events, with the corresponding weights. In the end, I need them sorted in weight order. It is like generating a probability tree, but I only care about the resulting leaves, not which nodes it took to get them. I don't need to look up specific entries during the creation of the end result, just to create all the values and sort them by weight.
There will be only about 5-15 events,but since there is 2^n resulting possibilities with n events, and this is to be done very often, I don’t want it to take unnecessarily long time. Speed is much more important than the amount of storage used.
The solution I came up with works but is slow. Any idea for a quicker solution or some ideas for improvement?
class ProbWeight {
double prob;
double eventWeight;
public ProbWeight(double aProb, double aeventWeight) {
prob = aProb;
eventWeight = aeventWeight;
}
public ProbWeight(ProbWeight aCellProb) {
prob = aCellProb.getProb();
eventWeight = aCellProb.geteventWeight();
}
public double getProb(){
return prob;
}
public double geteventWeight(){
return eventWeight;
}
public void doesHappen(ProbWeight aProb) {
prob*=aProb.getProb();
eventWeight += aProb.geteventWeight();
}
public void doesNotHappen(ProbWeight aProb) {
prob*=(1-aProb.getProb());
}
}
//Data generation for testing
List<ProbWeight> dataList = new ArrayList<ProbWeight>();
for (int i =0; i<5; i++){
ProbWeight prob = new ProbWeight(Math.random(), 10*Math.random(), i);
dataList.add(prob);
}
//The list where the results will end up
List<ProbWeight> resultingProbList = new ArrayList<ProbWeight>();
// a temporaty list to avoid modifying a list while looping through it
List<ProbWeight> tempList = new ArrayList<ProbWeight>();
resultingProbList.add(dataList.remove(0));
for (ProbWeight data : dataList){ //for each event
//go through the already created event combinations and create two new for each
for(ProbWeight listed: resultingProbList){
ProbWeight firstPossibility = new ProbWeight(listed);
ProbWeight secondPossibility = new ProbWeight(listed);
firstPossibility.doesHappen(data);
secondPossibility.doesNotHappen(data);
tempList.add(firstPossibility);
tempList.add(secondPossibility);
}
resultingProbList = new ArrayList<ProbWeight>(tempList);
}
// Then sort the list by weight using sort and a comparator
It is 50% about choosing an appropriate data structure and 50% about the algorithm. Data structure - I believe TreeBidiMap will do the magic for you. You will need to implement 2 Comparators - 1 for the weight and another for the probability.
Algorithm - trivial.
Good luck!
just a few tricks to try to speed up your code:
- try to avoid non necessary objects allocation
- try to use the right constructor for your collections , in your code sample it seems that you already know the size of the collections, so use it as a parameter in the constructors to prevent useless collections resizing (and gc calls)
You may try to use a Set instead of List in order to see the ordering made on the fly.....
HTH
jerome