I have list List
where Custom
is like
class Custom{
public int id;
public String name;
}
How to
You could also sort the list before looping, then use divide-and-conquer to find matches, then count. It really depends on your needs, like how many elements? Are there a lot of insertions after a search? etc
The way it is defined now will always require looping over the list.
Creating a secondary index with a map of names to list of ids is one good idea.
One more option would be to make sure the list is ordered by name, in which case all "Tom"s would be stored next to each other. Then you could find the fist "Tom" in O(log(n)) time with a binary search, and just keep counting from there until you reach a non-"Tom" or end of the list. The insert operation would have O(n) complexity as you need to move all elements after the insert location by one position, so consider this carefully :-)
This can now be done easily with Java 8 streams — no extra libraries required.
List<Custom> list = /*...*/;
long numMatches = list.stream()
.filter(c -> "Tom".equals(c.name))
.count();
Either you keep track as you add or remove items from the list. This could take the place of a hashmap Name->Count . Where when you add an item you increment the count for that name, and when you remove it you decrement the count.
Or you iterate over the collection with a loop checking for the name in question.
Depending on the behavior of your application one of these methods will be faster but without more information it is hard to tell which.
You can use count()
from Eclipse Collections.
MutableList<Custom> customList = Lists.mutable.empty();
int count = customList.count(each -> "Tom".equals(each.getName()));
If you can't change customList from List
:
List<Custom> customList = new ArrayList<>();
int count = ListAdapter.adapt(customList).count(each -> "Tom".equals(each.getName()));
If you have a method which checks for a name you can also use countWith()
:
MutableList<Custom> customList = Lists.mutable.empty();
int count = customList.countWith(Custom::isNamed, "Tom");
class Custom
{
public int id;
public String name;
public boolean isNamed(String nameToCheck)
{
return nameToCheck.equals(this.name);
}
}
Note: I am a contributor to Eclipse Collections.
What about this? :
package test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class CustomArrayBuilder extends ArrayList<Custom> {
Map<String, Integer> namesMap = new HashMap<String, Integer>();
public CustomArrayBuilder(Collection<? extends Custom> c) {
super(c);
this.prepareAddAll(c);
}
public int getDifferentNamesAmount() {
return this.namesMap.size();
}
public int getNameAmount(String name) {
Integer integer = this.namesMap.get(name);
return (integer != null) ? integer : 0;
}
/**
* {@inheritDoc}
*/
@Override
public Custom set(int index, Custom element) {
Custom custom = super.set(index, element);
prepareSet(custom, element);
return custom;
}
/**
* {@inheritDoc}
*/
@Override
public boolean add(Custom e) {
this.prepareAdd(e);
return super.add(e);
}
/**
* {@inheritDoc}
*/
@Override
public void add(int index, Custom element) {
this.prepareAdd(element);
super.add(index, element);
}
/**
* {@inheritDoc}
*/
@Override
public Custom remove(int index) {
Custom custom = super.remove(index);
this.prepareRemove(custom);
return custom;
}
/**
* {@inheritDoc}
*/
@Override
public void clear() {
super.clear();
this.namesMap.clear();
}
/**
* {@inheritDoc}
*/
@Override
public boolean addAll(Collection<? extends Custom> c) {
this.prepareAddAll(c);
return super.addAll(c);
}
/**
* {@inheritDoc}
*/
@Override
public boolean addAll(int index, Collection<? extends Custom> c) {
this.prepareAddAll(c);
return super.addAll(index, c);
}
/**
* {@inheritDoc}
*/
@Override
public boolean remove(Object o) {
if (super.remove(o)) {
this.prepareRemove((Custom) o);
return true;
} else {
return false;
}
}
private void prepareSet(Custom oldCustom, Custom newCustom) {
if (oldCustom != null && !oldCustom.name.equals(newCustom.name)) {
this.prepareRemove(oldCustom);
this.prepareAdd(newCustom);
}
}
private void prepareAdd(Custom custom) {
if (custom != null) {
Integer integer = this.namesMap.get(custom.name);
this.namesMap.put(custom.name, (integer != null) ? integer + 1 : 1);
}
}
private void prepareAddAll(Collection<? extends Custom> c) {
for (Custom custom : c) {
this.prepareAdd(custom);
}
}
private void prepareRemove(Custom custom) {
if (custom != null) {
Integer integer = this.namesMap.get(custom.name);
this.namesMap.put(custom.name, (integer != null && integer > 0) ? integer - 1 : 0);
}
}
}
Usage:
package test;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<Custom> list = new ArrayList<Custom>() {{
add(new Custom("A"));
add(new Custom("B"));
add(new Custom("C"));
add(new Custom("A"));
add(new Custom("A"));
add(new Custom("B"));
}};
CustomArrayBuilder customs = new CustomArrayBuilder(list);
Custom custom = new Custom("B");
customs.add(custom);
customs.add(custom);
customs.remove(custom);
customs.remove(custom);
customs.remove(custom);
System.out.println("A: " + customs.getNameAmount("A"));
System.out.println("B: " + customs.getNameAmount("B"));
System.out.println("C: " + customs.getNameAmount("C"));
System.out.println("Z: " + customs.getNameAmount("Z"));
System.out.println("Total different names: " + customs.getDifferentNamesAmount());
}
}
Output:
A: 3
B: 2
C: 1
Z: 0
Total different names: 3
It could be usefull when you often use your count operations. Note: You shouldn't change name of custom object, it should be final:
package test;
class Custom {
public int id;
final public String name;
public Custom(String name) {
this.name = name;
}
}
Or you must do something with list too, when you are changing name of some Custom object from the list.