How to check uniqueness of particular elements of an ArrayList - java

I have a class called LineUp, it is an ArrayList of a class called Event. An Event has three values a String Act, a Venue (it's own class), and an int Session.
An Event might be declared like this.
Event e1 = new Event("Foo Fighters", northstage, "1")
LineUp is an ArrayList, Event being elements like e1.
In my LineUp class I have to make an invariant that checks that every Event contained within the ArrayList lineup has a unique Venue and Session. Because this assignment requires that I follow specification exactly, it is irrelevant whether the combination of Act, Venue and Session is unique, to follow specification I must /only/ ensure that Venue and Session are unique.
How do I check for duplicates but only of specific values within an ArrayList?
Thank-you.

If you only need to check if there are duplicates (considering venue-session pairs), you could create a helper Pair class with only the attributes that matter in this specific case. Then map the events to Pair objects, remove the duplicates and check if the size is the same.
You could, for example, create a nested class inside LineUp:
class LineUp {
private List<Event> events = new ArrayList<>();
private static final class Pair<U, V> {
final U first;
final V second;
Pair(U first, V second) {
this.first = first;
this.second = second;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Pair)) {
return false;
}
Pair<U, V> that = (Pair<U, V>) o;
return Objects.equals(this.first, that.first)
&& Objects.equals(this.second, that.second);
}
#Override
public int hashCode() {
return Objects.hash(this.first, this.second);
}
}
// rest of the LineUp class
}
Then create a method that return false if there are any duplicates:
public boolean duplicateVenueSessions() {
// Map each Event to a Pair<Venue, Integer> and remove the duplicates
long numDistinct = this.events.stream()
.map(e -> new Pair<>(e.venue, e.session))
.distinct()
.count();
// return false if the original number of events is different from the
// number of distinct events considering only venue and session values
return this.events.size() != numDistinct;
}
If can't use Java 8, you could use a Set instead:
public boolean duplicateVenueSessions() {
Set<Pair<String, Integer>> distinct = new HashSet<>();
for (Event e : this.events) {
Pair<String, Integer> venueSession = new Pair<>(e.venue, e.session);
if (distinct.contains(venueSession)) {
return true;
}
distinct.add(venueSession);
}
return false;
}

If I understand everything correctly you can use Map in a method to store values
Map<Map<Venue, Integer>, Act> lineup = new HashMap<>();
it incorporated uniqueness of Venue-Session pair.
However as Venue is your own class, you will have to implement equals() and hashCode() methods for Venue in order for this solution to work
EDIT:
what I meant wa something like this:
Map<Map<Integer, Venue>,String> uniqueMap = new HashMap<>();
for (Event event: events) { // assuming events is ArrayList
Map<Integer, Venue> sessionVenueMap = new HashMap<>();
sessionVenueMap.put(event.getSession(), event.getVenue());
//check if we stored this pair in our cool map
if (uniqueMap.get(sessionVenueMap) == null) {
//if not
//store this in our uniqieMap in our method
uniqueMap.put(sessionVenueMap, event.getAct);
sessionVenueMap.put(event.getSession(), event.getVenue);
} else {
// if map has this pair
// then it is not unique
return false;
}
venueSessionMap.put(.getVenue(); event.getSession();
}
return true;
code is not tested though, but you get the general idea, although it seems quite complex. probably there is a better solution

Related

Find if string / object is present in array of objects

I have an object list that retrieves multiple values from a database, like so:
List<Object> listRequest = daoManager.getLaptopsForRequest(BigInteger.valueOf(itemTransItem.getAssetType().getId()), BigInteger.valueOf(approverId));
The result of which looks like this (printed out on the console, via for each):
{asset_type_id=1, inventory_id=1, from_dt=2015-09-18 18:04:55.77, id=1, asset_id=1, status=1, thru_dt=null}
{asset_type_id=1, inventory_id=1, from_dt=2015-09-18 18:04:55.77, id=2, asset_id=2, status=1, thru_dt=null}
{asset_type_id=1, inventory_id=1, from_dt=2015-09-18 18:04:55.77, id=3, asset_id=3, status=1, thru_dt=null}
What's the quickest and/or most efficient way to get only the object where asset_id = 2, or an array of asset_id (1 and 2), and putting the results in another array?
I contemplated casting each object as a string, and then turning each string into an array (split by the comma), and then turning each item of the array into a further array (or a hashmap) by using the =, but that seems like a long, long, complex way of nested for loops that might fail (see comparing array of assets).
Perhaps there's another quicker / less complex way to do this that I'm missing? Any suggestions? Thanks.
EDIT: For reference, here's the getLaptopsForRequest function:
public List getLaptopsForRequest(BigInteger asset_type_id, BigInteger party_id){
SQLQuery query = sessionFactory.getCurrentSession().createSQLQuery(laptopsForRequestSql);
query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
List forRequest = query.setBigInteger(0, asset_type_id).setBigInteger(1, party_id).list();
return forRequest;
}
It returns a list of the results of the query. As this code has been in place, I'm not allowed to edit it.
A quick and dirty solution would be to match each item against regex ^.*asset_id=([0-9]+).*$.
If what you're getting from that method is indeed a list of Strings containing those JSONs, you could create a model class and use a JSON serializer like GSON or Jackson to read the strings into Java objects, and then you could work with them.
What you are trying to do basically is to filter a list of objects. You could implement the Filter Pattern writing your own Iterator for the list.
Just extends this class to implement your own filter.
public abstract class Filter<T> {
public abstract boolean passes(T object);
public Iterator<T> filter(Iterator<T> iterator) {
return new FilterIterator(iterator);
}
public Iterable<T> filter(Iterable<T> iterable) {
return new Iterable<T>() {
public Iterator<T> iterator() {
return filter(iterable.iterator());
}
};
}
private class FilterIterator implements Iterator<T> {
private Iterator<T> iterator;
private T next;
private FilterIterator(Iterator<T> iterator) {
this.iterator = iterator;
toNext();
}
public boolean hasNext() {
return next != null;
}
public T next() {
if (next == null)
throw new NoSuchElementException();
T returnValue = next;
toNext();
return returnValue;
}
public void remove() {
throw new UnsupportedOperationException();
}
private void toNext() {
next = null;
while (iterator.hasNext()) {
T item = iterator.next();
if (item != null && passes(item)) {
next = item;
break;
}
}
}
}
}
and then use it in this way:
List<MyObject> newList = new ArrayList<MyObject>();
for(MyObject obj : filter.filter(listObjs) ){
newList.add(obj);
}
Assuming your objects have getter et setter methods.
Only the object where asset_id = "2", asset_id here being a string
listRequest.stream().filter(e -> e.getAssetId() == "2" ).toArray();

Ordered insertion in linkedHashSet, any performant way ?

So I have a LinkedHashSet , with values say a1, a2, , b, c1, c2
I want to replace, b with x , such that the order of x should be same as order of b.
One obvious way would be
private LinkedHashSet<String> orderedSubstitution(final Set<String> originalOrderedSet, final String oldItem,
final String newItem) {
final LinkedHashSet<String> newOrderedSet = new LinkedHashSet<String>();
// Things we do to maintain order in a linkedHashSet
for (final String stringItem : originalOrderedSet) {
if (stringItem.equals(oldItem)) {
newOrderedSet.add(newItem);
} else {
newOrderedSet.add(stringItem);
}
}
return newOrderedSet;
}
not only this is O(n) i also feel this is not the fastest way. Any better solution ?
NOTE : I HAVE TO use linkedHashMap.
One way to do it would be to use a subclass of LinkedHashSet that has the replacement built in, e.g.:
public class ReplacingLinkedHashSet extends LinkedHashSet<String> {
private final String what;
private final String with;
public ReplacingLinkedHashSet(String what, String with) {
this.what = what;
this.with = with;
}
#Override
public Iterator<String> iterator() {
final Iterator<String> iterator = super.iterator();
return new Iterator<String>() {
#Override
public boolean hasNext() {
return iterator.hasNext();
}
#Override
public String next() {
String next = iterator.next();
return what.equals(next) ? with : next;
}
#Override
public void remove() {
iterator.remove();
}
};
}
}
But that means the replacement would have to be known before you fill the Set.
(Of course you could easily turn this <String> version into a generic one.
Responding to comments:
OK, then there is no way to solve it without a full iteration. You could however just leave the LinkedHashSet untouched and decorate the iterator when retrieving the values.
Create a structure Map
Insert all the string with < String, OrderOfTheString>
Do the insertion of the new String by adding a small Delta after the current string's OrderOfTheString.
Convert Map to LikedHashSet
I know it is complicated but it is definately better when we have linked hash Map of ~1000000 elements and there are about 1000 elements to be inserted.

Is there a Java Class similar to ArrayList that can do this?

I have been running into this problem sometimes when programming.
Imagine I have a table of data with two columns. The first column has strings, the second column has integers.
I want to be able to store each row of the table into a dynamic array. So each element of the array needs to hold a string and an integer.
Previously, I have been accomplishing this by just splitting each column of the table into two separate ArrayLists and then when I want to add a row, I would call the add() method once on each ArrayList. To remove, I would call the remove(index) method once on each ArrayList at the same index.
But isn't there a better way? I know there are classes like HashMap but they don't allow duplicate keys. I am looking for something that allows duplicate entries.
I know that it's possible to do something like this:
ArrayList<Object[]> myArray = new ArrayList<Object[]>();
myArray.add(new Object[]{"string", 123});
I don't really want to have to cast into String and Integer every time I get an element out of the array but maybe this is the only way without creating my own? This looks more confusing to me and I'd prefer using two ArrayLists.
So is there any Java object like ArrayList where it would work like this:
ArrayList<String, Integer> myArray = new ArrayList<String, Integer>();
myArray.add("string", 123);
Just create simple POJO class to hold row data. Don't forget about equals and hashCode and prefer immutable solution (without setters):
public class Pair {
private String key;
private Integer value;
public Pair(String key, Integer value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public Integer getValue() {
return value;
}
// autogenerated
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Pair)) return false;
Pair pair = (Pair) o;
if (key != null ? !key.equals(pair.key) : pair.key != null) return false;
if (value != null ? !value.equals(pair.value) : pair.value != null) return false;
return true;
}
#Override
public int hashCode() {
int result = key != null ? key.hashCode() : 0;
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
}
Usage:
List<Pair> list = new ArrayList<Pair>();
list.add(new Pair("string", 123));
Note: in other languages there are build-in solutions for it like case-classes and tuples in Scala.
Create a Row class that holds the data.
package com.stackoverflow;
import java.util.ArrayList;
import java.util.List;
/**
* #author maba, 2012-10-10
*/
public class Row {
private int intValue;
private String stringValue;
public Row(String stringValue, int intValue) {
this.intValue = intValue;
this.stringValue = stringValue;
}
public int getIntValue() {
return intValue;
}
public String getStringValue() {
return stringValue;
}
public static void main(String[] args) {
List<Row> rows = new ArrayList<Row>();
rows.add(new Row("string", 123));
}
}
You can create very simple object, like :
public class Row{
private String strVal;
private Integer intVal;
public Row(String s, Integer i){
strVal = s;
intVal = i;
}
//getters and setters
}
Then use it as follows :
ArrayList<Row> myArray = new ArrayList<Row>();
myArray.add(new Row("string", 123));
Map is the option if you are sure that any one value among integer or string is unique. Then you can put that unique value as a key. If it is not true for your case, creating a simple POJO is best option for you. Infact, if in future, there a chance to come more values (columns) per row then also using a POJO will be less time consuming. You can define POJO like;
public class Data {
private int intValue;
private String strValue;
public int getIntValue() {
return intValue;
}
public void setIntValue(int newInt) {
this.intValue = newInt;
}
public String getStrValue() {
return strValue;
}
public void setStrValue(String newStr) {
this.strValue = newStr;
}
And in the class you can use it like;
ArrayList<Data> dataList = new ArrayList<Data>();
Data data = new Data();
data.setIntValue(123);
data.setStrValue("string");
dataList.add(data);
You should create a class (e.g. Foo) that contains an int and a String.
Then you can create an ArrayList of Foo objects.
List<Foo> fooList = new ArrayList<Foo>();
This is called a map my friend. It is similar to a dictionary in .net
http://docs.oracle.com/javase/6/docs/api/java/util/Map.html
HashMap my be the class you are looking for assuming "string" going to different for different values. Here is documentation on HashMap
Example:
HashMap<String, Integer> tempMap = new HashMap<String, Integer>();
tempMap.put("string", 124);
If you need to add more than one value, you may create HashMap<String, ArrayList> like that.
you can use google collection library Guava there is a Map called Multimap. It is collection similar to a Map, but which may associate multiple values with a single key. If you call put(K, V) twice, with the same key but different values, the multimap contains mappings from the key to both values.
Use Map to solve this problem:
Map<String, Integer> map = new HashMap<String, Integer>();
Eg:
map.put("string", 123);

How to remove duplicates from a list based on a custom java object not a primitive type?

Before I post this question, I found somehow similar question posted here. But the answer was based on a String. However, I have a different situation here. I am not trying to remove String but another object called AwardYearSource. This class has an int attribute called year. So I want to remove duplicates based on the year. i.e if there is year 2010 mentioned more than once, I want to remove that AwardYearSource object. How can I do that?
The simplest way to remove elements based on a field is as follows (preserving order):
Map<Integer, AwardYearSource> map = new LinkedHashMap<>();
for (AwardYearSource ays : list) {
map.put(ays.getYear(), ays);
}
list.clear();
list.addAll(map.values());
Another way would be to override hashCode() and equals(Object obj) for your object. Since it just has one field you want to use to determine equality, this is pretty straightforward. Something like:
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof AwardYearSource)) {
return false;
}
return (this.year == ((AwardYearSource)obj).year);
}
public int hashCode() {
return this.year;
}
Then you can just stick all of the objects into a Set to remove duplicates:
Set<AwardYearSource> set = new Set<AwardYearSource>();
set.add(new AwardYearSource(2011));
set.add(new AwardYearSource(2012));
set.add(new AwardYearSource(2011));
for (AwardYearSource aws : set) {
System.out.println(aws.year);
}
Fairly simply. Although something bugs me about the map versions (not that I doubt they'd work, it just seems like overkill, somehow - although this version isn't necessarily any better in that regard).
Answer is functional, and threadsafe (assuming AwardYearSource is immutable).
public static List<AwardYearSource> removeDuplicateYears(
final Collection<AwardYearSource> awards) {
final ArrayList<AwardYearSource> input = new ArrayList<AwardYearSource>(awards);
// If there's only one element (or none), guaranteed unique.
if (input.size() <= 1) {
return input;
}
final HashSet<Integer> years = new HashSet<Integer>(input.size(), 1);
final Iterator<AwardYearSource> iter = input.iterator();
while(iter.hasNext()) {
final AwardYearSource award = iter.next();
final Integer year = award.getYear();
if (years.contains(year)) {
iter.remove();
} else {
years.add(year);
}
}
return input;
}
You could use a map and store your objects with the year as a key:
Map<Integer, AwardYearSource> map = new HashMap<Integer, AwardYearSource>();
map.put(someAwardYearSource1.getYear(), someAwardYearSource1);
map.put(someAwardYearSource2.getYear(), someAwardYearSource2);
etc.
At the end the map will contain unique values by year, which you can call with the values method:
Collection<AwardYearSource> noDups = map.values();
Create a HashMap object with int as the key type and your class as the value type. Then iterate over the list and insert each element to the map using:
mymap.put(source.year, source);
Then remove all elements from the origianl list and iterate over the map and insert each element to the list.
If your AwardYearSource class overrides equals and hashcode methods (Eclipse can generate both), then you can add them to a Set. The Set will not contain any duplicates.
public class AwardYearSource
{
private final int year;
public AwardYearSource(int year)
{
this.year = year;
}
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + year;
return result;
}
#Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AwardYearSource other = (AwardYearSource) obj;
if (year != other.year)
return false;
return true;
}
#Override
public String toString()
{
return String.valueOf(year);
}
public static void main(String[] args)
{
Set<AwardYearSource> set = new HashSet<AwardYearSource>();
set.add(new AwardYearSource(2000));
set.add(new AwardYearSource(2000));
set.add(new AwardYearSource(2000));
set.add(new AwardYearSource(2000));
System.out.println(set);
}
}
The output is [2000]. Only one item in the set.
Set<Integer> set = new HashSet<>();
list.removeIf(i -> set.contains(i.getYear()) ? true : !set.add(i.getYear()));
This should help wherein, duplication is decided based on certain property (or combination of properties), year in this case. Hope this helps.

Grouping objects by two fields in java

I have bunch of log files and I want to process them in java, but I want to sort them first so I can have more human readable results.
My Log Class :
public class Log{
//only relevant fields here
private String countryCode;
private AccessType accessType;
...etc..
}
AccessType is Enum, which has values WEB, API, OTHER.
I'd like to group Log objects by both countryCode and accessType, so that end product would be log list.
I got this working for grouping Logs into log list by countryCode like this :
public List<Log> groupByCountryCode(String countryCode) {
Map<String, List<Log>> map = new HashMap<String, List<Log>>();
for (Log log : logList) {
String key = log.getCountryCode();
if (map.get(key) == null) {
map.put(key, new ArrayList<Log>());
}
map.get(key).add(log);
}
List<Log> sortedByCountryCodeLogList = map.get(countryCode);
return sortedByCountryCodeLogList;
}
from this #Kaleb Brasee example :
Group by field name in Java
Here is what I've been trying for some time now, and really stuck now ..
public List<Log> groupByCountryCode(String countryCode) {
Map<String, Map<AccessType, List<Log>>> map = new HashMap<String, Map<AccessType, List<Log>>>();
AccessType mapKey = null;
List<Log> innerList = null;
Map<AccessType, List<Log>> innerMap = null;
// inner sort
for (Log log : logList) {
String key = log.getCountryCode();
if (map.get(key) == null) {
map.put(key, new HashMap<AccessType, List<Log>>());
innerMap = new HashMap<AccessType, List<Log>>();
}
AccessType innerMapKey = log.getAccessType();
mapKey = innerMapKey;
if (innerMap.get(innerMapKey) == null) {
innerMap.put(innerMapKey, new ArrayList<Log>());
innerList = new ArrayList<Log>();
}
innerList.add(log);
innerMap.put(innerMapKey, innerList);
map.put(key, innerMap);
map.get(key).get(log.getAccessType()).add(log);
}
List<Log> sortedByCountryCodeLogList = map.get(countryCode).get(mapKey);
return sortedByCountryCodeLogList;
}
I'm not sure I know what I'm doing anymore
Your question is confusing. You want to sort the list, but you are creating many new lists, then discarding all but one of them?
Here is a method to sort the list. Note that Collections.sort() uses a stable sort. (This means that the original order of items within a group of country code and access type is preserved.)
class MyComparator implements Comparator<Log> {
public int compare(Log a, Log b) {
if (a.getCountryCode().equals(b.getCountryCode()) {
/* Country code is the same; compare by access type. */
return a.getAccessType().ordinal() - b.getAccessType().ordinal();
} else
return a.getCountryCode().compareTo(b.getCountryCode());
}
}
Collections.sort(logList, new MyComparator());
If you really want to do what your code is currently doing, at least skip the creation of unnecessary lists:
public List<Log> getCountryAndAccess(String cc, AccessType access) {
List<Log> sublist = new ArrayList<Log>();
for (Log log : logList)
if (cc.equals(log.getCountryCode()) && (log.getAccessType() == access))
sublist.add(log);
return sublist;
}
If you're able to use it, Google's Guava library has an Ordering class that might be able to help simplify things. Something like this might work:
Ordering<Log> byCountryCode = new Ordering<Log>() {
#Override
public int compare(Log left, Log right) {
return left.getCountryCode().compareTo(right.getCountryCode());
}
};
Ordering<Log> byAccessType = new Ordering<Log>() {
#Override
public int compare(Log left, Log right) {
return left.getAccessType().compareTo(right.getAccessType());
}
};
Collections.sort(logList, byCountryCode.compound(byAccessType));
You should create the new inner map first, then add it to the outer map:
if (map.get(key) == null) {
innerMap = new HashMap<AccessType, List<Log>>();
map.put(key, innerMap);
}
and similarly for the list element. This avoids creating unnecessary map elements which will then be overwritten later.
Overall, the simplest is to use the same logic as in your first method, i.e. if the element is not present in the map, insert it, then just get it from the map:
for (Log log : logList) {
String key = log.getCountryCode();
if (map.get(key) == null) {
map.put(key, new HashMap<AccessType, List<Log>>());
}
innerMap = map.get(key);
AccessType innerMapKey = log.getAccessType();
if (innerMap.get(innerMapKey) == null) {
innerMap.put(innerMapKey, new ArrayList<Log>());
}
innerMap.get(innerMapKey).add(log);
}
Firstly, it looks like you're adding each log entry twice with the final line map.get(key).get(log.getAccessType()).add(log); inside your for loop. I think you can do without that, given the code above it.
After fixing that, to return your List<Log> you can do:
List<Log> sortedByCountryCodeLogList = new ArrayList<Log>();
for (List<Log> nextLogs : map.get(countryCode).values()) {
sortedByCountryCodeLogList.addAll(nextLogs);
}
I think that code above should flatten it down into one list, still grouped by country code and access type (not in insertion order though, since you used HashMap and not LinkedHashMap), which I think is what you want.

Categories