I've been going through SO pages all morning trying to figure out the best way to attack my question:
What is the most efficient way to sort through an ArrayList, match on a name in the ArrayList with a name I'm pulling from a WebElement. I'm not experienced with Java, and wondering if this context it makes more sense to use HashTables, but I couldn't find an easily understandable answer on how to use them with multiple values per index:
My custom class:
public class KnowledgePermission {
public String name;
public String htmlType;
public Boolean isAllowed;
public KnowledgePermission(String name, String htmlType, Boolean isAllowed) {
this.name = name;
this.htmlType = htmlType;
this.isAllowed = isAllowed;
}
public String getName() {
return name;
}
public String getHtmlType() {
return htmlType;
}
public Boolean getIsAllowed() {
return isAllowed;
}
#Override
public boolean equals(Object obj) {
boolean result = false;
if(obj instanceof KnowledgePermission) {
KnowledgePermission otherPermission = (KnowledgePermission) obj;
result = (this.name == otherPermission.name);
}
return result;
}
#Override
public int hashCode() {
int result = 17;
result = 31 * result + name.hashCode();
result = 31 * result + htmlType.hashCode();
result = 31 * result + isAllowed.hashCode();
return result;
}
}
I'm able to use Java 8, so I've looked at filters, haven't been successful yet.
Here's the snippet after I've created a list using my class type.
What I'm trying to do is get the XPath of some browser page items, get its name via Selenium's WebDriver API, and for the one item I know should match in my permission list, access one of the other two properties - htmlType or isAllowed - and continue logic based off of that.
List<KnowledgePermission> permissionList = new ArrayList<KnowledgePermission>();
permissionList.add(new KnowledgePermission("checkbox1sName", "checkbox", true ));
permissionList.add(new KnowledgePermission("checkbox2sName", "checkbox", true ));
List<WebElement> checkboxes = driver.findElements(By.xpath("//*someXpathinfoHere//input[#type='checkbox']"));
// check the value of each checkbox and display
for(WebElement item : checkboxes) {
String elname = item.getAttribute("name");
Boolean hasBeenSelected = item.isSelected();
// find the permission in the list
System.out.println("filtering permissions list");
List<KnowledgePermission> currentPermission = permissionList.stream().filter(permission -> elname.equals(permission)).collect(Collectors.toList());
System.out.println(currentPermission);
}
All that prints out for each iteration of the loop is:
filtering permissions list
[]
So I'm guessing I'm not understanding filtering correctly here.
Any help and I'd be grateful!!
.filter(permission -> elname.equals(permission.getName()))
... is all you have to change
If elname can be null, change the order to
.filter(permission -> permission.getName().equals(elname))
Since instance.equals(null) returns false, not NullPointerException
Related
This simple method is supposed to add elements to a HashMap. The elements are read from a txt file, I have tested that and everything regarding that area is working just fine. The issue comes when I print the values that are in the Map.Therefore, I think there might be some issue when adding them.
The content of the txt file is
SW: (scores, 90); (scores, 91); (scores, 92);
Where the Permissions is "SW" and the Attributes are "(scores, 90)... (scores, 92)". These are the elements used in the following methods.
public Map<Permissions, ArrayList<Attributes>> paMap;
public void addMap(Permissions per, Attributes att) {
if(paMap.containsKey(per)) {
paMap.get(per).add(att);
}
else{
ArrayList<Attributes> attList = new ArrayList<Attributes>();
attList.add(att);
paMap.put(per, attList);
}
}
//Attributes contain variables (Scores) and values(90-93), that is just for printing
public void printMap() {
ArrayList<Attributes> list;
for(Permissions p: paMap.keySet()) {
list = paMap.get(p);
System.out.print(p.name + ": ");
for(Attributes l: list) {
System.out.print("<" + l.variable +", " + l.value + ">; ");
}
System.out.println();
}
When I print the Map I expect to get exactly the same thing that is in the txt file, but instead I get
SW: (scores, 92); (scores, 92); (scores, 92)
Even if I remove the paMap.get(per).add(att) line from the addMap method I end up getting SW: (scores, 92);
Also, I have tested if the for loop is being called properly and it is.
Therefore, I am confused on how the attributes on the ArrayList are getting updated and what would be the right way of doing it. Do you see any error in the way I am adding or printing the values?
Thank you for your help
I dont know how looks like read method but seems like You are adding all the time the same attribute, so maybe in read function You are not create new attribute but using the same object all the time and just call sets on this, so in the array lsit at the end is the same object added three times
btw:
also method addmap can be replaced by:
paMap.computeIfAbsent(per,x->new ArrayList<Attributes>()).add(att);
If You are using java8 and above ;)
Couple of suggestions:
use interfaces in the Map declaration:
public Map<Permissions, List<Attributes>> paMap;
in order to be used as key in the Map ensure Permissions is
immutable and has equals and hashCode properly defined
So, assuming Permissions has only 1 String field, it should look something like this:
public final class Permissions {
private final String permission;
public Permissions(String permission) {
this.permission = permission;
}
public String getPermission() {
return this.permission;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Permissions that= (Permissions ) o;
return Objects.equals(this.permission, that.permission);
}
#Override
public int hashCode() {
return Objects.hash(this.permission);
}
}
Try another implementation of printMap method. For example, with Java
8:
private void printMap() {
paMap.entrySet().stream().map(this::format).forEach(System.out::println);
}
private String format(Map.Entry<Permissions, List<Attributes>> entry) {
String key = formatPermissions(entry.getKey());
String value = formatAttributes(entry.getValue());
return String.format("%s: %s", key, value);
}
private String formatPermissions(Permissions permissions) {
return permissions.name;
}
private String formatAttributes(List<Attributes> attributes) {
return attributes.stream()
.map(attr -> String.format("(%s, %s)", attr.variable, attr.value))
.collect(Collectors.joining("; "));
}
I have a class like this:
public static class TiposDeHistorial
{
String CODIGO, TIPO;
public TiposDeHistorial()
{
}
public String getCODIGO()
{
return CODIGO;
}
public void setCODIGO(String CODIGO)
{
this.CODIGO = CODIGO;
}
public String getTIPO()
{
return TIPO;
}
public void setTIPO(String TIPO)
{
this.TIPO = TIPO;
}
}
and a list of it:
ArrayList<TiposDeHistorial> tiposHistorial;
So my question is: can I use tiposHistorial.contains(...) to search in a specific array field, CODIGO or TIPO, for example?
First of, you do not have an array but an ArrayList.
The contains method on a List operates with the equals method of it's stored elements (TiposDeHistorial in your case). Therefore the answer to your question is no.
Trying something like tiposHistorial.contains("a") will not work as there is a type mismatch: your list is of type TiposDeHistorial while you try to check for an element of String.
If you are using Java 8 you can use following code:
tiposHistorial.stream()
.filter(x -> "specific value for CODIGO".equals(x.getCODIGO()))
.findFirst()
.orElse(null);
It will return TiposDeHistorial object in the list containing specific CODIGO value or null otherwise.
As for your question: "contains" method just returns "true" or "false", not an object. Moreover it uses "equals" method of your object, so it will not help if you want to search using fields.
Contains method will return true only if your object equals with ur list elements objects.
You can try extending equals method and have your own criteria which can work for either CODIGO or TIPO.
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
test other = (test) obj;
if (CODIGO == null) {
if (other.CODIGO != null)
return false;
} else if (!CODIGO.equals(other.CODIGO))
return false;
return true;
}
The answers already given here are all correct, just if You don't know java streams, and would like to check if the list contains both some CODIGO and TIPO fields, for me the simplest solution would be:
ArrayList<TiposDeHistorial> tiposHistorial = new ArrayList<>();
//add elements to the list
String tipo = "TIPO"; // the TIPO value You are looking for in the list
String codigo = "CODIGO"; // the CODIGO value You are looking for in the list
boolean containsTipo = false;
boolean containsCodigo = false;
for (TiposDeHistorial element: tiposHistorial) {
if (!containsTipo && element.getTIPO().equals(tipo)) {
containsTipo = true;
}
if (!containsCodigo && element.getCODIGO().equals(codigo) ){
containsCodigo = true;
}
if (containsTipo && containsCodigo)
break;
}
By editing it just a bit, You may also find which elements of the array contain the values You are looking for, if that will be Your intention
I have a list which contains Strings ABC:123,abc:123 ;when I am converting it to Set its giving me 2 different elements.Is there a one liner way to convert this List to Set ignoring the case so that my Set contains ABC:123 only.` But if the input List contains ABC:123a4,abc:1234A4 it should give me 2 different elements in the Set : ABC:123a4,ABC:1234A4
I know this can be done spliting the list elements on ":" first and converting the abc to all uppercase and adding them to new list and then the rest.But just wanted to know if there a better way (small lines of code) to do that.Thanks for any brain storming ideas in advance.
List<String> memlist = new ArrayList<String>(Arrays.asList(memberList.split(",")));
Set<String> memberSet = new HashSet<String>(memlist );
memlist = new ArrayList<String>(memberSet);
You can use a TreeSet with the String.CASE_INSENSITIVE_ORDER flag set.
String startingString = "ABC:123,abc:123";
List<String> caseSensitiveList = Arrays.asList(startingString.split(","));
Set<String> caseInsensitiveSet = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
caseInsensitiveSet.addAll(caseSensitiveList);
for(String caseInsensitiveString : caseInsensitiveSet){
System.out.println(caseInsensitiveString);
}
This code, when run, gives me the output:
ABC:123
replace
memberList.split(",")
with
memberList.toUpperCase().split(",")
The cleanest solution is the one suggested by #SQLHacks. But then you said ABC:123a4 must be different from abc:1234A4. I guess the only solution now is to create a wrapper for the String objects and override the equals() and hashCode() method to do what you want, as #PaulBoddington suggested in his comment.
This is what I came up with (edited and improved based on #nafas answer):
public class StringWrapper {
private String value;
private String beforeColon;
private String afterColon;
private int hash;
public StringWrapper(String value) {
this.value = value;
String[] splitted = value.split(":");
beforeColon = splitted[0];
afterColon = splitted[1];
hash = Objects.hash(beforeColon.toUpperCase(), afterColon);
}
public String getValue() {
return value;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof StringWrapper) {
StringWrapper other = (StringWrapper) obj;
return beforeColon.equalsIgnoreCase(other.beforeColon) && afterColon.equals(other.afterColon);
}
return false;
}
#Override
public int hashCode() {
return hash;
}
#Override
public String toString() {
return value;
}
}
And then:
// this method is just to help you building a List<StringWrapper> from your String (memberList variable)
public static List<StringWrapper> split(String string, String regex) {
List<StringWrapper> list = new ArrayList<>();
for (String element : string.split(regex)) {
list.add(new StringWrapper(element));
}
return list;
}
public static void main(String[] args) {
String memberList = "ABC:123,abc:123,ABC:123a4,ABC:123A4";
List<StringWrapper> memlist = new ArrayList<>(split(memberList, ","));
Set<StringWrapper> memberSet = new HashSet<>(memlist);
memlist = new ArrayList<StringWrapper>(memberSet);
for (StringWrapper element : memlist) {
System.out.println(element);
}
}
If you run this, you get as output the following:
ABC:123a4
ABC:123A4
ABC:123
abc:123 is out but ABC:123a4 and ABC:123A4 are both present.
You can make things even easier changing the static split method to create the Set for you. The reason I didn't do that was to make things look familiar to you.
what is wrong with actually creating a little Model class to take care of all possible cases?
final class Model{
final String firstPart;
final String secondPart;
final int hashCode;
Model(String s){
String[] splitted=s.split(":");
firstPart=splitted[0];
secondPart=splitted[1];
hashCode=Objects.hash(firstPart.toLowerCase(),secondPart);
}
#Override
public boolean equals(Object o){
String[] splitted=o.toString().split(":");
return firstPart.equalsIgnoreCase(splitted[0]) && secondPard.equals(splitted[1]);
}
#Override
public int hashCode(){
return hashCode;
}
#Override
public String toString(){
return firstPart+":"+secondPart;
}
}
now create a set and etc:
Set<Model> set =new HashSet<Model>();
You can try this Java 8 one liner
Set<String> memberSet = memlist.stream().map(s -> s.toUpperCase()).collect(Collectors.toSet());
It will go through all strings in memlist convert them to uppercase and put them to a set.
That means of course that if your list contains "abc:123" but not "ABC:123", you will still get "ABC:123" in the set.
I am getting a warning that watchStore.contains(s) is a suspicious call to java.util.Collection#contains. How can I fix it? I want to use contains() to find a particular object with the matching serial number.
public Watch findWatchBySerialNumber(long srch) {
long s = srch;
Watch watch = null;
for(int i = 0; i < watchStore.size(); i++) {
watch = watchStore.get(i);
if(watchStore.contains(s)) {
System.out.print("item found");
return watch;
}
}
System.out.print("item not found");
return null; // watch is not found.
}
Presuming that Watch is the class, watchStore is a List<Watch>, and that a field serialNo exists on Watch...
public Optional<Watch> findWatchBySerialNumber(long serial) {
return watchStore.stream()
.filter(w -> w.getSerialNo() == serial)
.findFirst();
}
If you're not using Java 8, the code is close, but a bit more dangerous since you have the chance to return null. If you can use Guava's Optional, that'd be a better choice here.
public Watch findWatchBySerialNumber(long serial) {
for(Watch w : watchStore) {
if(w.getSerialNo() == serial) {
return w;
}
}
return null;
}
Your contains isn't going to work since your list doesn't contain Longs, it contains Watchs. This is also why the compiler sees it as dubious; contains accepts an Object but it will return false if what you're looking for doesn't have a comparable equals for what's in your list.
You have to iterate over the entirety of your collection to find it in this scenario, especially since you're looking for a specific property on those objects as opposed to a specific, easy-to-provide value.
please how can I fix that. I want to use the contain() to find a
particular object with the matching serial number.
In that case override Watch's equals() to use serialNumber field for comparison.
Then add constructor that accepts serialNumber.
public class Watch {
private final long serialNumber;
public Watch(long serialNumber) {
this.serialNumber = serialNumber;
}
#Override
public boolean equals(Object obj) {
return obj == this ||
(obj instanceof Watch && ((Watch)obj).serialNumber == serialNumber);
}
#Override
public int hashCode() {
return (int)serialNumber;
}
}
Replace if(watchStore.contains(s)){ with if(watchStore.contains(watchToFind)){ where Watch watchToFind = new Watch(s);
you can use contains method from org.apache.commons.lang.ArrayUtils package.
Checks if the value is in the given array.
The method returns false if a null array is passed in.
Parameters:
array the array to search through
valueToFind the value to find
Returns:
true if the array contains the object
long [] imageHashes= {12l,13l,14l,15l};
System.out.println(ArrayUtils.contains(imageHashes, 13l));
There is a collection of 20 objects of a POJO class. I Want to write a method that return objects with distinct value. Now this is my Pogo class
class Student {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName( String firstName ) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName( String lastName ) {
this.lastName = lastName;
}
}
Now i want some method which returns unique last names values. I could not understand which logic i have to put in this.
If you are using something like Eclipse, you can right-click the source and select Source > "Generate hashCode() and equals()...". Doing so will yield something like this:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (firstName == null) {
if (other.firstName != null)
return false;
} else if (!firstName.equals(other.firstName))
return false;
if (lastName == null) {
if (other.lastName != null)
return false;
} else if (!lastName.equals(other.lastName))
return false;
return true;
}
Then, you'll want to add your objects to an instance of Set, maybe HashSet. Sounds like you can just return the populated Set then.
See also this.
EDIT: Note that I am not suggesting to put all of this on the Student class. The code shown above goes on Student, but the method that returns the set of distinct students goes somewhere else.
EDIT 2: If you are only interested in unique last names, you could modify hashCode() and equals() to not consider first name, but I concede that this would be quite unintuitive and recommend to avoid this in any circumstance other than an academic exercise. So, more correct might be to layer on an instance of Comparator that only considers last name--see doc and this.
You can use an Arraylist, it has a built in function called .contains() which checks if the arrayList contains a specific value. So you would create an arrayList of last names and if it doesn't exist in the array list, just add it. See http://docs.oracle.com/javase/1.5.0/docs/api/java/util/ArrayList.html#contains(java.lang.Object)
You can try to use Set, if you need to get only one field, or Map, if you need to know object(student) with this field.
If you need to know all distinct Students (pair: first name + surname), you need to override getHashCode() and equals methods and use HashSet, HashMap
An easy way (for a beginner) to do this is just create a new array (same size of the input array). Then to loop through your array then compare every value to every other value in the array. If you can't find a match, then put this value in the new array.
Pseudo code:
public static Student[] GetUniqueLastNames(Student[] students){
Student[] retArray;//new array
for(i = 0; i < students.size; i++){
unique = true
for(j=0; j < students.size; j++){
if(i != j){//make sure its not comparing the same value
if(students[i].lastname.equals(students[j].lastname)){
unique = false
break
}
}
}
if(unique){
retArray[i] = students[i]
}
}
return retArray
}
Note: There are far better ways of doing this, but this is a nice basic way to do it if you're learning Java (or programming in general).
If you don't care about keeping the order of the objects, you can use a set:
public static <S extends Student> Collection<S> uniqByLastName(Collection<S> source){
TreeSet<S> result = new TreeSet<S>(new Comparator<S>() {
#Override
public int compare(S s1, S s2) {
return s1.getLastName().compareTo(s2.getLastName());
}
});
result.addAll(source);
return result;
}
If you care about the order
public static <S extends Student> Collection<S> uniqByLastName(Collection<S> source){
Collection<S> result = new ArrayList<S>();
Set<String> addedStudents = new HashSet<String>();
for(S student : source){
String lastName = student.getLastName();
if(!addedStudents.contains(lastName)){
result.add(student);
addedStudents.add(lastName);
}
}
return result;
}
If you want to modify the collection without returning a new one
public static <S extends Student> void uniqByLastName(Collection<S> source){
Set<String> addedStudents = new HashSet<String>();
Iterator<S> iterator = source.iterator();
while(iterator.hasNext()){
S student = iterator.next();
String lastName = student.getLastName();
if(addedStudents.contains(lastName)){
iterator.remove();
} else {
addedStudents.add(lastName);
}
}
}
If you are using Java 8, you can use lambda expression to solve it. Using following code snippet should solve your problem:
list.stream().collect(Collectors.toMap(Student::getLastName, p -> p, (p, q) -> p)).values();
Note: it will return first student with a given last name and as you might have already guessed, you don't need to override equals and hashcode.