Custom method for ArrayList - java

Hello I would like to make a custom method for ArrayList class.
So lets say I make a new ArrayList.
ArrayList<String> list = new ArrayList<String>
I would like to make a method I can call on list.
Something like this:
list.myMethod();
What I want to solve with my method is so you can get an Object by Object name and not index inside the ArrayList.
So basically I want to make a method returning following:
list.get(list.indexOf(str));
To sum it up:
ArrayList<String> list= new ArrayList<>();
String str = "asd";
String str2 = "zxc";
list.add(str2);
list.add(str);
System.out.println(list.get(0));
System.out.println(list.get(list.indexOf(str)));
Will print: "asd" "asd".
So instead of writing: list.get(list.indexOf(Object))
I would like to be a able to write list.myMethod(Object) and get the same result. I hope you understand my question. I know this is probably a dumb solution and I could just use a Map. But this is for learning purpose only and nothing I will use.

Custom method >>
public class MyArrayList<E> extends ArrayList<E> {
public E getLastItem(){
return get(size()-1);
}
}
How to use it >>
MyArrayList<String> list= new MyArrayList<>();
String str = "asd";
String str2 = "zxc";
list.add(str2);
list.add(str);
System.out.println(list.getLastItem());

what you need requires to extend the ArrayList classs, but you should consider using instead a
Map<String, Object>
with that approach you can do something like
myMap.get("myObject1");

You should just extend the ArrayList class creating your own with the new method. But the performance would be horrible if your list grow too much. The indexOf method have O(n), so greater is the size of your array longer is the time you have to wait.
May be you should choose a different collection if you want access directly to the element. In your case, it elements stored in the collection are unique, you could use a Set.
On the other hand, a Set does not preserve the insertion order. I don't know if this is a think you have to care of.
And a Set just let you know if the element is contained into the collection.
Another collection that can be of your interest is the Map, this is a key-value collection.
But given that you have only keys this it seems not be your case.

Related

Assigning List with preserving reference

Suppose I have two Lists and I want to copy/assign all of one list to another list with preserving reference to the original list. I use this code
List<String> mylist = new List<String>();
List<String> another = getSomeList();
// I have to do
mylist.clear();
mylist.addAll(another);
This works fine, but my question is, is there any better way to do this?
Thanks
I don't think that there is an easier way. You can just implement your own list that has e.g. a setAll() method.
class MyArrayList<E> extends ArrayList<E> {
public void setAll(Collection<E> collection) {
clear();
addAll(collection);
}
}
But this only moves the the clear() and addAll() invokation into another method. Sure from a clients prespective it makes the call easier
MyArrayList<String> mylist = new MyArrayList<String>();
mylist.setAll(another);
but at the price that you use a special list implementation. Maybe you only use this implementation inside of a class and your api does not expose that you use a MyArrayList. Than it might be ok. I would just do it the way you already do.
is there any better way to do this?
No, not for your specifications. You can delete mylist = another;.
And there's no need to call mylist.clear() when you just assigned mylist = new List<String>();
So really all you need is that last line where you addAll(another).
You can loop through first and add all elements into another:
for(String s : mylist){
another.add(s);
}

<List<String[]>> issue

Here is my situation:
I have a method which sorts a file based on keywords and save the result into almost 21 List<String[]> variables.
List<String[]> sortKeyword(List<String[]> csvList, String[] keywords)
So call this function, I get 21 List<String[]> variables, but I don't know how to return the these lists.
After receive the return result, I also need to do for loop to update Database.
Thus I have two questions:
1. How to return the lists.
2. How to sort the return lists.
Could someone give me some advice. I really appreciate it.
Thank you
You can't return more than one object in Java but that returned object can be a collection of objects.
For your purpose you can add all those lists to a new List and return the new List.
You can return a list of lists.
List<List<String[]>> ll = new ArrayList<>();
ll.add(list1);
ll.add(list2);
return ll;
To return the three lists, either you create a wrapper class to add all the three lists to it or simply create a list of lists.
Another problem in your code, :
Use equals for string comparisoon:
if(kWord =="kWord1")
should be replaced to
if(kWord.equals("kWord1"))
Change the return type from List<String[]> to Map<String, ArrayList<String[]>>. The map String key will store the keyword and the ArrayList value will store the result line.
If I understand your problem, you're doing the above in a function and want to get the function to return all three lists, list1, list2, and list3.
In this case, you could return an array of three lists, or an ArrayList of three lists. In general, if I want a function that returns multiple values (that may not all be the same type), I usually just create a small class to contain them:
private static class KeywordSearchResults {
List<String[]> list1;
List<String[]> list2;
List<String[]> list3;
KeywordResults (List<String[]> list1, List<String[]> list2,
List<String[]> list3) {
this.list1 = list1; this.list2 = list2; this.list3 = list3;
}
}
(I usually do this as a nested class; whether to make it private or public depends on your needs.)
Normally it's a bad idea to declare a class with non-private fields, but I think if you're just using it as a wrapper just to return multiple values from a function or to create an object whose only purpose is to pass certain fields around together, and there aren't any other methods in the class, I think it's fine because the class doesn't really represent some "higher concept". Still, this is the kind of solution you'd adopt only when it's really appropriate; I think it's best to look around to see if there's a better design.

In Java, How to remove duplication from an ArrayList<StringBuilder> efficiently?

I tried to use HashSet to remove the duplications from an ArrayList<StringBuilder>.
E.g. Here is an ArrayList, each line is a StringBuilder object.
"u12e5 u13a1 u1423"
"u145d"
"u12e5 u13a1 u1423"
"u3ab4 u1489"
I want to get the following:
"u12e5 u13a1 u1423"
"u145d"
"u3ab4 u1489"
My current implementation is:
static void removeDuplication(ArrayList<StringBuilder> directCallList) {
HashSet<StringBuilder> set = new HashSet<StringBuilder>();
for(int i=0; i<directCallList.size()-1; i++) {
if(set.contains(directCallList.get(i)) == false)
set.add(directCallList.get(i));
}
StringBuilder lastString = directCallList.get(directCallList.size()-1);
directCallList.clear();
directCallList.addAll(set);
directCallList.add(lastString);
}
But the performance becomes worse and worse as the ArrayList size grows. Is there any problem with this implementation? Or do you have any better ones in terms of performance?
StringBuilder doesn't implement equals() or hashcode(). Two StringBuilders are only equal if they are the exact same object, so adding them to a HashSet won't exclude two different StringBuilder objects with identical content.
You should convert the StringBuilders to String objects.
Also, you should initialize your HashSet with an "initial capacity" in the constructor. This will help with the speed if you are dealing with large numbers of objects.
Lastly, it's not necessary to call contains() on the hashset before adding an object. Just add your Strings to the set, and the set will reject duplicates (and will return false).
Let's analyze your method to find where we can improve it:
static void removeDuplication(ArrayList<StringBuilder> directCallList) {
HashSet<StringBuilder> set = new HashSet<StringBuilder>();
for(int i=0; i<directCallList.size()-1; i++) {
if(set.contains(directCallList.get(i)) == false)
set.add(directCallList.get(i));
}
This for loop repeats once for each element in the ArrayList. This seems unavoidable for the task at hand. However, since HashSet can only contain one of each item, the if statement is redundant. HashSet.add() does the exact same check again.
StringBuilder lastString = directCallList.get(directCallList.size()-1);
I don't understand the need to get the lastString from your list and then add it. If your loop works correctly, it should have already been added to the HashSet.
directCallList.clear();
Depending on the implementation of the list, this can take up to O(n) time because it might need to visit every element in the list.
directCallList.addAll(set);
Again, this takes O(n) time. If there are no duplicates, set contains the original items.
directCallList.add(lastString);
This line seems to be a logic error. You will add a String which is already in the set and added to directCallList.
}
So overall, this algorithm takes O(n) time, but there is a constant factor of 3. If you can reduce this factor, you can improve the performance. One way to do this is to simply create a new ArrayList, rather than clearing the existing one.
Additionally, this removeDuplication() function can be written in one line if you use the correct constructors and return the ArrayList without duplicates:
static List<StringBuilder> removeDuplication(List<StringBuilder> inList) {
return new ArrayList<StringBuilder>(new HashSet<StringBuilder>(inList));
}
Of course, this still doesn't address the issues with StringBuilder that others have pointed out.
So you had some other options, but I like my solutions short, simple, and to the point. I've changed your method to no longer manipulate the parameter, but rather return a new List. I used a Set<String> to see if the contents of each StringBuilder was already included and returned the unique Strings. I also used a for each loop instead of accessing by index.
static List<StringBuilder> removeDuplication(List<StringBuilder> directCallList) {
HashSet<String> set = new HashSet<String>();
List<StringBuilder> returnList = new ArrayList<StringBuilder>();
for(StringBuilder builder : directCallList) {
if(set.add(builder.toString())
returnList.add(builder);
}
return returnList;
}
As Sam states, StringBuider does not override hashCode and equals and so the Set will not work appropriately.
I think the answer is to wrap the Builder in an object that executes toString only once:
class Wrapper{
final String string;
final StringBuilder builder;
Wrapper(StringBuilder builder){
this.builder = builder;
this.string = builder.toString();
}
public int hashCode(){return string.hashCode();}
public boolean equals(Object o){return string.equals(o);}
}
public Set removeDups(List<StringBuilder> list){
Set<Wrapper> set = ...;
for (StringBuilder builder : list)
set.add(new Wrapper(builder));
return set;
}
The removeDups method could be updated to extract the builders from the set and return a List<StringBuilder>
As explained, StringBuilders don't override Object#equals and aren't Comparable.
Although using StringBuilders to concatenate your Strings is the way to go, I would suggest that once you are done with your concatenation, you should store the underlying strings (stringBuilder.toString()) instead of the StringBuilders in your list.
Removing duplicates then becomes a one line:
Set<String> set = new HashSet<String>(list);
Or even better, store the strings in the set directly if you don't need to know that there are duplicates.

ArrayList with returned index on add value

I am looking for a java data structure similar to an ArrayList that when I do an add or a push with only a value argument an index will be returned for me automatically.
For example:
ArrayList<String> elements = new ArrayList<String>();
String element = "foo";
String elementTwo = "bar";
int index1 = elements.add(element); //note this does not exist, i.e. returns bool in api
int index2 = elements.add(elementTwo);
System.out.println(elements.get(index1)); //would give "foo"
I could see writing a wrapper class around ArrayList that manages a counter that is incremented on every add operation and invoking:
ArrayList.add(int index, E element)
Do you really need to write a wrapper around ArrayList for this? This seems like something simple enough to be provided out of the box somewhere?
Edit:
I need the index (key) to be fixed and unique for this usecase. A map was suggested and I agree with that. Does anyone know of a map implementation that gives you an automatically (uniquely) generated key on a value insert? I am just trying to decide if I need to implement my own wrapper for this.
The element will be added at the end of the list. So you can use elements.size()-1 to get the new elements index.
Note that this will not work reliable if multiple threads are modifying the list at the same time.
EDIT: Also note that it might not be a good idea to use an ArrayLists index as a unique ID because an elements index can change (for example when you remove an element or insert a new one using add(int, Object)). If this is a problem depends on what you want to do with the index: If you only need it for a short time after adding an element and can be sure that the list is not modified in the meantime, there is no problem. In the other case even a method returning the index when calling add(Object) would not help because the index does not get updated in anyway. To prevent this issue you can:
Make sure you never remove elements from the list and never add elements using add(int, Object).
Instead of removing elements you could also set them to null using the method set(int, null). This way no elements index will change.
Use some other data structure like for example a map with a custom ID like helloannalil suggests in his answer.
EDIT 2: I did not find a appropriate, ready to use implementation (but this does not mean there is none, of course). To suggest a good solution, more information on the intended use of the data structure is needed, but here are some ideas and notes:
If the maximum number of elements is not to large, an ArrayList could be used and the elements index represents the ID. As stated above, to remove an element it can be set to null so that no indices are changed. When inserting, positions with null values can be reused.
You can also use one of the two methods show in this answer: https://stackoverflow.com/a/8939049/1347968 (keywords AtomicLong or IdentityHashMap)
Do not depend on the "uniqueness" of Object.hashCode() or System.identityHashCode(Object) as it is not guaranteed (try it by running the example at the bottom of Suns/Oracles Bug #6321873).
Well what I do in that cases (I love ArrayLists) is to get the last index by asking the size of the list:
String thing = "theThing";
List<String> strList = new ArrayList<String>();
strList.add(thing);
int indexOfThing = strList.size() - 1;
I mean, is easier than implement your own List and just works.
if you really want this function, you can use map but not list
Based on your comments and edited question I think you can extend a HashMap for your use like this:
public class MyMap<V> extends HashMap<Integer, V> {
private static final long serialVersionUID = 1L;
public int add(V elem) {
int key = System.identityHashCode(elem);
super.put(key, elem);
return key;
}
}
Then inside your class declare MyMap like this:
private MyMap<String> map = new MyMap<String>();
And then add your elements to MyMap like this:
.....
.....
String element = "foo";
String elementTwo = "bar";
int index1 = map.add(element);
int index2 = map.add(elementTwo);
Now you have index1 and index2 as indices of you inserted strings that you can use or pass around for the lifetime of your application. You can insert or remove elements in MyMap as many times you want but your indices (index1 and index2) will give you back your inserted elements like this:
String elem1 = map.get(index1); // will return "foo"
String elem2 = map.get(index2); // will return "bar"
String thing = "theThing";
List<String> strList = new ArrayList<String>();
strList.add(thing);
int indexOfThing = strList.size() - 1;
If you remove an item, this will no longer work.

Setting a list equal to list using equal sign or copy constructor?

This is a simple question but if I do
List<Object> list = getObjectsFromDatabase();
This would not be the correct way to handle this?
But this would?
List<Object> firstList = getObjectsFromDatabase();
List<Object> list = new ArrayList<Object>(firstList);
Or if I had a class
public class ReportDisplayModel<T> {
public ReportDisplayModel(List<T> data) {
this.data = data;
}
public List<T> data;
}
And I wanted to set the data in this model I would use the constructor?
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>(getData());
Instead of
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>();
model.data = getData();
Just need a clarification. Thanks.
It depends entirely on what getData() returns.
usually it is made to return Collections.unmodifiableList(result) so that clients can't modify the result.
if this result is not used anywhere else, and modifications to it doesn't mess with anything, it is fine to use the result as-is
It is rarely needed to use the copy constructor - use it when you are sure that modifying the data will impact some other component.
Regarding
List<Object> list = getObjectsFromDatabase();
vs
List<Object> firstList = getObjectsFromDatabase();
List<Object> list = new ArrayList<Object>(firstList);
either approach is fine. Depends on if you want list to refer to the list returned by getObjectsFromDatabase() or if you want it to refer to a copy of it.
If simply want to, say, print the database objects, the first approach is fine.
If you want to, say, filter out half of the database objects (i.e., remove objects from the list), and you can't say for sure that getObjectsFromDatabase() returns a mutable list, then you'll have to go with the second approach.
Regarding
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>(getData());
vs
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>();
model.data = getData();
I'd prefer the first method. Simply because I wouldn't want to worry about null pointer exceptions etc if I accidentally do something like
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>();
model.printData();
model.data = getData();
I don't quite get your question, but I'll give it a try.
The main difference is that using the copy constructor creates a new independent copy of the list, i.e.
List<Object> firstList = getObjectsFromDatabase(); // firstList is the list returned by the database
List<Object> list = new ArrayList<Object>(firstList); //list is an independent copy of firstList
Now if you change firstList the list returned by getObjectsFromDatabase() would be changed as well (or would throw an exception if changes are not supported). On the other hand list could freely be changed without the original list being affected.
Avoid using the equal sign, because it breaks encapsulation (bad practice). Go for the copy constructor (best practice).

Categories