Is it possible to add a wildcard to a string in a list in Java?
I have a string, which has different endings (for example: Key(1), Key(2), Key(3), Key(4), Key(5) etc etc.)
Is it possible to somehow add a string to a list, with a wildcard, so I could just do something like:
list.add("Key*wildcard*");
And it would have all the different endings of the string?
Many thanks :-)
EDIT:
This is how I'll use it.
I'm coding something for a game, which has to check for a specific item. This item has charges, which defines the ending of the name of the item. The item is called "Games Necklace".
Now, I want to check, if the user has the item, and if not, it will buy one. I have a list of items required to do a certain task, but the Games Necklace changes name (from 8-1, so it can be either "Games Necklace(8), Games Necklace(7), Games Necklace(6) and down to 1). I want to check if the users has all the items in the list of items needed, but it should return true, if the user has any version of the necklace
You can add a wildcard string to the list, but then you have to implement the search in the list yourself, and you can't rely on contains to work for you, as it simply calls the equals method, and equals would not do what you need.
Here is a possible solution:
Instead of using a List, use a List, and define the item as a regular expression.
List<Pattern> requiredList = new List<>();
requiredList.add(Pattern.compile("Sword");
requiredList.add(Pattern.compile("Games Necklace\\(\\d+\\)");
Write a method that matches all the patterns in the list against another collection. e.g.
public static hasAll( List<Pattern> required, Collection<String> available) {
for ( Pattern pat : available ) {
boolean matched = false;
for ( String item : needed ) {
if ( pat.matcher(item).matches() ) {
matched = true;
break;
}
}
if ( ! matched ) {
return false;
}
}
return true;
}
Another option is to define your game items in their own class, not as strings. That class will have a "type" and a "display name". And you'll check it in the list using the type, which will be "Games Necklace" (for all of them), and display using the display name.
No, as it doesn't make sense for a String to have multiple values (in Java at least). You could do it by making a class that represents your "multi-string" (of course change the mechanics oif it to fit your exact needs.
class MultiString {
List<String> strings = new ArrayList<String>();
public MultiString(String, prefix, int numValues) {
for(int i = 0; i < numValues; i++) strings.add(prefix + String.valueOf(i));
}
public String get(int i) {
return strings.get(i);
}
}
Or you could just add several values to the list.
public void addStrings(List<String> list, String prefix, int numValues) {
for(int i = 0; i < numValues; i++) list.add(prefix + String.valueOf(i));
}
More specific solutions can't be given until we know your exact needs.
List<String> list = new ArrayList<>();
for (int i= 8; i>0;i--){
list.add("Games Necklace("+i+")");
}
Related
I have two Strings
String first = "value=[ABC,PQR,XYZ]"
String second="value=[ABC]"
I am trying to check the contains of string second into a string first.
I am using the below code
List<String> list = new Arraylist<>();
list.add(first);
if(list.contains(second)){
// do something
}
How to check contains in the list which has string with multiple ","?
Which data structure should I use for above problem?
Probably, you don't know how to work with lists in java...
In your case, you are adding a string "value=[ABC,PQR,XYZ]" to the list. Hence, you have a list with only one item.
If you want to create such a list ["ABC","PQR","XYZ"], you have to add these three elements one by one.
P.S. If you studied java basic, you wouldn't have such problems...
String first = "value=[ABC,PQR,XYZ]";
String second ="value=[ABC]";
String secondVal = second.substring(second.indexOf("[") + 1, second.indexOf("]"));
String[] firstArry = first.substring(first.indexOf("[") + 1, first.indexOf("]")).split(",");
boolean secondInFirst = false;
for (int i = 0; i < firstArry.length; i++) {
if (firstArry[i].equals(secondVal)) {
secondInFirst = true;
break;
}
}
I'm not sure why the first and second are formatted in such a way, however, assuming they are always formatted the same way ("value=[X,Y,Z]"),
We must break first up into a fixed list ("value=[ABC,PQR,XYZ]" -> {"ABC","PQR","XYZ"})
Format second to be readable ("value=[ABC]" -> "ABC")
Loop through firstArry and find matches
Store the result in secondInFirst
I have a map:
Map<String, String> abc = new HashMap<>();
"key1" : "value1",
"key2" : "value2"
And an array:
String[] options= {"value1", "value2", "value3"}
I am creating this array as following (I am using following method to do something else which is not relevant to the question that I am asking here):
public String[] getOptions() {
List<String> optionsList = getOptionsFromAMethod(WebElementA);
String[] options = new String[optionsList.size()];
options = optionsList.toArray(options);
return options;
}
What is the best way to verify if String[] contains each value from Map?
I am thinking about doing this:
for (Object value : abc.values()) {
Arrays.asList(options).contains(value);
}
Explanation
Your current approach creates an ArrayList (from java.util.Arrays, not to confuse with the regular ArrayList from java.util) wrapping the given array.
You then call, for each value of the map, the ArrayList#contains method. However this method is very slow. It walks through the whole list in order to search for something.
Your current approach thus yields O(n^2) which doesn't scale very well.
Solution
We can do better by using a data-structure which is designed for a fast contains query, namely a HashSet.
So instead of putting all your values into an ArrayList we will put them into a HashSet whose contains method is fast:
boolean doesContainAll = true;
HashSet<String> valuesFromArray = new HashSet<>(Arrays.asList(options));
for (String value : abc.values()) {
if (!valuesFromArray.contains(value)) {
doesContainAll = false;
break;
}
}
// doesContainAll now is correctly set to 'true' or 'false'
The code now works in O(n) which is far better and also optimal in terms of complexity.
Of course you can optimize further to speedup by constant factors. For example you can first check the size, if options.length is greater than abc.values().size() then you can directly return with false.
JStream solution
You can also use Java 8 and Streams to simplify the above code, the result and also the procedure behind the scenes is the same:
HashSet<String> valuesFromArray = new HashSet<>(Arrays.asList(options));
boolean doesContainAll = abc.values().stream()
.allMatch(valuesFromArray::contains);
Insights of ArrayList#contains
Let's take a closer look into java.util.Arrays.ArrayList. You can find its code here.
Here is its code for the contains method:
public boolean contains(Object o) {
return indexOf(o) != -1;
}
Lets see how indexOf is implemented:
public int indexOf(Object o) {
E[] a = this.a;
if (o == null) {
for (int i = 0; i < a.length; i++)
if (a[i] == null)
return i;
} else {
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
}
So indeed, in all cases the method will traverse from left to right through the source array in order to find the object. There is no fancy method that is able to directly access the information whether the object is contained or not, it runs in O(n) and not in O(1).
Note on duplicates
If either of your data may contain duplicates and you plan to count them individually, then you will need a slightly different approach since contains will not bother for the amount of duplicates.
For this you may collect your abc.values() first into a List for example. Then, every time you checked an element, you will remove the matched element from the List.
Alternatively you can setup a HashMap<String, Integer> which counts for every element its occurrences. Then, every time you checked an element, decrease the counter by one.
You can use https://docs.oracle.com/javase/7/docs/api/java/util/List.html#containsAll(java.util.Collection)
Arrays.asList("value1", "value2", "value3").containsAll(abc.values())
I would recommend using a stream:
final List<String> optionsList = Arrays.asList(options);
abc.values().stream().allMatch(optionsList::contains);
I have an ArrayList with lots of objects. I want to be able to add any objects into a choice of 3 different LinkedLists. A user input will select which item to add to which LinkedList by typing the index they wish to be added. This is the kind of thing i'm after, but i just can't get it to work:
public void addToRepository(int indx, int r) {
if (r == 1){ //checks to see if chosen repository 1
for (int i=0; i < itemList.size(); i++) {
i = indx;
} // ignore this it was just me playing around to see if i'd be able to get the index this way..
repo1.add(itemList.get(indx)); //adds item from "itemList" with the index that
the user input has given
//other if statements
}
}
I'm not sure this is the correct idea but it gives error "Item cannot be converted to String". If not, how can i go about doing this?
So you have
ArrayList<Item> itemList = new ArrayList<Item>();
and you are trying to do -
repo1.add(itemList.get(indx));
As per the Exception you are getting it looks like repo1 has String data. You can do one of the following things -
Use repo1.add(itemList.get(indx).toString()); OR
Change repo1 generics to include Item data instead of String
I have an ArrayList of Items and I want to be able remove one Item from the list by entering only one Item attribute, for example its number (int ItemNumber). I also wanna do the same when I check Item quantities.
These are my equals() & contains() methods, do I need to make any changes here?
public boolean contains(T anEntry) {
boolean found = false;
for (int index = 0; !found && (index < numberOfEntries); index++) {
if (anEntry.equals(list[index]))
found = true;
}//end for
return found;
} // end contains
public boolean equals(Object object){
Item item = (Item) object;
if (itemNo == item.itemNo)
return true;
return false;
}
If you change the class Item equals() and compareTo() methods, so that they check only one object field, such as a quantity, it could result in strange behavior in other parts of your application. For example, two items with different itemNo, itemName, and itemPrice, but with the same quantities could be considered equal. Besides, you wouldn't be able to change the comparison attribute without changing the equals() code every time.
Also, creating a custom contains() method makes no sense, since it belongs to the ArrayList class, and not to Item.
If you can use Java 8, a clean way to do it is to use the new Collection's removeIf method:
Suppose you have an Item class with the num and name properties:
class Item {
final int num;
final String name;
Item(int num, String name) {
this.num = num;
this.name = name;
}
}
Given a List<Item> called items and an int variable called number, representing the number of the item you want to remove, you could simply do:
items.removeIf(item -> item.num == number);
If you are unable to use Java 8, you can achieve this by using custom comparators, binary search, and dummy objects.
You can create a custom comparator for each attribute you need to look for. The comparator for num would look like this:
class ItemNumComparator implements Comparator<Item> {
#Override
public int compare(Item a, Item b) {
return (a.num < b.num) ? -1 : ((a.num == b.num) ? 0 : 1);
}
}
Then you can use the comparator to sort and search for the desired elements in your list:
public static void main(String[] args) {
List<Item> items = new ArrayList<>();
items.add(new Item(2, "ball"));
items.add(new Item(5, "cow"));
items.add(new Item(3, "gum"));
Comparator<Item> itemNumComparator = new ItemNumComparator();
Collections.sort(items, itemNumComparator);
// Pass a dummy object containing only the relevant attribute to be searched
int index = Collections.binarySearch(items, new Item(5, ""), itemNumComparator);
Item removedItem = null;
// binarySearch will return -1 if it does not find the element.
if (index > -1) {
// This will remove the element, Item(5, "cow") in this case, from the list
removedItem = items.remove(index);
}
System.out.println(removedItem);
}
To search for another field like name, for example, you would need to create a name comparator and use it to sort and run the binary search on your list.
Note this solution has some drawbacks though. Unless you are completely sure that the list didn't change since the last sort, you must re-sort it before running the binarySearch() method. Otherwise, it may not be able to find the correct element. Sorting complexity is O(nlogn), so running it multiple times can get quite expensive depending on the size of your list.
Do you want to remove an object at a specific index? I'm not entirely sure what you mean by 'number field'. If so, jump to method: remove(int):
http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#remove%28int%29
EDIT: If you want to find/adjust a field of an object in the Array list, you can do this (piece of my own code):
public boolean studentHasISBN(ArrayList<Student> st, String s){
for(Student j : st) {
if(s.equals(j.getRentedBookISBN()))
return true;
}
return false;
}
All you have to do is iterate through the list, and search through the field that you want to find. Then use the remove(int) method.
simply use the remove function of ArrayLists in Java:
theNameOfYourArrayList.remove(ItemNumber);
to remove the element which has the index (int ItemNumber)
to check if the element with item number (int ItemNumber) exists in your ArrayList (hypothetically called theNameOfYourArrayList):
theNameOfYourArrayList.get(ItemNumber);
I'm going to assume that by 'number field' you mean that you invoked ArrayList with the Integer data type. I have a few different solutions to your problem:
ArrayLists, assuming that the ArrayList is ArrayList<Integer> numList = new ArrayList<Integer>(); you can simply write a method that will search 'numList' and delete the index that the number is. The problem is, contains and find in ArrayLists can be slow.
public void deleteNumField(int field) {
// this will stop any error if field isn't actually in numList
// and it will remove the first index of field in the ArrayList
if(numList.contains(field)) numList.remove(numList.find(field));
}
HashSets, HashSets are a handy data type that is like an ArrayList, except, its data is its 'index' (sortof). I won't go in depth about how they work, but I will say that searching in them is considered O(1). This will make your deletion really easy, and fast. Note: the HashSet assumes there are no duplicate numbers, if there are use a HashMap.
HashSet<Integer> numList = new HashSet<Integer>();
public void deleteNumField(int field) {
// this will stop errors from attempting to remove a
// non-existant element, and remove it if it exists.
if(numList.contains(field)) numList.remove(field);
}
For more information on HashMaps, HashSets and ArrayLists, see:
http://docs.oracle.com/javase/8/docs/api/
I have a list with some strings in it:
GS_456.java
GS_456_V1.java
GS_456_V2.java
GS_460.java
GS_460_V1.java
And it goes on. I want a list with the strings with the highest value:
GS_456_V2.java
GS_460_V1.java
.
.
.
I'm only thinking of using lots of for statements...but isn't there a more pratical way? I'd like to avoid using too many for statements...since i'm using them a lot when i execute some queries...
EDIT: The strings with the V1, V2,.... are the names of recent classes created. When someone creates a new version of GS_456 for example, they'll do it and add its version at the end of the name.
So, GS_456_V2 is the most recent version of the GS_456 java class. And it goes on.
Thanks in advance.
You will want to process the file names in two steps.
Step 1: split the list into sublists, with one sublist per file name (ignoring suffix).
Here is an example that splits the list into a Map:
private static Map> nameMap = new HashMap>();
private static void splitEmUp(final List names)
{
for (String current : names)
{
List listaly;
String[] splitaly = current.split("_|\\.");
listaly = nameMap.get(splitaly[1]);
if (listaly == null)
{
listaly = new LinkedList();
nameMap.put(splitaly[1], listaly);
}
listaly.add(current);
}
Step 2: find the highest prefix for each name. Here is an example:
private static List findEmAll()
{
List returnValue = new LinkedList();
Set keySet = nameMap.keySet();
for (String key : keySet)
{
List listaly = nameMap.get(key);
String highValue = null;
if (listaly.size() == 1)
{
highValue = listaly.get(0);
}
else
{
int highVersion = 0;
for (String name : listaly)
{
String[] versions = name.split("_V|\\.");
if (versions.length == 3)
{
int versionNumber = Integer.parseInt(versions[1]);
if (versionNumber > highVersion)
{
highValue = name;
highVersion = versionNumber;
}
}
}
}
returnValue.add(highValue);
}
return returnValue;
}
I guess you don't want simply the lexicographic order (the solution would be obvious).
First, remove the ".java" part and split your string on the character "_".
int dotIndex = string.indexOf(".");
String []parts = split.substring(0, dotIndex).split("_");
You are interested in parts[1] and parts[2]. The first is easy, it's just a number.
int fileNumber = Integer.parseInt(parts[1]);
The second one is always of the form "VX" with X being a number. But this part may not exist (if it's the base version of the file). In which case we can say that version is 0.
int versionNumber = parts.length < 2 ? 0 : Integer.parseInt(parts[2].substring(1));
Now you can compare based on these two numbers.
To make things simple, build a class FileIdentifier based on this:
class FileIdentifier {
int fileNumber;
int versionNumber;
}
Then a function that create a FileIdentifier from a file name, with logic based on what I explained earlier.
FileIdentifier getFileIdentifierFromFileName(String filename){ /* .... */ }
Then you make a comparator on String, in which you get the FileIdentifier for the two strings and compare upon FileIdentifier members.
Then, to get the string with "the highest value", you simply put all your strings in a list, and use Collections.sort, providing the comparator.