Optimal way of pairing up string values java - java

I want some way to pair String values and pass it on as a data structure. Any recommendations? Would a Map work? The issue that I'm having with a Map is that not all strings will be paired in this context, only a few of them. I need to have all the strings and if there exists one, it's string pair as well. If anything lacks clarity, let me know.

A Map can be used, with a special value for string without pair.

Well, this is probably not the best way, but what I sometimes use is a duplex type structure that is similar to Python's tuple.
I build a generic structure like so:
class Duplex<T,T> {
private T item1, item2;
public Duplex(T one){
item1 = one;
}
public Duplex(T one, T two){
item1 = one;
item2 = two;
}
//Getters + Accessors
}
Doesn't have to be generic, but it lets you reuse it for other situations.
I haven't used maps before though, so those might actually be better.

A map will give you the key value pairing that you want. What you may want is something like this:
class MyPair
{
String first;
String second;
// Equals and Hashcode
}
Now it is up to you to decide the inner parameters of your MyPair class.
Map pairings = new HashMap();

Since most of your elements will be one String I suggest turning your pairs into Strings as well.
public static final String SEP = "\uFFFF"; // not a valid character by definition.
public static String pair(String a, String b) {
return a + SEP + b;
}
public static String[] split(String str) {
return str.split(SEP);
}

One straightforward way is to use arrays.
List<String[]> pairs = new ArrayList<String[]>();
...
pairs.add(new String[]{"first", "second"});
pairs.add(new String[]{"pairless"});
Or use whatever container you want, List here is just an example, point is String[].
Note: Using plain array is sort of "quick and dirty", it's more "proper" to create a custom "Pair" class. Especially if you have methods which operate on sigle pair, it makes sense to create a class to contain them. In that class you can still internally use this kind of array to store 1 or 2 (or some other number of) items.

Related

2D Array List of different types in Java

So this is something that has me a little stumped. I'm trying to make an array list that holds objects, and each object will have a string associated with it.
For an example lets say I have this array list of adjacent rooms...
ArrayList<Object> adjacentRooms = new ArrayList<Object>();
I could add Room objects to that array list that are adjacent to whichever subject.. however, when I add to the array list with adjacentRooms.add(x); (where x could be an object Room type).. I would also like to add a string to that position. For example adjacentRooms.add(x, "north");.. <- now I know that that is not possible unless I do something like a 2D array list possibly?
So after some time researching I am at a loss. I just can't quite figure out how to add an object with an associated string in a 2D array list...
Instead of a List use a Map: That can "map" a value to another, and store it in a collection.
Something like this:
Map<String, Room> adjacentRooms = new HashmMap<>();
adjacentRooms.put("north", room);
adjacentRooms.get("east");
You may want to use constants, to make sure the values are "discrete".
It has a drawback, tho: it cannot assign more than 1 value to a key, that is more than 1 Rooms to a direction...
An ArrayList can only hold one data type. But I'm curious as to why you cant associate a string as a member in the Object you're talking about. Since you want a 2d arraylist, I'm assuming the string and "room" are related
Object foo = new Object();
foo.data = "your string"
adjacentRooms.add(foo);
access by
adjacentRooms.get(index).data
However, if you must, you can do a 2d ArrayList, but they get annoying
ArrayList<ArrayList<String> > list = new ArrayList();
access would be something like list.get(i).get(k) with 'i' referring to the index of ArrayList of Strings, and k referring to the index of a String in that 'i' ArrayList.
However, that structure does not store the "Object" you're talking about...
Hope that helps.
The "array" in ArrayList describes the way the list is implemented.
A List is always one-dimensional. When you need more dimensions, use objects in your list that can store additional information.
So either create a new class that holds your data (e.g. DetailedRoom with members Room and a String) or use an existing collection class. The latter would be a poor design, but still... it could be List for instance, so that you end up with List<List<Object>>
.
If you are to create your own class.. this is what I did...
public class AdjacentRoom {
private Room room;
private String direction;
public AdjacentRoom(Room room, String direction) {
this.room = room;
this.direction = direction;
}
public Room getRoom(){
return room;
}
public String getDirection(){
return direction;
}
}
Also for sake of example here is a bare bones room class...
public class Room {
private String name;
public Room(String name){
this.name = name;
}
public String getName(){
return name;
}
}
What this all allows me to do is ...
//Firstly i can create a room...
Room testRoom = new Room("Test Room");
//If i change my array list in the question to an AdjacentRoom list...
ArrayList<AdjacentRoom> adjacentRooms = new ArrayList<AdjacentRoom>();
//I can add rooms and strings to it like this..
adjacentRooms.add(new AdjacentRoom(testRoom, "north"));
Now I can still access each of the rooms methods while also printing each string or 'direction' associated to each room.. For example..
for(AdjacentRoom room : adjacentRooms){
System.out.println(room.getRoom().getName() + " " + room.getDirection());
}
While this is a cool and customization solution, using a map like in Usagi Miyamoto's answer ( link ) is great for this situation.

Sorting LinkedHashMap<String, String[]>.entrySet() by the keys, but in the middle of the keys using a regex

I have a LinkedHashMap which maps strings to string arrays.
The keys have the format of something like this: "xxx (yyy(0.123))"
Basically, I want to be able to sort the entry set in such a way that it sorts it by the decimal part, and not the beginning of the string. What I have done so far is converting the entry set to an ArrayList so that I can try calling Arrays.sort on it, but obviously that's going to just sort by the beginning of the string.
What I'm currently thinking is that I would have to go through this array, convert each key in the pair to a custom class with a comparator that compares the way I want it to (with the regular expression .*\((.*)\)\) to find the decimal). However, that sounds like a bunch of unnecessary overhead, so I was wondering if there was a simpler way. Thanks in advance.
First, you cannot "sort" a LinkedHashMap. LinkedHashMap maintain the iteration order based on the order of insertion.
If you means creating another LinkedHashMap by inserting using values from the original map, with order based on sorted order: You need to be aware of any new entries added after your initial construction will be unsorted. So you may want to create an unmodifiable Map.
For the Comparator implementation, you do not need to make it to your custom class. Just create a comparator that do the comparison is good enough.
Like this:
(haven't compiled, just to show you the idea)
// assume the key is in format of "ABCDE,12345", and you want to sort by the numeric part:
Map<String, Foo> oldMap = ....; // assume you populated something in it
Map<String, Foo> sortedMap
= new TreeMap((a,b) -> {
// here I use split(), you can use regex
int aNum = Integer.valueOf(a.split(",")[1]);
int bNum = Integer.valueOf(b.split(",")[1]);
if (aNum != bNum ) {
return aNum - bNum;
} else {
return a.compareTo(b);
});
sortedMap.addAll(oldMap);
// now sortedMap contains your entries in sorted order.
// you may construct a new LinkedHashMap with it or do whatever you want
Your solution sounds fine.
If you run into performance issues, you could look buffering the decimal value by replacing your strings with an object that contains the string and the decimal value. Then it does not need to be recalculated multiple times during the sort.
There are trade offs for the buffered solution as above and figuring out which technique is optimal will really depend on your entire solution.
Is there a reason you need to use LinkedHashMap? The javadoc specifically states
This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order)
TreeMap seems a better fit for what you're trying to achieve, which allows you to provide a Comparator at construction. Using Java 8, this could be achieved with something like:
private static final String DOUBLE_REGEX = "(?<value>\\d+(?:\\.\\d+)?)";
private static final String FIND_REGEX = "[^\\d]*\\(" + DOUBLE_REGEX + "\\)[^\\d]*";
private static final Pattern FIND_PATTERN = Pattern.compile(FIND_REGEX);
private static final Comparator<String> COMPARATOR = Comparator.comparingDouble(
s -> {
final Matcher matcher = FIND_PATTERN.matcher(s);
if (!matcher.find()) {
throw new IllegalArgumentException("Cannot compare key: " + s);
}
return Double.parseDouble(matcher.group("value"));
});
private final Map<String, List<String>> map = new TreeMap<>(COMPARATOR);
Edit: If it has to be a LinkedHashMap (yours), you can always:
map.putAll(yours);
yours.clear();
yours.putAll(map);

How to sort two arrays with respect to eachother.

I have 2 arrays:
private String[] placeName;
private Double[] miles;
The data in them look like this:
placeName = {"home", "away", "here"};
miles = {111, 11, 3};
The position of the values match to each other. ie home = 111 and away = 11
I need to sort these arrays together so I don't lose how they are matched by the the number- lowest to highest. What is the best way to accomplish this? Do I need to combine the arrays first?
Since the two values are so tightly coupled together I would actually write a custom class to contain the information and then sort those classes instead of playing around with raw arrays. Doing so would leave you open to many possible bugs down the line.
This allows for much better control, data encapsulation and future expansion of what methods or data your class may contain.
public class MyDistance implements Comparable<MyDistance> {
private String placename;
private double mileage;
public MyDistance(String placename, double milage) {
this.placename = placename;
this.milage = milage;
}
public String getPlacename() {
return this.placename;
}
public double getMilage() {
return this.milage;
}
#Override
public int compareTo(MyDistance anotherDistance)
{
return milage.compareTo(anotherDistance.getMilage());
}
}
If you want more flexibility in your sort then instead of having your MyDistance class implement Comparable you can write a custom Comparator<MyDistance> class:
public class DistanceComparator extends Comparator<MyDistance> {
#Override
public int compare(MyDistance dist1, MyDistance dist2) {
return dist1.getMilage().compareTo(dist2.getMilage());
}
}
You can use this Comparator to sort using Collections:
List<MyDistance> distanceList = getDistanceListSomehow();
Collections.sort(distanceList, new DistanceComparator());
You are not restricted to a List, I just used it for explanatory purposes. You should look at the full range of Java Collections types to best choose one that suits your purposes. As a suggestion though, the ArrayList type is easy to use and retains order like you would want.
One way is to create a TreeMap. Assuming you are sorting by miles.
TreeMap tm = new TreeMap<Double, String>();
for (int i=0; i<miles.length; i++) {
tm.put(miles[i], placeName[i]);
}
// tm is already sorted - iterate over it...
NOTE: IF you have places with the same exact distance in miles this will not work. e.g. if you had a "work" that was 11 miles, just like "away", this won't work. You'd probably want some form of MultiMap for that...
Maybe put the arrays in a TreeMap and sort it
SortedMap<Double,String> map = new TreeMap<>();
map.put(111,"home");
map.put(11,"away");
map.put(3,"here");
The elemtns are inserted sorted by their key

Get unique values from ArrayList in Java

I have an ArrayList with a number of records and one column contains gas names as CO2 CH4 SO2, etc. Now I want to retrieve different gas names(unique) only without repeatation from the ArrayList. How can it be done?
You should use a Set. A Set is a Collection that contains no duplicates.
If you have a List that contains duplicates, you can get the unique entries like this:
List<String> gasList = // create list with duplicates...
Set<String> uniqueGas = new HashSet<String>(gasList);
System.out.println("Unique gas count: " + uniqueGas.size());
NOTE: This HashSet constructor identifies duplicates by invoking the elements' equals() methods.
You can use Java 8 Stream API.
Method distinct is an intermediate operation that filters the stream and allows only distinct values (by default using the Object::equals method) to pass to the next operation.
I wrote an example below for your case,
// Create the list with duplicates.
List<String> listAll = Arrays.asList("CO2", "CH4", "SO2", "CO2", "CH4", "SO2", "CO2", "CH4", "SO2");
// Create a list with the distinct elements using stream.
List<String> listDistinct = listAll.stream().distinct().collect(Collectors.toList());
// Display them to terminal using stream::collect with a build in Collector.
String collectAll = listAll.stream().collect(Collectors.joining(", "));
System.out.println(collectAll); //=> CO2, CH4, SO2, CO2, CH4 etc..
String collectDistinct = listDistinct.stream().collect(Collectors.joining(", "));
System.out.println(collectDistinct); //=> CO2, CH4, SO2
I hope I understand your question correctly: assuming that the values are of type String, the most efficient way is probably to convert to a HashSet and iterate over it:
ArrayList<String> values = ... //Your values
HashSet<String> uniqueValues = new HashSet<>(values);
for (String value : uniqueValues) {
... //Do something
}
you can use this for making a list Unique
ArrayList<String> listWithDuplicateValues = new ArrayList<>();
list.add("first");
list.add("first");
list.add("second");
ArrayList uniqueList = (ArrayList) listWithDuplicateValues.stream().distinct().collect(Collectors.toList());
ArrayList values = ... // your values
Set uniqueValues = new HashSet(values); //now unique
Here's straightforward way without resorting to custom comparators or stuff like that:
Set<String> gasNames = new HashSet<String>();
List<YourRecord> records = ...;
for(YourRecord record : records) {
gasNames.add(record.getGasName());
}
// now gasNames is a set of unique gas names, which you could operate on:
List<String> sortedGasses = new ArrayList<String>(gasNames);
Collections.sort(sortedGasses);
Note: Using TreeSet instead of HashSet would give directly sorted arraylist and above Collections.sort could be skipped, but TreeSet is otherwise less efficent, so it's often better, and rarely worse, to use HashSet even when sorting is needed.
When I was doing the same query, I had hard time adjusting the solutions to my case, though all the previous answers have good insights.
Here is a solution when one has to acquire a list of unique objects, NOT strings.
Let's say, one has a list of Record object. Record class has only properties of type String, NO property of type int.
Here implementing hashCode() becomes difficult as hashCode() needs to return an int.
The following is a sample Record Class.
public class Record{
String employeeName;
String employeeGroup;
Record(String name, String group){
employeeName= name;
employeeGroup = group;
}
public String getEmployeeName(){
return employeeName;
}
public String getEmployeeGroup(){
return employeeGroup;
}
#Override
public boolean equals(Object o){
if(o instanceof Record){
if (((Record) o).employeeGroup.equals(employeeGroup) &&
((Record) o).employeeName.equals(employeeName)){
return true;
}
}
return false;
}
#Override
public int hashCode() { //this should return a unique code
int hash = 3; //this could be anything, but I would chose a prime(e.g. 5, 7, 11 )
//again, the multiplier could be anything like 59,79,89, any prime
hash = 89 * hash + Objects.hashCode(this.employeeGroup);
return hash;
}
As suggested earlier by others, the class needs to override both the equals() and the hashCode() method to be able to use HashSet.
Now, let's say, the list of Records is allRecord(List<Record> allRecord).
Set<Record> distinctRecords = new HashSet<>();
for(Record rc: allRecord){
distinctRecords.add(rc);
}
This will only add the distinct Records to the Hashset, distinctRecords.
Hope this helps.
public static List getUniqueValues(List input) {
return new ArrayList<>(new LinkedHashSet<>(incoming));
}
dont forget to implement your equals method first
If you have an array of a some kind of object (bean) you can do this:
List<aBean> gasList = createDuplicateGasBeans();
Set<aBean> uniqueGas = new HashSet<aBean>(gasList);
like said Mathias Schwarz above, but you have to provide your aBean with the methods hashCode() and equals(Object obj) that can be done easily in Eclipse by dedicated menu 'Generate hashCode() and equals()' (while in the bean Class).
Set will evaluate the overridden methods to discriminate equals objects.

Java: Tricky sorting of prefixed Strings (ArrayLists)

(No networking knowledge required whatsoever. This is purely String and Lists).
Say I have a function in place, one that accepts a list of String IPv4 dotted address, and sorts them in ascending order. (Not alphabetical, true ip long format sorting). Let's call this:
public static ArrayList<String> sortListOfIpv4s(ArrayList<String> unsortedIPv4s);
This function already works correctly. Given an input:
192.168.1.1, 8.8.8.8, 4.5.6.7, 244.244.244.244, 146.144.111.6
It will output the list:
4.5.6.7, 8.8.8.8, 146.144.111.6, 192.168.1.1, 244.244.244.244
(Let's not get into a debate on whether it should modify the list in place or return a new list. It just returns a new list. Also, the function cannot be modified because of numerous reasons.)
However, my input list looks like this:
e192.168.1.1, f8.8.8.8, e4.5.6.7, f244.244.244.244, e146.144.111.6
When I remove the prefixes (only one of e or f, NOT NECESSARILY alternating) and create a clean array to pass to the sorting function, I lose the prefix information. What I would like is an output of the type:
e4.5.6.7, f8.8.8.8, e146.144.111.6, e192.168.1.1, f244.244.244.244
Basically, prior to sorting, whatever prefix was present for each element in the unsorted list, the same prefix needs to be added back to the elements in the sorted list.
Caveats:
An IP Address can repeat in the original list, a maximum of two times
When repeating twice, each of the two elements will have the same prefix, guaranteed
Sorting algorithm will not remove duplicates.
A little algorithmic help please? (Remember, we already have a function that can sort clean IPv4 String arraylists).
Don't remove the prefixes prior to passing it to the sorting function. Instead, in the sortListOfIpv4s method, always compare Strings using s.substring(1), which will give you the entire string without the prefix, and add s to the resulting sorted array.
If sortListOfIpv4s is a black box and you are required to pass the prefix-free Strings, then you could cache the prefixes beforehand in a Map from prefix-free IP -> prefix:
Map<String, String> prefixMap = new HashMap<String, String>();
for (String ip : unsortedIPv4s) {
prefixMap.put(ip.substring(1), ip.substring(0, 1));
}
Then sort and recover the prefixes from the Map:
List<String> sortedIPV4s = sortListOfIpv4s(unsortedIPv4s);
for (String ip : sortedIPV4s) {
String prefix = prefixMap.get(ip);
String originalIp = prefix + ip;
}
Your method could move any prefix to the end of the String, sort the list, and then go through the Strings again and move the prefixes from the end back to the start.
You could implement Comparator:
public class IpComparator implements Comparator<String> {
#Override
public int compare(String ipA, String ipB) {
return doComparison( ipA.substring(1), ipB.substring(1) );
}
}
Then you can use it:
return Collections.sort(unsortedIPv4s, new IpComparator());

Categories