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.
As a note beforehand: I think about this as opportunity to simplify tests, so it doesn't have to be cutting-edge efficient.
Lets say I have this unit test:
#Test
public void testVoiceOutput() {
Stone testStone = new Stone();
testStone.talkTo("Hey, how are you doing?");
assertNull("Stone's answer", testStone.listenForReply());
}
When I now add possibilities of conditions, the stone can be in, this gets more and more unreadable:
#Test
public void testVoiceOutput() {
Stone testStone = new Stone();
for (String currentEnvironment : new String[]{"FLOOR", "MEADOW", "STREET"}) {
testStone.setEnvironment(currentEnvironment);
testStone.talkTo("Hey, how are you doing?");
assertNull("Stone's answer, environment=" + currentEnvironment, testStone.listenForReply());
}
}
and
#Test
public void testVoiceOutput() {
Stone testStone = new Stone();
for (String timeOfDay : new String[]{"NIGHT", "NOON", "TEATIME", "DINNERTIME"}) {
for (String currentWeather : new String[]{"RAINY", "FOGGY", "SUNNY", "SNOWY"}) {
for (String currentEnvironment : new String[]{"FLOOR", "MEADOW", "STREET"}) {
testStone.setTimeOfDay(timeOfDay);
testStone.setWeather(currentWeather);
testStone.setEnvironment(currentEnvironment);
testStone.talkTo("Hey, how are you doing?");
assertNull("Stone's answer, environment=" + currentEnvironment + ", weather=" + currentWeather + ", time=" + timeOfDay, testStone.listenForReply());
}
}
}
}
Im am currently collecting ideas on how to simplify that, maybe with a functional interface that creates a Runnable? I am fairly new to the lambda functionalities of newer Java, so maybe you could give me a hint into a promising direction. Of course, in reality it wouldn't all be Strings as arguments.
I already know the #Parameterized functionalities of JUnit, but that's very unhandy if I only have one test that needs all possibilities or if I want to reuse the same Stone object over and over.
If I understand you correctly, you basically want to have a generic method that builds the permutations based on a list of parameters which includes types and values and a class that represents a permutation. In that case you could do something like this:
//P is the type of the Permutation object, T is the type of the parameter
class Parameter<P, T> {
private final List<T> values;
private final BiConsumer<P, T> setter;
private Parameter<P, ?> next;
private int valueIndex = 0;
private boolean indexReset = false;
//first in line
//pass the setter first and the values as varargs for convenience
public Parameter(BiConsumer<P, T> setter, T... vals) {
this(null, setter, vals);
}
//next chain elements
//pass the previous element and the setter first and the values as varargs for convenience
public Parameter(Parameter<P, ?> prev, BiConsumer<P, T> setter, T... vals) {
this.values = Arrays.stream(vals).collect(Collectors.toList());
this.setter = setter;
if( prev != null ) {
prev.next = this;
}
}
//add the next parameter in the chain
public void setNext(Parameter<P, ?> next) {
this.next = next;
}
//creates the permutations and uses the supplier to create a new instance at the end of the chain
public List<P> createPermutations(Supplier<P> instanceSupplier) {
//we don't know the number of permutations so use a linked list here
List<P> permutations = new LinkedList<>();
//run as long as the index didn't get reset to 0 (reached the end of 1 complete iteration)
while( !indexReset ) {
permutations.add(build(instanceSupplier));
}
return permutations;
}
//builds a single permutation by passing the command along the chain
private P build(Supplier<P> instanceSupplier) {
P permutation;
//at the end of the chain build the permutation first, otherwise follow the chain
if( next == null ) {
permutation = instanceSupplier.get();
} else {
permutation = next.build(instanceSupplier);
}
//at this point we're unwinding, i.e. follow the chain tail to head
//set the value, the index is basically the permutation index so we need to get it into range
setter.accept(permutation, values.get(valueIndex));
//assume the index has not reset, we'll check this next
indexReset = false;
//increment the index if there is no next parameter or it has reset in this chain of calls
if( next == null || next.indexReset ) {
//increment
valueIndex++;
//reset if needed
if( valueIndex >= values.size() ) {
valueIndex = 0;
indexReset = true;
}
}
return permutation;
}
}
So what does this class do? It basically builds a linked list of parameters used to build the permutation. The order normally shouldn't matter, just pick what makes sense.
When you build the permutations the class will create call chains for each permutation, creates the instance at the end of the chain and then unwinds, sets the values to the new instance and increments indices as needed. So this is a depth-first approach.
Now let's build the permutations using your Stone class:
//time of day
Parameter<Stone, String> todParam = new Parameter<>(Stone::setTimeOfDay, "NIGHT", "NOON", "TEATIME", "DINNERTIME");
//current weather
Parameter<Stone, String> wheaterParam = new Parameter<>(todParam, Stone::setCurrentWeather, "RAINY", "FOGGY", "SUNNY", "SNOWY");
//current environment
Parameter<Stone, String> envParam = new Parameter<>(wheaterParam, Stone::setCurrentEnvironment, "FLOOR", "MEADOW", "STREET");
List<Stone> permutations = todParam.createPermutations(Stone::new);
This would create permutions like
NIGHT, RAINY, FLOOR
NIGHT, RAINY, MEADOW
...
NIGHT, FOGGY, FLOOR
...
DINNERTIME, SNOWY, STREET
Note that there's still a lot room for improvement, especially on the API, but it should get you started.
You could as well build a kinf of iterator that returns just one Stone permutation on each call and even reuse the same Stone in that case. As mentioned, this is meant to get you started. Now it's time to play around with the code and fit it to your needs :)
JUnit 5 has #ParameterizedTest. This parameterizes the test case. Each test case is created by a static method that returns Stream<Arguments>. You can test efficiently if you create a combinations method that returns all the combinations of the arrays specified by the arguments as Stream<Arguments>.
(I'm adding the number parameter to indicate that there may be parameters other than strings.)
static Stream<Arguments> combinations(Object[]... values) {
Stream.Builder<Arguments> builder = Stream.builder();
int length = values.length;
Object[] arguments = new Object[length];
new Object() {
void perm(int index) {
if (index >= length)
builder.add(Arguments.of(arguments.clone()));
else
for (int i = 0, max = values[index].length; i < max; ++i) {
arguments[index] = values[index][i];
perm(index + 1);
}
}
}.perm(0);
return builder.build();
}
static Stream<Arguments> stonesCombinations() {
return combinations(
new Integer[] {1, 2},
new String[] {"NIGHT", "NOON", "TEATIME", "DINNERTIME"},
new String[] {"RAINY", "FOGGY", "SUNNY", "SNOWY"},
new String[] {"FLOOR", "MEADOW", "STREET"});
}
#ParameterizedTest
#MethodSource("stonesCombinations") // Specify name of the static method that generates test cases
void testVoiceOutput(int number, String timeOfDay, String currentWeather, String currentEnvironment) {
System.out.println(
"number=" + number
+ ", timeOfDay=" + timeOfDay
+ ", currentWeather=" + currentWeather
+ ", currentEnvironment=" + currentEnvironment);
}
output:
number=1, timeOfDay=NIGHT, currentWeather=RAINY, currentEnvironment=FLOOR
number=1, timeOfDay=NIGHT, currentWeather=RAINY, currentEnvironment=MEADOW
number=1, timeOfDay=NIGHT, currentWeather=RAINY, currentEnvironment=STREET
number=1, timeOfDay=NIGHT, currentWeather=FOGGY, currentEnvironment=FLOOR
number=1, timeOfDay=NIGHT, currentWeather=FOGGY, currentEnvironment=MEADOW
number=1, timeOfDay=NIGHT, currentWeather=FOGGY, currentEnvironment=STREET
number=1, timeOfDay=NIGHT, currentWeather=SUNNY, currentEnvironment=FLOOR
.....
number=2, timeOfDay=DINNERTIME, currentWeather=SNOWY, currentEnvironment=FLOOR
number=2, timeOfDay=DINNERTIME, currentWeather=SNOWY, currentEnvironment=MEADOW
number=2, timeOfDay=DINNERTIME, currentWeather=SNOWY, currentEnvironment=STREET
First keep in mind that tests need to justify their costs in terms of added running time, complexity and maintenance. You must ask yourself what is the extra benefit of an exhaustive test like the one you are describing over a sampling one that checks 2 or three different permutations? Is there anything in the code that would make it fail only for a particular permutation of time/whether/environment out of 1000?
Now let us suppose that for some reason the exhaustive test is a must.
Sometimes code with lambdas is harder to read than traditional code.
( at least for me )
I think you answered your own question. A small and simple step for reducing and simplifying the test code, would be to create a simple test helper, as helper functions, or preferably as a helper class.
#Test
public void testVoiceOutput() {
Stone testStone = new Stone();
for (String timeOfDay : new String[]{"NIGHT", "NOON", "TEATIME", "DINNERTIME"}) {
for (String currentWeather : new String[]{"RAINY", "FOGGY", "SUNNY", "SNOWY"}) {
for (String currentEnvironment : new String[]{"FLOOR", "MEADOW", "STREET"}) {
prepareTestStone(testStone,timeOfDay,currentWeather,currentEnvironment);
testStone.talkTo("Hey, how are you doing?");
String expected = getStoneResponse(testStone,timeOfDay,currentWeather,currentEnvironment);
assertEquals(expected, testStone.listenForReply());
}
}
}
}
There's a lot of room to play with here. If you really want to abstract the test further ( now this may be hiding too much of the test for my taste, you can move both the loading of the inputs and the testing of the results into the helper )
#Test
public void testVoiceOutput() {
Stone testStone = new Stone();
StoneTestHelper stoneTestHelper = new StoneTestHelper(testStone);
for (String timeOfDay : new String[]{"NIGHT", "NOON", "TEATIME", "DINNERTIME"}) {
for (String currentWeather : new String[]{"RAINY", "FOGGY", "SUNNY", "SNOWY"}) {
for (String currentEnvironment : new String[]{"FLOOR", "MEADOW", "STREET"}) {
stoneTestHelper.checkStone(testStone,timeOfDay,currentWeather,currentEnvironment);
}
}
}
}
class StoneTestHelper {
private Stone testStone;
public void StoneTestHelper(Stone stone) { this.testStone=stone; }
public void CheckStone(String timeOfDay,String currentWeather,String currentEnvironment) {
{
testStone.setTimeOfDay(timeOfDay);
testStone.setWeather(currentWeather);
testStone.setEnvironment(currentEnvironment);
testStone.talkTo("Hey, how are you doing?");
assertEquals("Stone's answer, environment=" + currentEnvironment + ", weather=" + currentWeather + ", time=" + timeOfDay, testStone.listenForReply());
}
}
}
Removing brackets from list of anagrams
Hello, i have been asked to create new question insted of asking again in the same thread.
public static void main(String[] args) throws IOException {
getAnagrams(new InputStreamReader(new URL("http://www.puzzlers.org/pub/wordlists/unixdict.txt").openStream(),
StandardCharsets.UTF_8)).forEach(items -> {
for (String item : items) {
System.out.print(item + " ");
}
System.out.println();
});
}
private static String canonicalize(String string) {
return Stream.of(string.split("")).sorted().collect(Collectors.joining());
}
public static List<Set<String>> getAnagrams(Reader rr) {
Map<String, Set<String>> mapa = (Map<String, Set<String>>) new BufferedReader(rr).lines()
.flatMap(Pattern.compile("\\W+")::splitAsStream)
.collect(Collectors.groupingBy(Main::canonicalize, Collectors.toSet()));
return mapa.values().stream().filter(lista -> lista.size() > 4).collect(Collectors.toList());
}
}
How do i sort my output like "evil levi live veil vile"? Cuz for now i have "veil evil vile levi live"
UPDATE
Last thing is to sort output alphabeticaly
For now = "evil levi live veil vile, abel able bale bela elba"
Needed = "abel able bale bela elba, evil levi live veil vile"
Here's the easiest way I can think of
return mapa.values().stream()
.filter(lista -> lista.size() > 4)
.map(set -> new TreeSet<>(set))
.collect(Collectors.toList());
TreeSet keeps its elements sorted.
I have a string arraylist under that i need to pass 22184 elements from ["AA00001", "AA00005" ,"AA00003" ----- "ZZ00678"] and i need to generate the sequence elements which are not present in the list. I have written code for that and for less inputs it is generating the required output. But when i am adding 22184 elements and want to generate 200 unique ids which are not present in the arraylist i am getting error as
The code of method main(String[]) is exceeding the 65535 bytes limit
Can someone please help ?
import java.util.ArrayList;
public class GenerateIds
{
private static ArrayList<String> ids = new ArrayList<>();
static int n=50; //no of Ids u want to generate
static int completed =0;
static char ID[] = new char[7];
public static void main(String[] args)
{
ids.add("AA00001");
ids.add("AA00004");
ids.add("AA00007");
generateIds(0);
for(String id : ids)
{
System.out.println(id);
}
}
private static void generateIds(int i)
{
if(n!=completed)
{
if(i<2)
{
for(char c ='A';c<'Z';c++)
{
ID[i]=c;
generateIds(i+1);
}
}
else if(i>=2 && i<7)
{
for(char c ='0';c<='9';c++)
{
ID[i]=c;
generateIds(i+1);
}
}else if(i==7)
{
String id = String.valueOf(ID);
if(!ids.contains(id))
{
ids.add(id);
completed++;
}
}
}
}
}
You can put your id's in a text file. Then use something like.
List<String> ids = Files.readAllLines(Paths.get("ids.txt"));
In java a methods can't have more than 65535 bytes.
The main method is becoming too large since you are doing all the adds inline:
ids.add("AA00001");
ids.add("AA00004");
ids.add("AA00007");
...
This will make the main method too long. What you can do to solve this (and to find the missing elements) is putting all the String values in a List and loop over it to find the missing elements:
public void findMissingElements() {
List<String> missingIds = allPossibleIds.stream()
.filter(isMissingIn(existingIds))
.collect(toList());
//do something with the missingIds...
}
As other readers such as matt suggested, you can e.g. put all the Strings in a file and read the file.
I wrote a small example to show how it would all work together. I rewrote your generateIds method with jOOλ to generate all the possible ids and renamed it to allPossibleIds (however your recursive method would work too). I limited the ids to a 3 size digit number to limit the search time as an example.
public class FindMissingIdsTest {
private List<String> allPossibleIds;
private List<String> existingIds;
#Before
public void setup() throws IOException {
allPossibleIds = allPossibleIds();
existingIds = retrieveIdsFromSubSystem();
}
#Test
public void findMissingElements() {
List<String> missingIds = allPossibleIds.stream()
.filter(isMissingIn(existingIds))
.collect(toList());
}
private Predicate<String> isMissingIn(List<String> existingIds) {
return possibleId -> !existingIds.contains(possibleId);
}
public List<String> allPossibleIds(){
List<String> alphabet = Seq.rangeClosed('A', 'Z').map(Object::toString).toList();
List<String> letterCombinations = Seq.seq(alphabet).crossJoin(Seq.seq(alphabet)).map(t -> t.v1 + t.v2).toList();
List<String> numbericParts = IntStream.range(0, 1000)
.mapToObj(i -> String.format("%03d", i))
.collect(toList());
return Seq.seq(letterCombinations).crossJoin(Seq.seq(numbericParts)).map(t -> t.v1 + t.v2).toList();
}
public List<String> retrieveIdsFromSubSystem() throws IOException {
return Files.readAllLines(Paths.get("ids.txt"));
}
}
To change to 5 digits again you can just change the 1000 to 100000 and the %03d to %05d.
If you can order the list, you could probably find a faster and better algorithm. It all depends on the situation. e.g. if you have an ordered list, you could build up the stream of all the ids, iterate over it and follow in the existing list with a pointer instead of always doing a resource consuming contains().
I am having one problem in java arraylist. I am good in databases :) We normally use
"group by" to group rows. I want the same thing but in java for one of my project
I have following format in arraylist
name1:val1
name1:val2
name1:val3
name2:val8
name2:val7
name7:val54
name7:val76
name7:val34
I want to convert this arraylist to give me following output:
-name1
val1
val2
val3
-name2
val8
val7
-name7
.
.
.
val34
this is not a school assignment :). may be for some of Java Guru it looks like a small
thing to do.
I like to do that kind of thing with a map.
import java.util.*;
public class DoIt {
public static void main(String[] args) {
List l = new ArrayList<String>();
l.add("name1:val1");
l.add("name1:val2");
l.add("name1:val3");
l.add("name1:val4");
l.add("name2:val1");
Map results = new HashMap<String,String>();
for (Iterator i = l.iterator(); i.hasNext();) {
String s = (String)i.next();
String[] tmp = s.split(":");
if (!results.containsKey(tmp[0])) {
System.out.println("-"+tmp[0]+"\n"+tmp[1]);
results.put(tmp[0], tmp[1]);
} else {
System.out.println(tmp[1]);
}
}
}
}
Use a Map<String, List<Integer>>. You could use the following snippets:
// declare
Map<String, List<Integer>> m = new HashMap<String, List<Integer>>();
// insert into the structure the pair 'a':2
List<Integer> l = m.get("a");
if ( l == null ) {
l = new ArrayList<Integer>();
m.put("a", l);
}
l.add(2);
// iterate over the values
for (Map<String, List<Integer>>.Entry e : m) {
System.out.println("-" + e.getKey());
for (Integer i : m.getValue()) {
System.out.println(" " + i);
}
}
What you are looking for is called multi-map.
There is no standard multi-map in java.util, but Google collections has implemented it -> here. The project home page is here
Use Comparator
List<Samp> bla = new ArrayList<Samp>();
Collections.sort(bla, new Comparator<Samp>() {
#Override
public int compare(Samp s1, Samp s2) {
return s1.getCategory().compareTo(s2.getCategory());
}
});
then Create 1 List .,. compare if that list already contains the category, if not,
add Category and Name, else just add Name. see code below :)
List<String> catList = new ArrayList<String>();
for(Samp s : bla){
if (!catList.contains(s.getCategory())){
catList.add(s.getCategory());
System.out.println(s.getCategory() + " - ");
System.out.println(s.getName());
} else {
System.out.println(s.getName());
}
}