Java: Arrays & Vectors - java

I'm used to working with PHP but lately I've been working with Java and I'm having a headache trying to figure this out. I want to save this representation in Java:
Array (
["col_name_1"] => Array (
1 => ["col_value_1"],
2 => ["col_value_2"],
... ,
n => ["col_value_n"]
),
["col_name_n"] => Array (
1 => ["col_value_1"],
2 => ["col_value_2"],
... ,
n => ["col_value_n"]
)
)
Is there a clean way (i.e. no dirty code) to save this thing in Java? Note; I would like to use Strings as array indexes (in the first dimension) and I don't know the definite size of the arrays..

Try using a Map<String, List<String>>. This will allow you to use Strings as keys / indices into the outer map and get a result being a list of Strings as values. You'll probably want to use a HashMap for the outer map and ArrayList's for the inner lists.
If you want some clean code that is similar to the PHP you gave to initialize it, you can do something like this:
Map<String, List<String>> columns = new HashMap<String, List<String>>() {{
put("col_name_1", Arrays.asList("col_val_1", "col_val_2", "col_val_n"));
put("col_name_2", Arrays.asList("col_val_1", "col_val_2", "col_val_n"));
put("col_name_n", Arrays.asList("col_val_1", "col_val_2", "col_val_n"));
}};

You can use a Map and a List (these both are interfaces implemented in more than one way for you to choose the most adequate in your case).
For more information check the tutorials for Map and List and maybe you should start with the Collections tutorial.
An example:
import java.util.*;
public class Foo {
public static void main(String[] args) {
Map<String, List<String>> m = new HashMap<String, List<String>>();
List<String> l = new LinkedList<String>();
l.add("col_value_1");
l.add("col_value_2");
//and so on
m.put("col_name_1",l); //repeat for the rest of the colnames
//then, to get it you do
List<String> rl = m.get("col_name_1");
}
}

You want a Map, which are keyed by just about anything. HashMaps work in most cases.
Something like this.
List<String> col1Vals = new java.util.ArrayList<String>();
col1Vals.add("col_value_1");
col1Vals.add("col_value_2");
Map<String, List<String>> map = new HashMap<String, List<String>>();
map.put("col_name_1", col1Vals);
If you want something simpler, the commons-lang library has a MultiMap.

Be forewarned that the Vector is legacy code for the Collections framework. It synchronizes access to its elements which slows down performance. Most use cases for using List don't need this kind of thread safety, and even if you did, I would be more inclined to use the CopyOnWriteArrayList.

Related

Simplifying loop with Java 8

I have a method that adds maps to a cache and I was wondering what I could do more to simplify this loop with Java 8.
What I have done so far:
Standard looping we all know:
for(int i = 0; i < catalogNames.size(); i++){
List<GenericCatalog> list = DummyData.getCatalog(catalogNames.get(i));
Map<String, GenericCatalog> map = new LinkedHashMap<>();
for(GenericCatalog item : list){
map.put(item.name.get(), item);
}
catalogCache.put(catalogNames.get(i), map);};
Second iteration using forEach:
catalogNames.forEach(e -> {
Map<String, GenericCatalog> map = new LinkedHashMap<>();
DummyData.getCatalog(e).forEach(d -> {
map.put(d.name.get(), d);
});
catalogCache.put(e, map);});
And third iteration that removes unnecessary bracers:
catalogNames.forEach(objName -> {
Map<String, GenericCatalog> map = new LinkedHashMap<>();
DummyData.getCatalog(objName).forEach(obj -> map.put(obj.name.get(), obj));
catalogCache.put(objName, map);});
My question now is what can be further done to simplify this?
I do understand that it's not really necessary to do anything else with this method at this point, but, I was curios about the possibilities.
There is small issue with solution 2 and 3 they might cause a side effects
Side-effects in behavioral parameters to stream operations are, in
general, discouraged, as they can often lead to unwitting violations
of the statelessness requirement, as well as other thread-safety
hazards.
As an example of how to transform a stream pipeline that
inappropriately uses side-effects to one that does not, the following
code searches a stream of strings for those matching a given regular
expression, and puts the matches in a list.
ArrayList<String> results = new ArrayList<>();
stream.filter(s -> pattern.matcher(s).matches())
.forEach(s -> results.add(s)); // Unnecessary use of side-effects!
So instead of using forEach to populate the HashMap it is better to use Collectors.toMap(..). I am not 100% sure about your data structure, but I hope it is close enough.
There is a List and corresponding Map:
List<Integer> ints = Arrays.asList(1,2,3);
Map<Integer,List<Double>> catalog = new HashMap<>();
catalog.put(1,Arrays.asList(1.1,2.2,3.3,4.4));
catalog.put(2,Arrays.asList(1.1,2.2,3.3));
catalog.put(3,Arrays.asList(1.1,2.2));
now we would like to get a new Map where a map key is element from the original List and map value is an other Map itself. The nested Map's key is transformed element from catalog List and value is the List element itself. Crazy description and more crazy code below:
Map<Integer, Map<Integer, Double>> result = ints.stream().collect(
Collectors.toMap(
el -> el,
el -> catalog.get(el).stream().
collect(Collectors.toMap(
c -> c.intValue(),
c -> c
))
)
);
System.out.println(result);
// {1={1=1.1, 2=2.2, 3=3.3, 4=4.4}, 2={1=1.1, 2=2.2, 3=3.3}, 3={1=1.1, 2=2.2}}
I hope this helps.
How about utilizing Collectors from the stream API? Specifically, Collectors#toMap
Map<String, Map<String, GenericCatalog>> cache = catalogNames.stream().collect(Collectors.toMap(Function.identity(),
name -> DummyData.getCatalog(name).stream().collect(Collectors.toMap(t -> t.name.get(), Function.identity(),
//these two lines only needed if HashMap can't be used
(o, t) -> /* merge function */,
LinkedHashMap::new));
This avoids mutating an existing collection, and provides you your own individual copy of a map (which you can use to update a cache, or whatever you desire).
Also I would disagree with arbitrarily putting end braces at the end of a line of code - most style guides would also be against this as it somewhat disturbs the flow of the code to most readers.

How can I compare two MultiMaps?

I have two Multimaps which have been created from two huge CSV files.
Multimap<String, SomeClassObject> mapOne = ArrayListMultimap.create();
Multimap<String, SomeClassObject> mapTwo = ArrayListMultimap.create();
I have assumed one CSV column to be as a Key and each of the Key has thousands of values associated with it. Data contained within these Multimaps should be same. Now I want to compare the data within these Multimaps and find if any values are different. Here are the two approaches I am thinking of:
Approach One:
Make one big list from the Multimap. This big list will contain a few individual lists. Each of the smaller lists contains a unique value which is the "key" read from Multimap along with its associated values, which will form the rest of that individual list.
ArrayList<Collection<SomeClassObject>> bigList = new ArrayList<Collection<SomeClassObject>>();
Within bigList will be individual small lists A, B, C etc.
I plan on picking individual lists from each bigList of the two files on the basis of checking that individual list from second Multimap contains that "key" element. If it does, then compare both of these lists and find anything that could not be matched.
Approach Two:
Compare both the Multimaps but I am not sure how will that be done.
Which approach should have smaller execution time? I need the operation to be completed in minimum amount of time.
Use Multimaps.filterEntries(Multimap, Predicate).
If you want to get the differences between two Multimaps, it's very easy to write a filter based on containsEntry, and then use the filtering behavior to efficiently find all the elements that don't match. Just build the Predicate based on one map, and then filter the other.
Here's what I mean. Here, I'm using Java 8 lambdas, but you can look at the revision history of this post to see the Java 7 version:
public static void main(String[] args) {
Multimap<String, String> first = ArrayListMultimap.create();
Multimap<String, String> second = ArrayListMultimap.create();
first.put("foo", "foo");
first.put("foo", "bar");
first.put("foo", "baz");
first.put("bar", "foo");
first.put("baz", "bar");
second.put("foo", "foo");
second.put("foo", "bar");
second.put("baz", "baz");
second.put("bar", "foo");
second.put("baz", "bar");
Multimap<String, String> firstSecondDifference =
Multimaps.filterEntries(first, e -> !second.containsEntry(e.getKey(), e.getValue()));
Multimap<String, String> secondFirstDifference =
Multimaps.filterEntries(second, e -> !first.containsEntry(e.getKey(), e.getValue()));
System.out.println(firstSecondDifference);
System.out.println(secondFirstDifference);
}
Output is the element that is not in the other list, in this contrived example:
{foo=[baz]}
{baz=[baz]}
These multimaps will be empty if the maps match.
In Java 7, you can create the predicate manually, using something like this:
public static class FilterPredicate<K, V> implements Predicate<Map.Entry<K, V>> {
private final Multimap<K, V> filterAgainst;
public FilterPredicate(Multimap<K, V> filterAgainst) {
this.filterAgainst = filterAgainst;
}
#Override
public boolean apply(Entry<K, V> arg0) {
return !filterAgainst.containsEntry(arg0.getKey(), arg0.getValue());
}
}
Use it as an argument to Multimaps.filterEntries() like this:
Multimap<String, String> firstSecondDifference =
Multimaps.filterEntries(first, new FilterPredicate(second));
Multimap<String, String> secondFirstDifference =
Multimaps.filterEntries(second, new FilterPredicate(first));
Otherwise, the code is the same (with the same result) as the Java 8 version above.
From the ArrayListMultimap.equals doc:
Compares the specified object to this multimap for equality.
Two ListMultimap instances are equal if, for each key, they contain the same values in the same order. If the value orderings disagree, the multimaps will not be considered equal.
So just do mapOne.equals(mapTwo). You won't have a better execution time by trying to do it yourself.

How to make these simple and beautiful?

When I put a (KEY, VALUE) into a map such as Map<String, List<String>>, and I want to check if the KEY is existed first to decide if I have to make a new List, usually My Java Code looks like this:
Map<String, List<String>> example = new HashMap<>();
public void put(String k, String v){
if(example.containsKey(k)){
example.get(k).add(v);
return;
}
List<String> vs = new ArrayList<>();
vs.add(v);
example.put(k,vs);
}
It doesn't looks very nice. Is there any way to make it more simple and more beautiful?
If you have Java 8 you can write this as one line:
example.computeIfAbsent(k, key -> new ArrayList<>()).add(v);
This uses a lambda, so the new ArrayList is only created if required.
(k and key need to have different names, as they are different variables)
Map<String, List<String>> example = new HashMap<>();
public void put(String k, String v){
if (!example.containsKey(k)){
example.put(k, new ArrayList<>();
}
example.get(k).add(v);
}
Arguably, this is slightly wasteful - requiring you to get the list you just put - but to my eye it is much cleaner and more expressive.
If you can't use other libraries, or java 8, you could wrap the whole map and construct in a class of you own.
With your own class you:
Confine the messiness to one place.
Hide the face you are using a Map behind the scenes.
Have a place to move any additional logic to.

How to create and push dynamic elements in HashMap

static Map<Integer,HashMap<String,HashMap<String,String>>> maps = new HashMap<Integer, HashMap<String,HashMap<String,String>>>();
I want to insert the elements inside the HashMap I declared above , the inner most hashmap has values ready which I can use , now I am using it like ,
static Map<String,String> values = new HashMap<String, String>();
maps.put(1, new HashMap<<new String("")>, values>());
How can I achieve this ?
static Map<String,String> values1 = new HashMap<String,String>();
static Map<String,Map<String,String>> values2 = new HashMap<String,Map<String,String>>();
values2.put("", values1);
maps.put(1,values2);
btw, if you have java 7, you can use:
Map<String,String> values1 = new HashMap<>();
and so on for others
In cases you have map inside a map (inside a map), consider using Apache MultiKeyMap.
Coding will be more intuitive
It will improve the readability of your code
It will prevent many if(map.get(key) != null) blocks you will probably have in your code.
Why not to have instance of HashMap. When you wan to insert new value, you need to have Integer, String, String key and String value.
You continuously select nested HashMaps according to keys and then insert value to the most inner HashMap.
map.get(key1).get(key2).insert(key3, value)

Efficiently "modifying" an ImmutableMap

We're currently using Guava for its immutable collections but I was surprised to find that their maps don't have methods to easily create new maps with minor modifications. And on top of that, their builder doesn't allow assigning new values to keys, or removing keys.
So if I wanted to modify just one value, here's what I would like to be able to do:
ImmutableMap<Guid, ImmutableMap<String, Integer>> originalMap = /* get the map */;
ImmutableMap<Guid, ImmutableMap<String, Integer>> modifiedMap =
originalMap.cloneAndPut(key, value);
Here's what it looks like Guava are expecting me to do:
ImmutableMap<Guid, ImmutableMap<String, Integer>> originalMap = /* get the map */;
Map<Guid, ImmutableMap<String, Integer>> mutableCopy = new LinkedHashMap<>(originalMap);
mutableCopy.put(key, value);
originalMap = ImmutableMap.copyOf(mutableCopy);
/* put the map back */
By doing this I get a new copy of the map with the modification I want. The original copy is untouched and I will be using an atomic reference to put the thing back so the whole setup is thread-safe.
It's just slow.
There is a lot of wasted copying going on under the covers here. Suppose there's 1,024 buckets in the map. That's 1,023 buckets which you're unnecessarily creating all over again (twice each, too), when you could have used those immutable buckets as-is and cloned only one of them.
So I guess:
Is there a Guava utility method buried somewhere for this sort of thing? (It isn't in Maps or on the ImmutableMap.Builder.)
Is there any other Java library which gets this sort of thing right? I am under the impression that Clojure has this sort of thing under the hood but we're not ready to switch languages just yet...
A bit unexpected the map of Functional Java is mutable like Guava's. The list is immutable though as I would expect.
Googling for "persistent collection java" brought up: pcollections. There are's a Map implementation.
Before actually using any other implementation I would benchmark the memory and performance characteristics against Guava. I would not be surprised if it's still better.
One may go with the following, to just duplicate the map once instead of twice:
ImmutableMap<String, Object> originalMap = /* get the map */;
Map<String, Object> modifiedMap = ImmutableMap.<String, Object>builder().putAll( originalMap )
.put( "new key", new Object() )
.build();
However, removing a value looks quite less beautiful with an iteration over the existing map, e.g. like this:
ImmutableMap<String, Object> originalMap = /* get the map */;
if( !originalMap.containsKey( "key to remove" ) ) {
return;
}
ImmutableMap.Builder<String, Object> mapBuilder = ImmutableMap.builder();
for( Map.Entry<String, Object> originalEntry : originalMap.entrySet() ) {
if( originalEntry.getKey().equals( "key to remove" ) ) {
continue;
}
mapBuilder.put( originalEntry );
}
Map<String, Object> modifiedMap = mapBuilder.build();

Categories

Resources