Is there a way to merge this 2? - java

I'm running out of idea of how will I merge this 2 conditions, it has the same return so I need to merge
if ((StringUtils.isBlank(ext))) {
return true;
}
for (String str : INVALID_EXTENSION_ARR) {
if (ext.matches(str)) {
return true;
} else if (ext.matches(str.toLowerCase())) {
return true;
}
}

You don't need a loop.
Populate INVALID_EXTENSION_ARR with values in lowercase:
private static final List<String> INVALID_EXTENSION_ARR = Arrays.asList("foo", "bar", "whatever"); // Note: All in lowercase!
Then it's just one line:
return StringUtils.isBlank(ext) || INVALID_EXTENSION_ARR.contains(ext.toLowerCase());
Note: I have assumed when you used matches() you meant to use equals().
——-
If the list of acceptable extensions is “large” (say, more than 10), you’ll get better performance if you use a Set instead of a List:
private static final Set<String> INVALID_EXTENSION_ARR = new HashSet<>(Arrays.asList("foo", "bar", "whatever"));
Or for recent java versions:
private static final Set<String> INVALID_EXTENSION_ARR = Set.of("foo", "bar", "whatever");
But you would be unlikely to notice much difference unless the size was more than say 100.

Assuming that the loop will always be entered into,
for (String str : INVALID_EXTENSION_ARR) {
if (StringUtils.isBlank(ext) || ext.matches(str)) {
return true;
} else if (ext.matches(str.toLowerCase())) {
return true;
}
}
but I think that way that had it was easier to read and does not need to re-evaluate StringUtils.isBlank(ext) every time.

It is helpful if you provide more context, but this is one of the ways you can compact it.
for (String str : INVALID_EXTENSION_ARR) {
if (StringUtils.isBlank(ext) || ext.toLowerCase().matches(str.toLowerCase())) {
return true;
}
}

Related

Using regex with sets in Java

I have a set in Java with different strings, such as '+Cat', '+Dog', etc. I also have a string as shown below.
animals = '+Cat|+Dog|+Goat'
Basically, if the set contains any of the animals listed in animals, I want to return false. How would I go about doing this? I am not sure if you can use regex with contains. Furthermore the fact that I have the + in my string complicates regex.
public boolean containCheck(Set<String> newset) {
String animals = "+Cat|+Dog|+Goat";
for (String op : animals.split("\\|")) {
if (newset.contains(op)) {
return false;
}
}
return true;
}
You can split the string in array and use a for loop to check it the set contains any element presents in the array.
public Boolean isContaining(String animals, Set<String> sets ) {
for (String s : animals.split("\\|")) {
for (String set : sets) {
if (s.equals(set)) {
return false;
}
}
}
return true;
}

Optimizing the ArrayList Condition

I have some values in my Array list and I have to remove some particular values based on conditions.I am using IF condition but there are many conditions to compare so I need to optimize the comparison time.for e.g. my list is
Msisdn_array={45,85,79,60,502}
and if condition match then remove the entry.
Below are the Conditions, Is there any possible way to optimize this conditions.
if(Msisdn_array.contains("60") && Msisdn_array.contains("910"))
{
Msisdn_array.remove("60");
Msisdn_array.remove("910");
}
if(Msisdn_array.contains("75") && Msisdn_array.contains("500"))
{
Msisdn_array.remove("75");
Msisdn_array.remove("500");
}
if(Msisdn_array.contains("76") && Msisdn_array.contains("502"))
{
Msisdn_array.remove("76");
Msisdn_array.remove("502");
}
if(Msisdn_array.contains("61") && Msisdn_array.contains("911"))
{
Msisdn_array.remove("61");
Msisdn_array.remove("911");
}
if(Msisdn_array.contains("77") && Msisdn_array.contains("503"))
{
Msisdn_array.remove("77");
Msisdn_array.remove("503");
}
if(Msisdn_array.contains("78") && Msisdn_array.contains("505"))
{
Msisdn_array.remove("78");
Msisdn_array.remove("505");
}
if(Msisdn_array.contains("79") && Msisdn_array.contains("507"))
{
Msisdn_array.remove("79");
Msisdn_array.remove("507");
}
if(Msisdn_array.contains("62") && Msisdn_array.contains("912"))
{
Msisdn_array.remove("62");
Msisdn_array.remove("912");
}
if(Msisdn_array.contains("63") && Msisdn_array.contains("913"))
{
Msisdn_array.remove("63");
Msisdn_array.remove("913");
}
if(Msisdn_array.contains("64") && Msisdn_array.contains("914"))
{
Msisdn_array.remove("64");
Msisdn_array.remove("914");
}
if(Msisdn_array.contains("65") && Msisdn_array.contains("915"))
{
Msisdn_array.remove("65");
Msisdn_array.remove("915");
}
if(Msisdn_array.contains("66") && Msisdn_array.contains("916"))
{
Msisdn_array.remove("66");
Msisdn_array.remove("916");
}
if(Msisdn_array.contains("67") && Msisdn_array.contains("917"))
{
Msisdn_array.remove("67");
Msisdn_array.remove("917");
}
if(Msisdn_array.contains("68") && Msisdn_array.contains("918"))
{
Msisdn_array.remove("68");
Msisdn_array.remove("918");
}
if(Msisdn_array.contains("69") && Msisdn_array.contains("919"))
{
Msisdn_array.remove("69");
Msisdn_array.remove("919");
}
if(Msisdn_array.contains("70") && Msisdn_array.contains("920"))
{
Msisdn_array.remove("70");
Msisdn_array.remove("920");
}
if(Msisdn_array.contains("71") && Msisdn_array.contains("921"))
{
Msisdn_array.remove("71");
Msisdn_array.remove("921");
}
if(Msisdn_array.contains("72") && Msisdn_array.contains("922"))
{
Msisdn_array.remove("72");
Msisdn_array.remove("922");
}
if(Msisdn_array.contains("73") && Msisdn_array.contains("923"))
{
Msisdn_array.remove("73");
Msisdn_array.remove("923");
}
if(Msisdn_array.contains("74") && Msisdn_array.contains("924"))
{
Msisdn_array.remove("74");
Msisdn_array.remove("924");
}
if(Msisdn_array.contains("80") && Msisdn_array.contains("926"))
{
Msisdn_array.remove("80");
Msisdn_array.remove("926");
}
if(Msisdn_array.contains("81") && Msisdn_array.contains("927"))
{
Msisdn_array.remove("81");
Msisdn_array.remove("927");
}
if(Msisdn_array.contains("82") && Msisdn_array.contains("928"))
{
Msisdn_array.remove("82");
Msisdn_array.remove("928");
}
One potential optimization is that you could don't need to check if the second item is in the list. Instead just attempt to remove it. If it was removed, the remove method will return true and you can also remove the first item.
if(Msisdn_array.contains("60") && Msisdn_array.remove("910")){
Msisdn_array.remove("60");
}
If you don't want to write out each if statement, you could store the groups in a Map, with the first item as the key and the second item as the value.
Map<String, String> rules = new HashMap<>();
rules.put("60", "910");
rules.put("75", "500");
rules.put("76", "502");
...
...
for(Entry entry : rules.values()) {
if(Msisdn_array.contains(entry.getKey()) && Msisdn_array.remove(entry.getValue())){
Msisdn_array.remove(entry.getKey());
}
}
I think you only need to extract a method to check if all of a group values are existed in List and then remove all. For example:
private void removeIfAllExist(List<String> list, String[] values) {
for (String v : values) {
if (!list.contains(v)) {
return;
}
}
list.removeAll(Arrays.asList(values));
}
public void yourLogic() {
List<String> list = new ArrayList<>(Arrays.asList("45", "85", "79", "60", "502"));
String[][] conditions = new String[][]{
new String[]{"60", "910"},
new String[]{"75", "500"},
new String[]{"76", "502"},
new String[]{"61", "911"},
new String[]{"77", "503"},
// more conditions
};
for (String[] values : conditions) {
removeIfAllExist(list, values);
}
}
If you do not iterate through the list often, I suggest you use a Set.
Search in this collection is fast.
contains is an O(n) operation. The list is traversed until the element is found. So every time you call contains you are potentially traversing the entire list.
An optimization might be to traverse the list once, manually, and check if the elements exist, and then do your removes afterwards, at the cost of some extra memory to store the boolean variables:
boolean exists_72 = false;
boolean exists_922 = false;
for(String element : Msisdn_array) {
if(element.equals("72")) {
exists_72 = true;
} else if(element.equals("922")) {
exists_922 = true;
}
}
if(exists_72 && exists_922) }
Msisdn_array.remove("72");
Msisdn_array.remove("922");
}
as remove returns a boolean is sucessfully removed you could do
if (list.remove ("60") && list.remove ("90"))
{
// everything ok
}
else {
// put back "60"
list.add ("60");
}
but personally I would go for readability a just create a method
removeBoth (String one, String two) {
if(list.contains(one) && list.contains(two))
{
list.remove(one);
list.remove(two);
}
}
It's slow because of List.contains being slow and also because List.remove being even slower (as it must move all elements with bigger indexes in order to fill the gap). It's ugly because of code repetition.
Msisdn_array is against Java naming convention and it's no array, so lets call it inputList. Let's use a HashSet for the lookups and another one to track what should be removed.
class Worker {
private final Set<String> inputListAsSet = new HashSet<>();
private final Set<String> removals = new HashSet<>();
public static void process(List<String> inputList) {
final Worker worker = new Worker(inputList);
worker.removeIfBothPresent("60", "910");
worker.removeIfBothPresent("75", "500");
worker.removeIfBothPresent("76", "502");
worker.removeIfBothPresent("61", "911");
worker.removeIfBothPresent("72", "922");
worker.removeIfBothPresent("73", "923");
worker.removeIfBothPresent("74", "924");
worker.removeIfBothPresent("80", "926");
worker.removeIfBothPresent("81", "927");
worker.removeIfBothPresent("82", "928");
inputList.removeAll(worker.removals);
}
private Worker(List<String> inputList) {
inputListAsSet.addAll(inputList);
}
private void removeIfBothPresent(String first, String second) {
if (inputListAsSet.contains(first) && inputListAsSet.contains(second)) {
removals.add(first);
removals.add(second);
}
}
}
Instead of using a class instance, the sets could be passed as arguments, but creating a worker seems cleaner. Note that despite the optimizations, my code is shorter than the original. In case of duplicates, it's not exactly equivalent.

All combinations of alphanumeric string, better way?

The input to the "alphaNumeric" function is a String which consists of alphanumeric characters that are all lower case, for example "hello123hello". I want to be able to check all upper/lower case letter combinations for this string through a check( ) function. (Eg. HeLlO123hELlo is one of the combinations to be checked). I have written code in Java to do this where I store the matching String into an ArrayList, but would like to know if there a better way to do this without the ArrayList. Also, am I correct in saying the worst case runtime of this is O(2^n)? Note: Check is a function that returns either true or false, depending on whether the correct String is passed to the function.
public static String alphaNumeric(String input) {
ArrayList<String> list = new ArrayList<String>();
alphaHelper(input, "", list);
return list.get(0);
}
private static void alphaHelper(String in, String current, ArrayList<String> list) {
if (in.length() == 0) {
if (check(current)) {
list.add(current);
}
} else if (Character.isLetter(in.charAt(0))) {
alphaHelper(in.substring(1),current+in.substring(0,1).toLowerCase(),list);
alphaHelper(in.substring(1),current+in.substring(0,1).toUpperCase(),list);
} else if (Character.isDigit(in.charAt(0))) {
alphaHelper(in.substring(1),current+in.substring(0,1),list);
} else {
return;
}
}
If you just want to remove the ArrayList without changing your basic algorithm, you can do this:
public static String alphaNumeric(String input) {
return alphaHelper(input, "");
}
private static String alphaHelper(String in, String current) {
String result = null;
if (check(current)) {
result = current;
} else if (Character.isLetter(in.charAt(0))) {
result = alphaHelper(in.substring(1),current+in.substring(0,1).toLowerCase());
if (result == null) result = alphaHelper(in.substring(1),current+in.substring(0,1).toUpperCase());
} else if (Character.isDigit(in.charAt(0))) {
result = alphaHelper(in.substring(1),current+in.substring(0,1));
}
return result;
}
Yes it is O(2^n), and I can't see offhand how you would improve on that if you can't get the original string directly.
If you don't need to check substrings (i.e. you only care about case variations of the entire string) you could improve the algorithm by not testing the substrings, but it would still be O(2^n).
You could temporarily set both the check and input to lowercase and compare them then.
public static boolean alphaNumeric(String input, String check) {
return input.toLowerCase().equals(check.toLowerCase());
}
-Sean

Find unique superdirectories in a set of Java File objects

Given an unordered set of Java File objects, I want to eliminate all those that are subdirectories (or files in subdirectories) of other Files in the set. For instance, given:
File("/john/paul/george/ringo")
File("/foo/bar/baz")
File("/foo/bar/elvis")
File("/john/paul")
File("/john/jacob")
File("/foo/bar/baz/qux/quux")
File("/john/")
File("/foo/bar/")
the set should reduce to:
File("/foo/bar/")
File("/john/")
I can imagine cobbling something together based on converting to Strings with File.getAbsolutePath() and sorting (possibly by length first, then lexicographically), but surely this is a solved problem, or at least somebody must already have written utility methods for things like determining whether one File is an ancestor of another.
I'm not the best of programmers, but here's my shot at it. There's quite a bit of recursion involved.
class UniqueDirectories {
private final Map<String, UniqueDirectories> directories = new HashMap<String, UniqueDirectories>();
private boolean locked = false;
public UniqueDirectories() {}
private UniqueDirectories(boolean locked) {
this.locked = locked;
}
public boolean add(String path) {
if (path == null)
return false;
if (path.isEmpty())
return false;
if (locked)
return false;
String[] tokens = path.split("(?<!^)[/\\\\]", 2);
UniqueDirectories existingDirectory = directories.get(tokens[0]);
if (existingDirectory == null) {
if (tokens.length < 2 || tokens[1].isEmpty()) {
directories.put(tokens[0], new UniqueDirectories(true));
return true;
} else {
UniqueDirectories newDirectory = new UniqueDirectories(false);
directories.put(tokens[0], newDirectory);
return newDirectory.add(tokens[1]);
}
} else if (tokens.length >= 2 && !tokens[1].isEmpty()) {
return existingDirectory.add(tokens[1]);
} else {
directories.put(tokens[0], new UniqueDirectories(true));
return true;
}
}
public List<String> toList(char delimiter) {
List<String> list = new ArrayList<String>();
for (Map.Entry<String, UniqueDirectories> entry : directories.entrySet()) {
if (entry.getValue().directories.size() == 0) {
list.add(entry.getKey());
} else {
for (String subdirectory : entry.getValue().toList(delimiter)) {
list.add(entry.getKey() + delimiter + subdirectory);
}
}
}
return list;
}
public static void main(String[] args) {
String[] testPaths = {
"/john/paul/george/ringo",
"/foo/bar/baz",
"/foo/bar/elvis",
"/john/paul",
"/john/jacob",
"/foo/bar/baz/qux/quux",
"/john/",
"/foo/bar/"
};
UniqueDirectories directories = new UniqueDirectories();
for (String path : testPaths) {
directories.add(path);
}
System.out.println(directories.toList('/'));
}
}
Sorry about the lack of comments. Too lazy to go back and do it, but at least the code's relatively short. I haven't put it through rigorous testing either (just used the ones you provided), so you may want to use some more test cases.
The biggest thing to be careful with is consistency. If you start a path with a delimiter, all paths should start with one. Conversely, if one path does not start with a delimiter, the rest shouldn't either. Otherwise, they will be treated as different paths. You could easily fix this by removing the lookbehind in the regex though; I just wasn't sure how you wanted it to behave.

Use method to compare lists

I'm trying to use a method to compare t2o different lists. Basically I want to pass two different lists to a method which will return true or false if the elements of one array list are contained in the other using .contains. Right now it only returns true - and I'm not sure why. I'd like it to return false. If someone could help me figure this out, that would be great.
public class ArrayListTest {
public static void main(String[] args) {
List<String> list1 = new ArrayList<String>();
List<String> list2 = new ArrayList<String>();
list1.add("cat");
list1.add("dog");
list1.add("zebra");
list1.add("lion");
list1.add("mouse");
//Test Values
//list2.add("cat");
list2.add("lizard");
boolean doesitcontain = contains(list1, list2);
System.out.println(doesitcontain);
}
public static boolean contains (List<String>list1, List<String>list2){
boolean yesitcontains;
for(int i = 0; i < list1.size(); i++){
if(list2.contains(list1.get(i))){
System.out.println("Duplicate: "+list1.get(i));
yesitcontains = true;
System.out.println(yesitcontains);
}else{
yesitcontains = false;
System.out.println(yesitcontains);
}
}
if (yesitcontains = true){
return true;
}else
return false;
}
}
You have inadvertently used the assignment operator where you intended the equality operator. In your specific case you should rewrite all this:
if (yesitcontains = true){
return true;
}else
return false;
}
to just
return yesitcontains;
and avoid any chance of confusion.
Furthermore, your algorithm will not work because you should return true immediately when you see a duplicate. Instead you go on with the loop and "forget" your finding. You can expect this to always return false except if the very last elements coincide.
In a wider context, I should also give you the following general advice:
Avoid indexed iteration over lists. Not all lists are ArrayLists and may show O(n) complexity for get(i). Instead use the enhanced for loop, which is safer, more concise, and more obvious;
Know the library: if you're just after confirming there are no duplicates, just Collections.disjoint(list1, list2) would give you what you need;
Be aware of algorithmic complexity: checking for duplicates in two lists is O(n2), but if you turn one of them into a HashSet, you'll get O(n).
Taking everything said above into account, the following would be an appropriate implementation:
static boolean disjoint(Collection<?> c1, Collection<?> c2) {
for(Object o : c1)
if (c2.contains(o))
return true;
return false;
}
If you look at Collections.disjoint, you'll find this exact same loop, preceded by a piece of code which optimizes the usage of sets for reasons described above.
Seems to me your method should be rewritten to:
public static boolean contains(List<String>list1, List<String>list2) {
return list2.containsAll(list1);
}
The code you currently have actually only checks if the last element of list1 is also in list2.
If you're actually looking for a contains any, this simple solution will do:
public static boolean contains(List<String>list1, List<String>list2) {
for (String str : list1) {
if (list2.contains(str)) {
return true;
}
}
return false;
}
if (yesitcontains = true){
should be
if (yesitcontains == true){
== is for comparison and = is for assignment.
if (yesitcontains = true){
will always evaluate to if(true) which causing return true;
EDIT:
(OR)
simply return yesitcontains; as commented.
if (yesitcontains == true) { } // use `==` here
or just
if (yesitcontains) { }
The below code assigns true to yesitcontains , and the expression will always be true.
if (yesitcontains = true) { }
There is no point of if() in your code , you can simple return yesitcontains;

Categories