Why remove element of Map? - java

I am filtering the all list which ahve same lat,long in one list and put into an same list and put that list into map My code is as:-
private Collection<List<Applicationdataset>> groupTheList(ArrayList<Applicationdataset> arrayList)
{
Map<Key, List<Applicationdataset>> map = new HashMap<Key, List<Applicationdataset>>();
for(Applicationdataset appSet: arrayList)
{
Key key = new Key(appSet.getLatitude(), appSet.getLongitude());
List<Applicationdataset> list = map.get(key);
if(list == null){
list = new ArrayList<Applicationdataset>();
}
list.add(appSet);
map.put(key, list);
}
return map.values();
}
public class Key {
String _lat;
String _lon;
Key(String lat, String lon) {
_lat = lat;
_lon = lon;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key key = (Key) o;
if (!_lat.equals(key._lat)) return false;
if (!_lon.equals(key._lon)) return false;
return true;
}
#Override
public int hashCode() {
int result = _lat.hashCode();
result = 31 * result + _lon.hashCode();
return result;
}
}
But When I am debuging my code according to xml which come from web-service there is 2 list which have same lat long and they are saving in same list in amp at time of debuging but when I go next step of debug the element of map which have 2 item list decrease and showing size 1 I am unable to rectify this issue.

Your code looks OK: You've overridden equals() and hashCode() consistently.
Check for whitespace in the lat/lng values as the cause of your problems, perhaps trim() in the constructor:
Key(String lat, String lon) {
_lat = lat.trim();
_lon = lon.trim();
}
Also, you can simplify your code to this:
#Override
public boolean equals(Object o) {
return o instanceof Key
&& _lat.equals(((Key)o)._lat))
&& _lon.equals(((Key)o)._lon));
}
#Override
public int hashCode() {
// String.hashCode() is sufficiently good for this addition to be acceptable
return _lat.hashCode() + _lon.hashCode();
}

Thats a bit hard to understand what you are trying to accomplish. But I believe the issue is that you are using both latitude and longitude in Key hashCode()/equals() implementation thats why second Applicationdataset in your input list replaces the first one in your map object. You should implement the case when related list was already put into map and do not replace it.

Related

How to use an Object array as a key in hashmap

I have an object array called Position[] that returns an array of Position objects. I would like to use this as my key for a hashmap for the following: HashMap<Position[],Double> h = new HashMap<>();
I understand that arrays have different hashcodes even if the elements are the same. So I went ahead and tried to override the equals and hashcode. This was my attempt:
public class Key {
private Position p1;
private Position p2;
public Key(Position p1, Position p2){
this.p1 = p1;
this.p2 = p2
}
#Override
public boolean equals(Object object) {
if (!(object instanceof Key)) {
return false;
}
Key newKey = (Key) object;
return this.hashCode()== newKey.hashCode(); //bit of a hack way
}
#Override
public int hashCode(){
int result = 17;
result = 31 * result + this.p1.hashCode();
result = 31 * result + this.p2.hashCode();
return result;
}
}
So I had to change my map to HashMap<Key,Double> However, when ever i go to get the value using the key is still returns null.
An example of what can be passed into they constructor of Key are G2 G4 or E4 E6 ETC.
How would I go about achieving this so that the comparisons actually work?
Thanks.
You can create a special wrapper object to use Position[] as a key in a Map, using Arrays.deepEquals(Object[], Object[]) and Arrays.deepHashCode(Object[]) in the implementations of equals(Object) and hashCode().
import java.util.Arrays;
public final class PositionArrayKey {
private final Position[] array;
public PositionArrayKey(Position[] array) {
this.array = array;
}
#Override
public boolean equals(Object object) {
if (object == this) return true;
if (!(object instanceof PositionArrayKey)) return false;
return Arrays.deepEquals(this.array, ((PositionArrayKey) object).array);
}
#Override
public int hashCode() {
return Arrays.deepHashCode(this.array);
}
}
This enables storing Position[] array instances as keys in map, when wrapped. E.g.
Map<PositionArrayKey, Object> map = new HashMap<>();
map.put(new PositionArrayKey(new Position[]{...}), ...);
Object value = map.get(new PositionArrayKey(new Position[]{...}), ...);
(assuming that both of the Position[] arrays are deeply equal in this example)
Note that for large arrays, performance for invoking equals(Object) and hashCode() may be slow. You can modify the above snippet to cache the result Arrays.deepHashCode(this.array) for larger arrays, if you find it necessary.

Comparing multiple fields of ArrayList using Lambda API and collecting the objects

I am facing a situation similar to described below in my project, of which I am unable to implement the code.
I have a POJO Class
public class TranObject {
public String loadId;
public String vDate;
public String dDate;
public String pDate;
public TranObject(String loadId, String vDate, String dDate, String pDate) {
super();
this.loadId = loadId;
this.vDate = vDate;
this.dDate = dDate;
this.pDate = pDate;
}
//Getter and Setters
//toString()
}
Now I have another processor class where I want to implement some comparison between tranload objects that I am receiving through a data service call and collect them into another collection.
The implementation logic is given in the comments below. Please read the below comments
import java.util.Arrays;
import java.util.List;
public class DemoClass {
public static void main(String[] args) {
List<TranObject> listObj = Arrays.asList(
new TranObject("LOAD1", "20180102", "20180202", null),
new TranObject("LOAD2", "20180402", "20180403", null),
new TranObject("LOAD3", "20180102", "20180202", "20190302"),
new TranObject("LOAD4", "20180402", "20180403", null),
new TranObject("LOAD5", "20200202", "20200203", null)
);
/*
IF (obj1, obj3 vdate and dDate are equal)
IF(pDate == null for obj1 or obj3)
THEN obj1 and obj3 are equal/duplicates, and we collect them.
ELSE IF(pDate != null for obj1 and obj3)
IF(pDate is same for obj1 and obj3)
THEN obj1 and obj3 are duplicates, and we collect them.
ELSE
THEN obj1 and obj3 are unique.
*/
}
}
My End result should be a collection like List containing duplicate Tran objects for further update.
I searched internet in order to how to solve it using Lambda API.
-> Tried using groupingBy first with vDate and then dDate, but then I could not compare them for pDate equality.
Can anyone help me solve this issue. A little help will be very helpful for me. I am stuck here
UPDATE:
After some reading I am trying to implement the same by over-riding equals method in POJO class as shown below:
#Override
public boolean equals(Object obj) {
boolean isEqual=false;
if(obj!=null) {
TranObject tran = (TranObject) obj;
isEqual=(this.vDate.equals(tran.getvDate()) && this.dDate.equals(tran.getdDate()));
if(isEqual && this.pDate != null && tran.getpDate()!= null) {
isEqual = (this.pDate.equals(tran.getpDate()));
}
}
return isEqual;
}
Still it's not working as expected... Can anyone please help me why??
The closest to your requirement would be grouping in a nested manner and then filtering the inner Map for varied conditions while being interested only in values eventually.
Stream<Map<String, List<TranObject>>> groupedNestedStream = listObj.stream()
.collect(Collectors.groupingBy(a -> Arrays.asList(a.vDate, a.dDate)
, Collectors.groupingBy(t -> t.pDate == null ? "default" : t.pDate)))
.values().stream();
from these groupings further the conditions for the values (from map) to be eligible are
they all have same pDate in this case the innerMap would have just one entry with the common pDate (m.size() == 1)
one of the values after grouping has exactly one pDate as null (meaning m.containsKey("default") && m.get("default").size() == 1)
List<TranObject> tranObjects = groupedNestedStream
.filter(m -> m.size() == 1 || (m.containsKey("default") && m.get("default").size() == 1))
.flatMap(m -> m.values().stream().flatMap(List::stream))
.collect(Collectors.toList());
Note, the use of "default" string constant to avoid failures(or poor practice) in collecting a Map with null keys or values.
Sounds like TranObject needs an equals and hashCode method.
#Override
public boolean equals(Object obj) {
//check instanceof and self comparison
TranObject other = (TranObject) obj;
if(this.vDate.equals(other.vDate) && this.dDate.equals(other.dDate)) {
//if pDate is not given then consider them duplicate
if(this.pDate == null || other.pDate == null)
return true;
//if pDate are the same then they are duplicate, otherwise they are unique
return this.pDate.equals(other.pDate);
}
return false;
}
//auto generated by Eclipse
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dDate == null) ? 0 : dDate.hashCode());
result = prime * result + ((pDate == null) ? 0 : pDate.hashCode());
result = prime * result + ((vDate == null) ? 0 : vDate.hashCode());
return result;
}
Now that you have an equals method to determine if two TranObjects are considered equal (based on the rules you specified), just collect the elements that occur in the list more than once:
private static List<TranObject> collectDuplicates(List<TranObject> list) {
List<TranObject> result = new ArrayList<TranObject>();
for(TranObject element : list) {
if(Collections.frequency(list, element) > 1)
result.add(element);
}
return result;
}
This will return all elements that have a duplicate.
Note: collectDuplicates does not return a unique list of the elements that are duplicated. Instead, it returns a list of each duplicated element (as required by OP's question).

Checking if an ArrayList contains a certain String while being case insensitive

How can i search through an ArrayList using the .contains method while being case insensitive? I've tried .containsIgnoreCase but found out that the IgnoreCase method only works for Strings.
Here's the method I'm trying to create:
private ArrayList<String> Ord = new ArrayList<String>();
public void leggTilOrd(String ord){
if (!Ord.contains(ord)){
Ord.add(ord);
}
}
You will need to iterate over the list and check each element. This is what is happening in the contains method. Since you are wanting to use the equalsIgnoreCase method instead of the equals method for the String elements you are checking, you will need to do it explicitly. That can either be with a for-each loop or with a Stream (example below is with a Stream).
private final List<String> list = new ArrayList<>();
public void addIfNotPresent(String str) {
if (list.stream().noneMatch(s -> s.equalsIgnoreCase(str))) {
list.add(str);
}
}
If you are using Java7, simply override the contains() method,
public class CastInsensitiveList extends ArrayList<String> {
#Override
public boolean contains(Object obj) {
String object = (String)obj;
for (String string : this) {
if (object.equalsIgnoreCase(string)) {
return true;
}
}
return false;
}
}
If you are using Java 8.0, using streaming API,
List<String> arrayList = new ArrayList<>();
arrayList.stream().anyMatch(string::equalsIgnoreCase);
The List#Ccontains() method check if the parameter is present in the list but no changes are made in the list elements
use streams instead
public void leggTilOrd(String ordParameter) {
final List<String> ord = Arrays.asList(new String[]{ "a", "A", "b" });
final boolean ordExists = ord.stream().anyMatch(t -> t.equalsIgnoreCase(ordParameter));
System.out.println(ordExists);
}
If you want to avoid duplicates use HashSet instead of List. Hashing works faster while searching. In the underlying class override the equals and hashcode method to return expected results using String.toUpperCase(). If you have a List of String, you can create a string wrapper class.
String Wrapper could look like this:-
public class CaseInsensitiveString {
String string;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((string == null) ? 0 : string.toUpperCase().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;
CaseInsensitiveString other = (CaseInsensitiveString) obj;
if (string == null) {
if (other.string != null)
return false;
} else if (!string.toUpperCase().equals(other.string.toUpperCase()))
return false;
return true;
}
// Other Methods to access string
}

Java alternatives to PHP indexed arrays

I'm trying to iterate over an associative array and tally up how many instances of each combination there are (for use in determining conditional probability of A given B)
For example, in PHP I can iterate over the indexed array $Data[i] given input (A, ~B) and get a result of 2.
$Data[0] = array("A", "~B");
$Data[1] = array("~A", "B");
$Data[2] = array("A", "~B");
$Data[3] = array("A", "B");
I tried replicating this in Java with maps, but maps only allow a unique key for each value... So the following wouldn't work because key A is being used for three entries.
map.put("A", "~B");
map.put("~A", "B");
map.put("A", "~B");
map.put("A", "B");
Is there something else I can use?
Thanks!
You can use a Map<T,List<U>> (in your case it is Map<String,List<String>>) or you can use a Multimap<String,String> using some library such as guava (or apache commons version of it - MultiMap)
If iteration of the structure is your primary goal, a List<ConditionResult> would seem to be the most appropriate choice for your situation, where ConditionResult is given below.
If maintaining a count of the combinations is the sole goal, then a Map<ConditionResult,Integer> would also work well.
public class ConditionResult
{
// Assuming strings for the data types,
// but an enum might be more appropriate.
private String condition;
private String result;
public ConditionResult(String condition, String result)
{
this.condition = condition;
this.result = result;
}
public String getCondition() { return condition; }
public String getResult() { return result; }
public boolean equals(Object object)
{
if (this == object) return true;
if (object == null) return false;
if (getClass() != object.getClass()) return false;
ConditionResult other = (ConditionResult) object;
if (condition == null)
{
if (other.condition != null) return false;
} else if (!condition.equals(other.condition)) return false;
if (result == null)
{
if (other.result != null) return false;
} else if (!result.equals(other.result)) return false;
return true;
}
// Need to implement hashCode as well, for equals consistency...
}
Iteration and counting could be done as:
/**
* Count the instances of condition to result in the supplied results list
*/
public int countInstances(List<ConditionResult> results, String condition, String result)
{
int count = 0;
ConditionResult match = new ConditionResult(condition,result);
for (ConditionResult result : results)
{
if (match.equals(result)) count++;
}
return count;
}

Remove value from List of Map in java

Remove value from list in java containing map
I am having list in this form:
List: [{id=1,c_id=3,value=5},{id=1,c_id=2,value=5},{id=1,c_id=3,value=5},{id=2,c_id=1,value=5},
{id=2,c_id=null,value=5}}];
In result I am trying to get
List: [{id=1,c_id=3,value=5},,{id=1,c_id=2,value=5},{id=2,c_id=1,value=5}}]
For same id if c_id is same it should be removed and also if c_id is null it should be removed.
You can do
for(Iterator<Map<String, Object>> iter = list.iterator(); iter.hasNext(); ) {
Map<String, Object> map = iter.next();
Object c_id = map.get("c_id");
if (c_id == null || c_id.equals(matching_c_id))
iter.remove();
}
Guava solution:
Iterables.removeIf(list, new Predicate<Element>(){
#Override public boolean apply(Element e) {
return e.c_id == null || e.c_id.equals(matching_c_id);
}});
I think a solution could be the following one and I think it is readable.
First, construct a set element proxy for your object type:
public class MyTypeProxy {
private MyType obj;
public MyTypeProxy(MyType obj) {
this.obj = obj;
}
public MyType getObj() { return this.obj; }
public int hashcode() {
return obj.getC_id() ^ obj.getId();
}
public boolean equals(Object ref) {
if (ref.getClass() != MyTypeProxy.class) {
return false;
}
return
((MyTypeProxy)ref).obj.getC_id() == this.obj.getC_id() &&
((MyTypeProxy)ref).obj.getId() == this.obj.getC_id();
}
}
Then, in your code, you can construct a HashSet.
HashSet<MyTypeProxy> set = new HashSet<MyTypeProxy>();
for (MyType mt : list) {
if (mt.getC_id() != null){
set.add(new MyTypeProxy(myList));
}
}
The following does two things. First, it excludes objects with a null c_id field and second, it filters out objects with the same id and c_id combination. You might want to consider if keeping the first one is ok or not (that is what the above code does).
The set now contains exactly what you need.

Categories

Resources