This code is part of within a method. The code go through two lists using two for loop. I want to see whether there is a possibility of using multi thread to speed up this process for the two loops. My concern is how to make it thread safe.
EDITTED: more complete code
static class Similarity {
double similarity;
String seedWord;
String candidateWord;
public Similarity(double similarity, String seedWord, String candidateWord) {
this.similarity = similarity;
this.seedWord = seedWord;
this.candidateWord = candidateWord;
}
public double getSimilarity() {
return similarity;
}
public String getSeedWord() {
return seedWord;
}
public String getCandidateWord() {
return candidateWord;
}
}
static class SimilarityTask implements Callable<Similarity> {
Word2Vec vectors;
String seedWord;
String candidateWord;
Collection<String> label1;
Collection<String> label2;
public SimilarityTask(Word2Vec vectors, String seedWord, String candidateWord, Collection<String> label1, Collection<String> label2) {
this.vectors = vectors;
this.seedWord = seedWord;
this.candidateWord = candidateWord;
this.label1 = label1;
this.label2 = label2;
}
#Override
public Similarity call() {
double similarity = cosineSimForSentence(vectors, label1, label2);
return new Similarity(similarity, seedWord, candidateWord);
}
}
Now, is this 'compute' thread safe? There are 3 variables involved:
1) vectors;
2) toeknizerFactory;
3) similarities;
public static void compute() throws Exception {
File modelFile = new File("sim.bin");
Word2Vec vectors = WordVectorSerializer.readWord2VecModel(modelFile);
TokenizerFactory tokenizerFactory = new TokenizerFactory()
List<String> seedList = loadSeeds();
List<String> candidateList = loadCandidates();
log.info("Computing similarity: ");
ExecutorService POOL = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
List<Future<Similarity>> tasks = new ArrayList<>();
int totalCount=0;
for (String seed : seedList) {
Collection<String> label1 = getTokens(seed.trim(), tokenizerFactory);
if (label1.isEmpty()) {
continue;
}
for (String candidate : candidateList) {
Collection<String> label2 = getTokens(candidate.trim(), tokenizerFactory);
if (label2.isEmpty()) {
continue;
}
Callable<Similarity> callable = new SimilarityTask(vectors, seed, candidate, label1, label2);
tasks.add(POOL.submit(callable));
log.info("TotalCount:" + (++totalCount));
}
}
Map<String, Set<String>> similarities = new HashMap<>();
int validCount = 0;
for (Future<Similarity> task : tasks) {
Similarity simi = task.get();
Double similarity = simi.getSimilarity();
String seedWord = simi.getSeedWord();
String candidateWord = simi.getCandidateWord();
Set<String> similarityWords = similarities.get(seedWord);
if (similarity >= 0.85) {
if (similarityWords == null) {
similarityWords = new HashSet<>();
}
similarityWords.add(candidateWord);
log.info(seedWord + " " + similarity + " " + candidateWord);
log.info("ValidCount: " + (++validCount));
}
if (similarityWords != null) {
similarities.put(seedWord, similarityWords);
}
}
}
Added one more relevant method, which is used by the call() method:
public static double cosineSimForSentence(Word2Vec vectors, Collection<String> label1, Collection<String> label2) {
try {
return Transforms.cosineSim(vectors.getWordVectorsMean(label1), vector.getWordVectorsMean(label2));
} catch (Exception e) {
log.warn("OOV: " + label1.toString() + " " + label2.toString());
//e.getMessage();
//e.printStackTrace();
return 0.0;
}
}
(Answer updated for changed question.)
In general you should profile the code before attempting to optimise it, particularly if it is quite complex.
For threading you need to identify which mutable state is shared between threads. Ideally as much as that as possible before resorting to locks and concurrent data structures. Mutable state that is contained within one thread isn't a problem as such. Immutables are great.
I assume nothing passed to your task gets modified. It's tricky to tell. final on fields is a good idea. Collections can be placed in unmodifiable wrappers, though that doesn't stop them being modified via other references and does now show itself in static types.
Assuming you don't break up the inner loop, the only shared mutable state appears to be similarities and the values it contains.
You may or may not find you still end up doing too much serially and need to change similarities to become concurrent
ConcurrentMap<String, Set<String>> similarities = new ConcurrentHashMap<>();
The get and put of similarities will need to be thread-safe. I suggest always creating the Set.
Set<String> similarityWords = similarities.getOrDefault(seed, new HashSet<>());
or
Set<String> similarityWords = similarities.computeIfAbsent(seed, key -> new HashSet<>());
You could use a thread-safe Set (for instance with Collections.synchronizedSet), but I suggest holding a relevant lock for the entire inner loop.
synchronized (similarityWords) {
...
}
If you wanted to create similarityWords lazily then it would be "more fun".
Related
My problem is:
I have class:
public class AtomicStringBuilder {
private final AtomicReference<StringBuilder> sbRef;
}
I need to add new characters to StringBuilder concurrently and atomically. But problem is, only last 128 characters should be in this object. I can't use StringBuffer, because operation should be non-blocking.
So,there are two operations:
First: check if StringBuilder already has 128 chars.
Second: if it has not -> add new char, if it has -> delete first char and add new char.
Is there a way to make this two or three operations atomic?
I made this method, but it doesn't work:
public void append(String string) {
this.sbRef.getAndUpdate(ref -> {
if (ref.length() < 128) {
ref.append(string);
} else {
ref.append(string).delete(0, ref.length() - 128);
}
return ref;
});
}
For testing I created this method:
public void test() {
AtomicStringBuilder atomicStringBuilder = new AtomicStringBuilder();
Random random = new Random();
Stream<Integer> infiniteStream = Stream.iterate(0, i -> random.nextInt(10));
infiniteStream.parallel()
.limit(100000)
.forEach(integer -> atomicStringBuilder.append(String.valueOf(integer)));
assertEquals(128, atomicStringBuilder.getSb().get().length());
}
This is not a real prolem, I can change AtomicReference with anything else which will work. The task is to create operation that will be lock-free and without race conditions
Here's a solution with immutable Strings.
If you use AtomicReference you need to return a new reference rather than mutating the object the reference points to. Atomically comparing the current and expected value of the reference is the only way to know that it hasn't been updated by another thread.
getAndUpdate does this:
get the current reference
apply the lambda to the reference, getting a new reference
if the current reference hasn't changed, atomically set it to the new reference, otherwise go back to 1.
public class App {
static class AtomicStringBuilder {
public final AtomicInteger counter = new AtomicInteger();
public final AtomicReference<String> sbRef = new AtomicReference<>("");
public void append(String string) {
this.sbRef.getAndUpdate(ref -> {
counter.getAndIncrement();
if (ref.length() < 128) {
return ref + string;
} else {
String s = ref + string;
return s.substring(s.length() - 128);
}
});
}
}
static void test() {
AtomicStringBuilder atomicStringBuilder = new AtomicStringBuilder();
Random random = new Random();
Stream<Integer> infiniteStream = Stream.iterate(0, i -> random.nextInt(10));
infiniteStream.parallel()
.limit(100000)
.forEach(integer -> atomicStringBuilder.append(String.valueOf(integer)));
if (128 != atomicStringBuilder.sbRef.get().length()) {
System.out.println("failed ");
}
System.out.println(atomicStringBuilder.sbRef.get());
System.out.println(atomicStringBuilder.counter.get());
}
public static void main(String[] args) {
test();
}
}
I've added a counter to the lambda. The value it shows after running this program will be more than 100,000, because concurrent updates forced retries.
My Rest API works fine. However, I'm concerned about concurrency issues, though I've tested via scripts and have yet to see any. In my studies, I encountered some material with regards to utilizing Atomic Values with concurrentHasMap to avoid what amounts to dirty reads. My questions is twofold. First, should I be concerned, given my implementation? Second, if I should be, what would be the most prudent way to implement Atomic values, if indeed I should? I've contemplated dropping the wrapper class for the RestTemplate and simply passing a String back to the Angular 4 component as a catalyst for speed, but given I may use the value objects elsewhere, I'm hesitant. See, implementation below.
#Service
#EnableScheduling
public class TickerService implements IQuoteService {
#Autowired
private ApplicationConstants Constants;
private ConcurrentHashMap<String,Quote> quotes = new ConcurrentHashMap<String, Quote>();
private ConcurrentHashMap<String,LocalDateTime> quoteExpirationQueue = new ConcurrentHashMap<String, LocalDateTime>();
private final RestTemplate restTemplate;
public TickerService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public Quote getQuote(String symbol) {
if (this.quotes.containsKey(symbol)){
Quote q = (Quote)this.quotes.get(symbol);
//Update Expiration
LocalDateTime ldt = LocalDateTime.now();
this.quoteExpirationQueue.put(symbol, ldt.plus(Constants.getQuoteExpirationMins(),ChronoUnit.MINUTES));
return q;
} else {
QuoteResponseWrapper qRes = this.restTemplate.getForObject( Constants.getRestURL(symbol), QuoteResponseWrapper.class, symbol);
ArrayList<Quote> res = new ArrayList<Quote>();
res = qRes.getQuoteResponse().getResult();
//Add to Cache
quotes.put(symbol, res.get(0));
//Set Expiration
LocalDateTime ldt = LocalDateTime.now();
this.quoteExpirationQueue.put(symbol, ldt.plus(Constants.getQuoteExpirationMins(),ChronoUnit.MINUTES));
return res.get(0);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public ConcurrentHashMap<String,Quote> getQuotes(){
return this.quotes;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#Scheduled(fixedDelayString = "${application.quoteRefreshFrequency}")
public void refreshQuotes(){
if (quoteExpirationQueue.isEmpty()) {
return;
}
LocalDateTime ldt = LocalDateTime.now();
//Purge Expired Quotes
String expiredQuotes = quoteExpirationQueue.entrySet().stream().filter(x -> x.getValue().isBefore(ldt)).map(p -> p.getKey()).collect(Collectors.joining(","));
if (!expiredQuotes.equals("")) {
this.purgeQuotes(expiredQuotes.split(","));
}
String allQuotes = quoteExpirationQueue.entrySet().stream().filter(x -> x.getValue().isAfter(ldt)).map(p -> p.getKey()).collect(Collectors.joining(","));
List<String> qList = Arrays.asList(allQuotes.split(","));
Stack<String> stack = new Stack<String>();
stack.addAll(qList);
// Break Requests Into Manageable Chunks using property file settings
while (stack.size() > Constants.getMaxQuoteRequest()) {
String qSegment = "";
int i = 0;
while (i < Constants.getMaxQuoteRequest() && !stack.isEmpty()) {
qSegment = qSegment.concat(stack.pop() + ",");
i++;
}
logger.debug(qSegment.substring(0, qSegment.lastIndexOf(",")));
this.updateQuotes(qSegment);
}
// Handle Remaining Request Delta
if (stack.size() < Constants.getMaxQuoteRequest() && !stack.isEmpty()) {
String rSegment = "";
while (!stack.isEmpty()){
rSegment = rSegment.concat(stack.pop() + ",");
}
logger.debug(rSegment);
this.updateQuotes(rSegment.substring(0, rSegment.lastIndexOf(",")));
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private void updateQuotes(String symbols) {
if (symbols.equals("")) {
return;
}
System.out.println("refreshing -> " + symbols);
QuoteResponseWrapper qRes = this.restTemplate.getForObject( Constants.getRestURL(symbols), QuoteResponseWrapper.class, symbols);
for (Quote q : qRes.getQuoteResponse().getResult()) {
this.quotes.put(q.getSymbol(), q);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private void purgeQuotes(String[] symbols) {
for (String q : symbols) {
System.out.println("purging -> " + q);
this.quotes.remove(q);
this.quoteExpirationQueue.remove(q);
}
}
}
Changed implementation of IQuoteService and implementation TickerService to use concurrenHashMap with Atomic References:
#Autowired
private ApplicationConstants Constants;
private ConcurrentHashMap<AtomicReference<String>,AtomicReference<Quote>>
quotes = new ConcurrentHashMap<AtomicReference<String>,AtomicReference<Quote>> ();
private ConcurrentHashMap<AtomicReference<String>,AtomicReference<LocalDateTime>> quoteExpirationQueue = new ConcurrentHashMap<AtomicReference<String>,AtomicReference<LocalDateTime>>();
private final RestTemplate restTemplate;
The code works precisely as it did prior with the with the new implementation being that it "should" ensure that updates to values are not partially read prior to being completely written, and the values obtained should be consistent. Given, I could find no sound examples and acquire no answers on this topic, I will test this and post any issues I find.
The main concurrency risks with this code come about if refreshQuotes() was to be called concurrently. If this is a risk, then refreshQuotes just needs to be marked as synchronized.
Working on the premise that refreshQuotes() is only ever called once at a time, and that Quote/LocalDateTime are both immutable; then the question appears to be does updating immutable values within a ConcurrentHashMap risk dirty reads/writes. The answer is no, the values are immutable and ConcurrentHashMap handles the concurrency of updating the references.
For more information, I strongly recommend reading JSR133 (The Java Memory Model). It covers in some detail when data will and will not become visible between threads. Doug Lea's JSR133 Cookbook will almost certainly give you far more information than you ever wanted to know.
I don't know if this is possible in Java but I was wondering if it is possible to use an object in Java to return multiple values without using a class.
Normally when I want to do this in Java I would use the following
public class myScript {
public static void main(String[] args) {
// initialize object class
cl_Object lo_Object = new cl_Object(0, null);
// populate object with data
lo_Object = lo_Object.create(1, "test01");
System.out.println(lo_Object.cl_idno + " - " + lo_Object.cl_desc);
//
// code to utilize data here
//
// populate object with different data
lo_Object = lo_Object.create(2, "test02");
System.out.println(lo_Object.cl_idno + " - " + lo_Object.cl_desc);
//
// code to utilize data here
//
}
}
// the way I would like to use (even though it's terrible)
class cl_Object {
int cl_idno = 0;
String cl_desc = null;
String cl_var01 = null;
String cl_var02 = null;
public cl_Object(int lv_idno, String lv_desc) {
cl_idno = lv_idno;
cl_desc = lv_desc;
cl_var01 = "var 01";
cl_var02 = "var 02";
}
public cl_Object create(int lv_idno, String lv_desc) {
cl_Object lo_Object = new cl_Object(lv_idno, lv_desc);
return lo_Object;
}
}
// the way I don't really like using because they get terribly long
class Example {
int idno = 0;
String desc = null;
String var01 = null;
String var02 = null;
public void set(int idno, String desc) {
this.idno = idno;
this.desc = desc;
var01 = "var 01";
var02 = "var 02";
}
public int idno() {
return idno;
}
public String desc() {
return desc;
}
public String var01() {
return var01;
}
public String var02() {
return var02;
}
}
Which seems like a lot of work considering in Javascript (I know they are different) I can achieve the same effect just doing
var lo_Object = f_Object();
console.log(lo_Object["idno"] + " - " + lo_Object[desc]);
function f_Object() {
var lo_Object = {};
lo_Object = {};
lo_Object["idno"] = 1;
lo_Object["desc"] = "test01";
return lo_Object;
}
NOTE
I know the naming convention is wrong but it is intentional because I have an informix-4gl program that runs with this program so the coding standards are from the company I work for
The best way to do this is to use HashMap<String, Object>
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<String, Object> person =
new HashMap<String, Object>();
// add elements dynamically
person.put("name", "Lem");
person.put("age", 46);
person.put("gender", 'M');
// prints the name value
System.out.println(person.get("name"));
// asures that age element is of integer type before
// printing
System.out.println((int)person.get("age"));
// prints the gender value
System.out.println(person.get("gender"));
// prints the person object {gender=M, name=Lem, age=46}
System.out.println(person);
}
}
The advantage of doing this is that you can add elements as you go.
The downside of this is that you will lose type safety like in the case of the age. Making sure that age is always an integer has a cost. So to avoid this cost just use a class.
No, there is no such a feature, you have to type out the full type name(class name).
Or use may use val :
https://projectlombok.org/features/val.html
Also, if you use IntelliJ IDEA
try this plugin :
https://bitbucket.org/balpha/varsity/wiki/Home
I am not sure if it's possible with Java. Class is the primitive structure to generate Object. We need a Class to generate object. So, for the above code, i don't think there is a solution.
Java methods only allow one return value. If you want to return multiple objects/values consider returning one of the collections. Map, List, Queue, etc.
The one you choose will depend on your needs. For example, if you want to store your values as key-value pairs use a Map. If you just want to store values sequentially, use a list.
An example with a list:
list<Object> myList = new ArrayList<Object>();
myList.add("Some value");
return myList;
As a side note, your method create is redundant. You should use getters and setters to populate the object, or populate it through the constructor.
cl_Object lo_Object = new cl_Object(1, "test01");
The way you have it set up right now, you're creating one object to create another of the same type that has the values you want.
Your naming convention is also wrong. Please refer to Java standard naming convention:
http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-135099.html#367
Good evening Java wizards,
I am fairly new to Java, and would like to be educated on the mistake I am not able to resolve after some hours of research.
Assume a program that has the main thread with some variables that could dynamically read/lookup value in a Map of values. This Map is shared by threads and objects accross the application.
There would be one or more separate threads updating the Map with values thus the map will be synchronized - I am considering ConcurrentHashMap.
When the reading thread arrives to one of the variables with the dynamic values it will reach in to the cross-thread shared Map and retreive its latest value against its key.
I have some prototype code here - it compliles and seam to be running as expected. However it is set to work only with a String variable and values.
Here is the code:
<code>
/*
* Cross thread / cross object shared Map
* some threads update the map
* other threads read the map for most current values (updated by other threads)
* associated with the provided key
*/
public class CrossThreadUpdatableValues {
private static final Map<Integer, String> list = new ConcurrentHashMap<>();
// return the last updated value
// from the map
public static String getValue(Integer key) {
return list.get(key);
}
// update/write the passed in value in to the map
// (in to the element having the given key)
public static void setValue(Integer key, String ev) {
list.put(key, ev);
}
}
/*
* Thread 1
* 10 loops that update the cross thread / cross object shared Map
* with unique values
*/
public class Thread1 extends Thread {
private final int delay;
private final int threadNum = 1;
Thread1(int delay) {
this.delay = delay;
}
public void run(){
for (int i=0;i<10;i++) {
String v1 = threadNum + "-AAA"; Integer key1 = 1;
String v2 = threadNum + "-BBB"; Integer key2 = 2;
String v3 = threadNum + "-CCC"; Integer key3 = 3;
CrossThreadUpdatableValues.setValue(key1, v1);
CrossThreadUpdatableValues.setValue(key2, v2);
CrossThreadUpdatableValues.setValue(key3, v3);
System.out.println("Map values updated by Thread " + threadNum + ", loop " + i);
try {
Thread.sleep(this.delay);
} catch (InterruptedException ex) {
Logger.getLogger(Thread2.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
/*
* Thread 2 (similar to Thread 1)
* 10 loops that update the cross thread / cross object shared Map
* with unique values
*/
public class Thread2 extends Thread {
private final int delay;
private final int threadNum = 2;
Thread2(int delay) {
this.delay = delay;
}
public void run(){
for (int i=0;i<10;i++) {
String v1 = threadNum + "-XXX"; Integer key1 = 1;
String v2 = threadNum + "-YYY"; Integer key2 = 2;
String v3 = threadNum + "-ZZZ"; Integer key3 = 3;
CrossThreadUpdatableValues.setValue(key1, v1);
CrossThreadUpdatableValues.setValue(key2, v2);
CrossThreadUpdatableValues.setValue(key3, v3);
System.out.println("Map values updated by Thread " + threadNum + ", loop " + i);
try {
Thread.sleep(this.delay);
} catch (InterruptedException ex) {
Logger.getLogger(Thread2.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
/*
* Reading thread -
* 20 loops that read the cross thread / crooss object shared Map
* for the most current values updated by other threads in various intervals
*/
public class ThreadRead extends Thread {
private final int delay;
private final int threadNum = 0;
ThreadRead(int delay) {
this.delay = delay;
}
public void run(){
Integer key1 = 1;
Integer key2 = 2;
Integer key3 = 3;
for (int i=0;i<20;i++) {
String v1 = CrossThreadUpdatableValues.getValue(key1);
String v2 = CrossThreadUpdatableValues.getValue(key1);
String v3 = CrossThreadUpdatableValues.getValue(key1);
System.out.println(" - - - Map values read by (reading) thread " + threadNum + ", loop " + i + "; v1 = " + v2 + "; v1 = " + v2 + "; v3 = " + v3);
try {
Thread.sleep(this.delay);
} catch (InterruptedException ex) {
Logger.getLogger(Thread2.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
/**
*
* Main test class - start of test run
*/
public class Test_Main {
public static void main(String[] args) {
// start thread that populates the shared Map with unique values
// in 5 second intervals (10x)
Thread1 thread1 = new Thread1(5000);
thread1.start();
// start thread that populates the shared Map with unique values
// in 10 second intervals (10x)
Thread2 thread2 = new Thread2(10000);
thread2.start();
// start thread that reads the shared Map of unique values
// - the latest updates from any previous thread
// in 5 second intervals (20x)
ThreadRead threadRead = new ThreadRead(5000);
threadRead.start();
}
}
</code>
These dynamic variables will naturally be of different types (Integers, Strings, etc.), so I am considering using generics, BUT that gives me the uneducated headache. As the Map needs to be shared between all involved classes and threads, it needs to be declared static and Java won't permit the use of generics on this static Map.
Here is the modification of above class CrossThreadUpdatableValues using generics - that will NOT work but a hint what I am trying to achieve:
<code>
/*
* Cross thread / crooss object shared Map
* some threads update the map
* other threads read the map for most current values (udated by other threads)
* associated with the provided key
*/
public class CrossThreadUpdatableValues<K, V> {
private static final Map<K, V> list = new ConcurrentHashMap<>();
// return the last updated value
// from the map
public static V getValue(K key) {
return list.get(key);
}
// update/write the passed in value in to the map
// (in to the element having the given key)
public static void setValue(K key, V v) {
list.put(key, v);
}
}
</code>
I would appreciate your input into how to approach this in a thread save manner, allow handling various types of variables (I know Object could be used instead of V but is it the right way to go?) and perhaps point out some hints or references on a solution or a better approach.
Thank you
If you want to save different types in the Map you will need to use Object, using generics will force a design where a specific map is created for each combination of <K, V>.
If you want to store in the same map differents kind of objects, you don´t need generics, declare your map as
Map <String, Object>
Otherwise, you can use an interface like this
package a;
public interface ICrossThreadUpdatableValues<K, V> {
// return the last updated value
// from the map
V getValue(K key);
// update/write the passed in value in to the map
// (in to the element having the given key)
void setValue(K key, V v);
}
And then do a concrete implementation for the desired types like this
package a;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/*
* Cross thread / crooss object shared Map
* some threads update the map
* other threads read the map for most current values (udated by other threads)
* associated with the provided key
*/
public class IntegerCrossThreadUpdatableValues implements ICrossThreadUpdatableValues<String, Integer> {
private final Map<String, Integer> list = new ConcurrentHashMap<>();
private static IntegerCrossThreadUpdatableValues instance;
private IntegerCrossThreadUpdatableValues() {
}
// return the last updated value
// from the map
public Integer getValue(String key) {
return list.get(key);
}
// update/write the passed in value in to the map
// (in to the element having the given key)
public void setValue(String key, Integer v) {
list.put(key, v);
}
public static IntegerCrossThreadUpdatableValues getInstance() {
if (instance == null) {
instance = new IntegerCrossThreadUpdatableValues();
}
return instance;
}
}
Note that the implementation defines a singleton pattern, you can´t instantiate the class outside of it and you can get always the same object calling 'getInstance'
This is what I have and I'm new to Java:
import java.util.*;
public class WordPairs
{
// ArrayList<String> names = new ArrayList<String>();
// ArrayList<String> meanings = new ArrayList<String>();
//names.add("empty");
//meanings.add("empty");
String searchName;
String searchMeaning;
String names[] = new String[25];
String meanings[] = new String[25];
int Q = 1;
public void setWordAdd(String name,String meaning)
{
names[Q] = name;
meanings[Q] = meaning;
Q = Q++;
}
public void setWordDelete(String name)
{
for(int i=0;i<names.length;i++)
{
String check = names[i];
if(check == name)
{
String meanings = meanings.remove(i);
}
}
}
public void setWordSearch(String name)
{
int a = 1;
for(i=0;i<names.size();i++)
{
string check = names.get(i);
if(check == name)
{
searchName = names.get(i);
searchMeaning = meanings.get(i);
a = 0;
}
}
if(a == 1)
{
searchName = "word can not be found";
searchMeaning = "meaning can not be found";
}
}
public String getSearchName()
{
return searchName;
}
public String getSearchMeaning()
{
return searchMeaning;
}
}
http://download.oracle.com/javase/1.4.2/docs/api/java/util/HashMap.html
I believe you are a beginner, and by reading your code I assume you are not familiar with built-in collections yet (despite of some shy ArrayList attempt). That's ok, no one is born with that knowledge. Here is some reading for you:
Map interface: http://download.oracle.com/javase/6/docs/api/java/util/Map.html
All of implementations of this interface provide functionality you need, but in different ways. For example, HashMap is useful in most cases, but if you need keys (in your code - "names") sorted, TreeMap would be better. LinkedHashMap "remembers" the order in which keys were inserted, and so on. It's very interesting and informative reading and you definitely should know about maps, because they are among most useful Java classes.
Here is introduction to Java Collections Framework in general:
http://download.oracle.com/javase/tutorial/collections/index.html
Be sure to read it, because collections are irreplacable in even simplest Java applications.
I hope that helps, and good luck!