I want to implement a function in java that gets string input and process it. Here is how i implement it using if else statement:
class Main {
static String string_process(String s_in) {
String s_out;
if(s_in.contains("USA")){
s_out = "english";
}
else if(s_in.contains("Germany")){
s_out = "dutch";
}
else if(s_in.contains("Brazil")){
s_out = "Portuguese";
}
else {
s_out = "Uknown";
}
return s_out;
}
public static void main(String[] args) {
String process = string_process("I am from USA!");
System.out.println("I understand "+process);
}
}
I'm wondering if i can implement it hashmap. Is there any benefit of doing so in terms of complexity?
The benefit is that it requires less code to handle the cases and to add a new case.
This is what it looks like with a map:
class Main {
static Map<String, String> countryToLanguageMap = Map.of(
"USA", "english",
"Germany", "dutch",
"Brazil", "Portuguese"
);
static String string_process(String s_in) {
for (Map.Entry<String, String> entry : countryToLanguageMap.entrySet()) {
if(s_in.contains(entry.getKey())){
return entry.getValue();
}
}
return "Unknown";
}
public static void main(String[] args) {
String process = string_process("I am from USA!");
System.out.println("I understand "+process);
}
}
For example, let's suppose you want to add a new case, "UK" with "english". This is what you would have to add with the map-based version:
...
"UK", "english",
...
while with the original version:
...
else if(s_in.contains("UK")){
s_out = "english";
}
...
In short it would make the code more readable but it the runtime complexity would not change in a notable capacity. Like the other answers you would need to make a global hash-map variable to hold the key-value pairs.
Here's a possible solution using Stream API and static Map.
Note: names like string_process() are not aligned with Java naming convention and doesn't provide much information to the reader about its purpose.
Basically solution boils down to a combination of methods filter()+findFirst(), which produces an Optianal. In case if optional is empty, default value "Unknown" would be provided as a result.
private static final Map<String, String> COUNTRY_BY_LANGUAGE =
Map.of("USA", "English", "Germany", "German", "Brazil", "Portuguese");
public static String processCountry(String from) {
return COUNTRY_BY_LANGUAGE.entrySet().stream()
.filter(entry -> entry.getKey().contains(from))
.findFirst()
.map(Map.Entry::getValue)
.orElse("Unknown");
}
Simple brute-force solution without sophisticated algorithms would be:
build your HashMap
Loop through keys in hashMap, using 'indexOf' method of String checking if index >= 0
That would be n*k time complexity (n - keyword count, k - Average input string length)
public class Solution {
private static final Map<String, String> COUNTRY_TO_LANGUAGE = Map.of(
"USA", "English",
"Germany", "Deutsch",
"Brazil", "Portuguese");
private static final String UNKNOWN = "Unknown";
public static String find(String greeting) {
for(String key: COUNTRY_TO_LANGUAGE.keySet()) {
if (greeting.indexOf(key) >= 0) return COUNTRY_TO_LANGUAGE.get(key);
}
return UNKNOWN;
}
public static void main(String[] args) {
String greeting = "I am from USA!";
System.out.println("I understand " + find(greeting));
}
}
So instead of adding new if-else block you just update your map.
Related
I'm having a BiMap with a String as key and an array of Strings as value.
Now i'm trying to get with a single String (which is part of the value array) the key.
private static BiMap<String, String[]> map = ImmutableBiMap.<String, String[]>builder().build();
static {
map.put("000", new String[] {"CH", "CHE", "Switzerland"});
map.put("001", new String[] {"US", "USA", "United States of America"});
map.put("002", new String[] {"IT", "ITA", "Italy"});
}
And in the next method i'm trying to search with "CH" to get "000" (which does not work).
private static String getKey(Map<String,String[]> map, String find) {
Map<String[], String> inversedMap = map.inverse();
if(inversedMap.containsKey() {
return inversedMap.get(find);
}
return null;
}
Is there a way to 'find' the key like this, without that i need to search with an array like this: String[] find = new String[] {"CH", "CHE", "Switzerland"};
All the values and keys are unique, so there is expected only a single result.
And i'm searching always for the first value in the array, f.ex. "CH" or "US".
No, there is no way to find the key like you want. You have to either change the way you store the data to support all the different lookup method you need or go through all keys one by one (at which point making an inverse map makes no sense an you can just go through the Map entries).
A trivial approach would be a purpose built class that contains several maps.
In case you have a case to find smth. by value (not by key) then you could use for loop in case you do not worry about performance. Otherwise, you should wrap this BiMap with a wrapper and add addtional Map with val -> key:
public final class CountryCache {
private final Map<String, String[]> codeNames = new HashMap<>();
private final Map<String, String> nameCode = new HashMap<>();
{
codeNames.put("000", new String[] { "CH", "CHE", "Switzerland" });
codeNames.put("001", new String[] { "US", "USA", "United States of America" });
codeNames.put("002", new String[] { "IT", "ITA", "Italy" });
codeNames.forEach((code, names) -> Arrays.stream(names).forEach(name -> nameCode.put(name, code)));
}
private static final CountryCache INSTANCE = new CountryCache();
public static CountryCache getInstance() {
return INSTANCE;
}
private CountryCache() {
}
public String findByName(String name) {
return nameCode.get(name);
}
}
The source code I have uploaded it will join some strings value in a one line. I want a way that I can able to skip a particular string in time of string joining. Here i have stored the strings "This","is","a","test." in a string array. I want that in time of joining a particular string will be skipped. Like I want to skip "a". How can I able to do in Java? I want a generalized way that I will able to apply for any strings.
import java.util.StringJoiner;
public class Test_Joiner_for_seatPlan
{
public static void main(String[] args)
{
StringJoiner joinString = new StringJoiner( " ");
String[] testStringArray = {"This","is","a","test."};
String joinedString = null;
for(String s : testStringArray)
{
joinString.add(s);
}
joinedString = joinString.toString();
System.out.println("After Joining the String:\n"+joinedString);
}
}
Try with not equal condition with string. but its not feasible as its check everytime a value.
for(String s : testStringArray)
{
if(!s.equals("a")){
joinString.add(s);
}
}
If you have a list of values like a,b,c than you can do like this:
Set<String> exclude = new HashSet<String>(Arrays.asList("a","b","c"));
for(String s : testStringArray)
{
if(!exclude.contains(s)){
joinString.add(s);
}
}
Use Split method of string. It returns array. combine each of them to get the string.
for (String retval: Str.split("a")){
System.out.println(retval);
}
You can do it like this:
Code:
for(String s : testStringArray){
if(!s.equals("a")){ // replace 'a' with the required character or word you want to ignore
joinString.add(s);
}
}
One possible solution is to provide a Map or a List in which you store the String values that should get excluded in your join. Here is an example using a Map.
public static void main(String[] args) {
String[] testStringArray = {"This","is","a","test."};
Map<String, String> excludedStrings = createExcludingMap("a");
System.out.println("After Joining the String:\n" + join(testStringArray, excludedStrings));
}
private static String join(String[] inputData, Map<String, String> excludedStrings){
StringJoiner joinString = new StringJoiner( " ");
String joinedString = null;
for(String s : inputData)
{
if(excludedStrings.get(s) == null) // IF this return null, the String is not part of the Strings to be excluded, hence join the string
joinString.add(s);
}
joinedString = joinString.toString();
return joinString.toString();
}
private static Map<String, String> createExcludingMap(String... values) {
Map<String, String> output = new HashMap<>();
for(String s : values) { // Add each value to the map, with s as key
output.put(s, s);
}
return output;
}
output :/
After Joining the String:
This is test.
The StringJoiner is a utility class that was written specifically to be used with the new Java8 Stream functionality. The documentation of StringJoiner also refers to the Collectors.joining method. It creates the StringJoiner, and wraps it in an object suitable to pass to Stream.collect, actually a Collector.
As part of the Stream API we now also have direct support for filters, it is just a matter of employing the fluent API (fluent because we keep adding .something(...)) to add the .filter method.
You can use it as you did in your answer, but I would suggest doing it as follows:
import static java.util.stream.Collectors.joining;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class App {
public static String joinString(String [] sa,Predicate<String> filter) {
return Stream.of(sa).filter(filter).collect(joining(" "));
}
public static void main(String[] args) {
String[] testStringArray = new String [] {"This","is","a","test."};
System.out.println(joinString(testStringArray,(s)->!s.equals("a")));
}
}
I have deliberately broken it up by defining an extra method, so you can see the type of the filter passed along, exposing the Predicate. This would also make your function a little more generic making it work as you stated: 'I want a generalized way that I will able to apply for any strings'.
However if the Stream api is flexible enough that I do not see any need to abstract your own API for it. I suggest using it as-is:
import static java.util.stream.Collectors.joining;
import java.util.stream.Stream;
public class App {
public static void main(String[] args) {
System.out.println(
Stream
.of(new String [] {"This","is","a","test."})
.filter((s)->!s.equals("a"))
.collect(joining(" "))
);
}
}
I could have written most of it on a single line, but I like to break it up for readability.
I have a below program, My enum has value "2" but it just doesn't work the way it is expected. Any help will be appreciated.
package com.deepak.streams;
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Map;
public class Demo2 {
public static void main(String[] args) {
if (Scores.scoreMap.containsKey("2")) {
System.out.println("Contains Value");
} else {
System.out.println("Does not contains Value");
}
}
public enum Scores {
PASS(1), FAIL(2), MATRIC(3), PROMOTED(4);
private Integer alias;
private static final Map<Object, Scores> scoreMap;
Scores(Integer iAlias) {
alias = iAlias;
}
static {
final Map<Object, Scores> tmpMap = new HashMap<Object, Scores>();
for(final Scores scores : Scores.values()) {
tmpMap.put(scores.alias, scores);
tmpMap.put(scores.name(), scores);
}
scoreMap = ImmutableMap.copyOf(tmpMap);
}
public Integer getAlias() {
return alias;
}
public static Scores getStudentScoreEnum(Integer intVal) {
return getScores(intVal);
}
public static Scores getStudentScoreEnum(String strVal) {
return getScores(strVal);
}
private static Scores getScores(Object objVal) {
if(null != objVal && !scoreMap.containsKey(objVal)) {
throw new IllegalArgumentException("Unknown Value: " + objVal);
}
return scoreMap.get(objVal);
}
}
}
Your map key is an Object:
final Map<Object, Scores> tmpMap = new HashMap<Object, Scores>();
This means it can be any Java Object. When you use put twice:
tmpMap.put(scores.alias, scores);
tmpMap.put(scores.name(), scores);
your map will now contain two new keys. The first one is an Integer since the argument, scores.alias, is an Integer. The second one is a String.
When you perform a lookup on "2", it won't find the Integer key, because "2" is a String, and "2".equals(x) is false for any x that isn't a String. Put simply, the string "2" does not equal the Integer 2.
If you really want to look up keys using String representations of integers as the key, you'll need to use a String as a key when adding to the map. That is:
tmpMap.put(scores.alias.toString(), scores);
Your best bet is to declare the map as having a String key, instead of Object, so that problems like this will be caught at compile time--unless you really want to be able to use both Integer and String values to do your lookups.
Your map contains a binding for the integer 2, not the string "2". The name mapping will be "MATRIC" -> enumValue.
I'm developing a Java Application that reads a lot of strings data likes this:
1 cat (first read)
2 dog
3 fish
4 dog
5 fish
6 dog
7 dog
8 cat
9 horse
...(last read)
I need a way to keep all couple [string, occurrences] in order from last read to first read.
string occurrences
horse 1 (first print)
cat 2
dog 4
fish 2 (last print)
Actually i use two list:
1) List<string> input; where i add all data
In my example:
input.add("cat");
input.add("dog");
input.add("fish");
...
2)List<string> possibilities; where I insert the strings once in this way:
if(possibilities.contains("cat")){
possibilities.remove("cat");
}
possibilities.add("cat");
In this way I've got a sorted list where all possibilities.
I use it like that:
int occurrence;
for(String possible:possibilities){
occurrence = Collections.frequency(input, possible);
System.out.println(possible + " " + occurrence);
}
That trick works good but it's too slow(i've got millions of input)... any help?
(English isn’t my first language, so please excuse any mistakes.)
Use a Map<String, Integer>, as #radoslaw pointed, to keep the insertion sorting use LinkedHashMap and not a TreeMap as described here:
LinkedHashMap keeps the keys in the order they were inserted, while a TreeMap is kept sorted via a Comparator or the natural Comparable ordering of the elements.
Imagine you have all the strings in some array, call it listOfAllStrings, iterate over this array and use the string as key in your map, if it does not exists, put in the map, if it exists, sum 1 to actual result...
Map<String, Integer> results = new LinkedHashMap<String, Integer>();
for (String s : listOfAllStrings) {
if (results.get(s) != null) {
results.put(s, results.get(s) + 1);
} else {
results.put(s, 1);
}
}
Make use of a TreeMap, which will keep ordering on the keys as specified by the compare of your MyStringComparator class handling MyString class which wraps String adding insertion indexes, like this:
// this better be immutable
class MyString {
private MyString() {}
public static MyString valueOf(String s, Long l) { ... }
private String string;
private Long index;
public hashcode(){ return string.hashcode(); }
public boolean equals() { // return rely on string.equals() }
}
class MyStringComparator implements Comparator<MyString> {
public int compare(MyString s1, MyString s2) {
return -s1.getIndex().compareTo(s2.gtIndex());
}
}
Pass the comparator while constructing the map:
Map<MyString,Integer> map = new TreeMap<>(new MyStringComparator());
Then, while parsing your input, do
Long counter = 0;
while (...) {
MyString item = MyString.valueOf(readString, counter++);
if (map.contains(item)) {
map.put(map.get(item)+1);
} else {
map.put(item,1);
}
}
There will be a lot of instantiation because of the immutable class, and the comparator will not be consistent with equals, but it should work.
Disclaimer: this is untested code just to show what I'd do, I'll come back and recheck it when I get my hands on a compiler.
Here is the complete solution for your problem,
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DataDto implements Comparable<DataDto>{
public int count = 0;
public String string;
public long lastSeenTime;
public DataDto(String string) {
this.string = string;
this.lastSeenTime = System.currentTimeMillis();
}
public boolean equals(Object object) {
if(object != null && object instanceof DataDto) {
DataDto temp = (DataDto) object;
if(temp.string != null && temp.string.equals(this.string)) {
return true;
}
}
return false;
}
public int hashcode() {
return string.hashCode();
}
public int compareTo(DataDto o) {
if(o != null) {
return o.lastSeenTime < this.lastSeenTime ? -1 : 1;
}
return 0;
}
public String toString() {
return this.string + " : " + this.count;
}
public static final void main(String[] args) {
String[] listOfAllStrings = {"horse", "cat", "dog", "fish", "cat", "fish", "dog", "cat", "horse", "fish"};
Map<String, DataDto> results = new HashMap<String, DataDto>();
for (String s : listOfAllStrings) {
DataDto dataDto = results.get(s);
if(dataDto != null) {
dataDto.count = dataDto.count + 1;
dataDto.lastSeenTime = System.nanoTime();
} else {
dataDto = new DataDto(s);
results.put(s, dataDto);
}
}
List<DataDto> finalResults = new ArrayList<DataDto>(results.values());
System.out.println(finalResults);
Collections.sort(finalResults);
System.out.println(finalResults);
}
}
Ans
[horse : 1, cat : 2, fish : 2, dog : 1]
[fish : 2, horse : 1, cat : 2, dog : 1]
I think this solution will be suitable for your requirement.
If you know that your data is not going to exceed your memory capacity when you read it all into memory, then the solution is simple - using a LinkedList or a and a LinkedHashMap.
For example, if you use a Linked list:
LinkedList<String> input = new LinkedList();
You then proceed to use input.add() as you did originally. But when the input list is full, you basically use Jordi Castilla's solution - but put the entries in the linked list in reverse order. To do that, you do:
Iterator<String> iter = list.descendingIterator();
LinkedHashMap<String,Integer> map = new LinkedHashMap<>();
while (iter.hasNext()) {
String s = iter.next();
if ( map.containsKey(s)) {
map.put( s, map.get(s) + 1);
} else {
map.put(s, 1);
}
}
Now, the only real difference between his solution and mine is that I'm using list.descendingIterator() which is a method in LinkedList that gives you the entries in backwards order, from "horse" to "cat".
The LinkedHashMap will keep the proper order - whatever was entered first will be printed first, and because we entered things in reverse order, then whatever was read last will be printed first. So if you print your map the result will be:
{horse=1, cat=2, dog=4, fish=2}
If you have a very long file, and you can't load the entire list of strings into memory, you had better keep just the map of frequencies. In this case, in order to keep the order of entry, we'll use an object such as this:
private static class Entry implements Comparable<Entry> {
private static long nextOrder = Long.MIN_VALUE;
private String str;
private int frequency = 1;
private long order = nextOrder++;
public Entry(String str) {
this.str = str;
}
public String getString() {
return str;
}
public int getFrequency() {
return frequency;
}
public void updateEntry() {
frequency++;
order = nextOrder++;
}
#Override
public int compareTo(Entry e) {
if ( order > e.order )
return -1;
if ( order < e.order )
return 1;
return 0;
}
#Override
public String toString() {
return String.format( "%s: %d", str, frequency );
}
}
The trick here is that every time you update the entry (add one to the frequency), it also updates the order. But the compareTo() method orders Entry objects from high order (updated/inserted later) to low order (updated/inserted earlier).
Now you can use a simple HashMap<String,Entry> to store the information as you read it (I'm assuming you are reading from some sort of scanner):
Map<String,Entry> m = new HashMap<>();
while ( scanner.hasNextLine() ) {
String str = scanner.nextLine();
Entry entry = m.get(str);
if ( entry == null ) {
entry = new Entry(str);
m.put(str, entry);
} else {
entry.updateEntry();
}
}
Scanner.close();
Now you can sort the values of the entries:
List<Entry> orderedList = new ArrayList<Entry>(m.values());
m = null;
Collections.sort(orderedList);
Running System.out.println(orderedList) will give you:
[horse: 1, cat: 2, dog: 4, fish: 2]
In principle, you could use a TreeMap whose keys contained the "order" stuff, rather than a plain HashMap like this followed by sorting, but I prefer not having either mutable keys in a map, nor changing the keys constantly. Here we are only changing the values as we fill the map, and each key is inserted into the map only once.
What you could do:
Reverse the order of the list using
Collections.reverse(input). This runs in linear time - O(n);
Create a Set from the input list. A Set garantees uniqueness.
To preserve insertion order, you'll need a LinkedHashSet;
Iterate over this set, just as you did above.
Code:
/* I don't know what logic you use to create the input list,
* so I'm using your input example. */
List<String> input = Arrays.asList("cat", "dog", "fish", "dog",
"fish", "dog", "dog", "cat", "horse");
/* by the way, this changes the input list!
* Copy it in case you need to preserve the original input. */
Collections.reverse(input);
Set<String> possibilities = new LinkedHashSet<String>(strings);
for (String s : possibilities) {
System.out.println(s + " " + Collections.frequency(strings, s));
}
Output:
horse 1
cat 2
dog 4
fish 2
I have a list of Strings:
List<String> terms = ["Coding is great", "Search Engines are great", "Google is a nice search engine"]
How do I get the frequency of each word in the list:
E.g.{Coding:1, Search:2, Engines:1, engine:1, ....}
Here is my Code:
Map<String, Integer> wordFreqMap = new HashMap<>();
for (String contextTerm : term.getContexTerms() )
{
String[] wordsArr = contextTerm.split(" ");
for (String word : wordsArr)
{
Integer freq = wordFreqMap.get(word); //this line is getting reset every time I goto a new COntexTerm
freq = (freq == null) ? 1: ++freq;
wordFreqMap.put(word, freq);
}
}
An idiomatic solution with Java 8 streams:
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class SplitWordCount
{
public static void main(String[] args)
{
List<String> terms = Arrays.asList(
"Coding is great",
"Search Engines are great",
"Google is a nice search engine");
Map<String, Integer> result = terms.parallelStream().
flatMap(s -> Arrays.asList(s.split(" ")).stream()).
collect(Collectors.toConcurrentMap(
w -> w.toLowerCase(), w -> 1, Integer::sum));
System.out.println(result);
}
}
Note that you may have to think about whether upper/lower case of the strings should play a role. This one onverts the strings to lower case, and uses them as the keys for the final map. The result is then:
{coding=1, a=1, search=2, are=1, engine=1, engines=1,
is=2, google=1, great=2, nice=1}
public static void main(String[] args) {
String msg="Coding is great search Engines are great Google is a nice search engine";
ArrayList<String> list2 = new ArrayList<>();
Map map = new HashMap();
list2.addAll((List)Arrays.asList(msg.split(" ")));
String n[]=msg.split(" ");
int f=0;
for(int i=0;i<n.length;i++){
f=Collections.frequency(list2, n[i]);
map.put(n[i],f);
}
System.out.println("values are "+map);
}
Because the answer with Java 8, while being good, does not show you how to parallel it in Java 7 (and beside default implementation is the same than stream), here is an example:
public static void main(final String[] args) throws InterruptedException {
final ExecutorService service = Executors.newFixedThreadPool(10);
final List<String> terms = Arrays.asList("Coding is great", "Search Engines are great",
"Google is a nice search engine");
final List<Callable<String[]>> callables = new ArrayList<>(terms.size());
for (final String term : terms) {
callables.add(new Callable<String[]>() {
#Override
public String[] call() throws Exception {
System.out.println("splitting word: " + term);
return term.split(" ");
}
});
}
final ConcurrentMap<String, AtomicInteger> counter = new ConcurrentHashMap<>();
final List<Callable<Void>> callables2 = new ArrayList<>(terms.size());
for (final Future<String[]> future : service.invokeAll(callables)) {
callables2.add(new Callable<Void>() {
#Override
public Void call() throws Exception {
System.out.println("counting word");
// invokeAll implies that the future finished it work
for (String word : future.get()) {
String lc = word.toLowerCase();
// here it get tricky. Two thread might add the same word.
AtomicInteger actual = counter.get(lc);
if (null == actual) {
final AtomicInteger nv = new AtomicInteger();
actual = counter.putIfAbsent(lc, nv);
if (null == actual) {
actual = nv; // nv got added.
}
}
actual.incrementAndGet();
}
return null;
}
});
}
service.invokeAll(callables2);
service.shutdown();
System.out.println(counter);
}
Yes, Java 8 simplifies the work !
No, I tested it but don't know if it is better than simple loops nor if it perfectly threadsafe.
(And seeing how you define your list, are not coding in Groovy ? There exists parallelism support in Groovy).