Related
I have an ArrayList which is filled by Objects.
My object class called Article which has two fields ;
public class Article {
private int codeArt;
private String desArt;
public Article(int aInt, String string) {
this.desArt = string;
this.codeArt = aInt;
}
public int getCodeArt() {return codeArt; }
public void setCodeArt(int codeArt) {this.codeArt = codeArt;}
public String getDesArt() {return desArt;}
public void setDesArt(String desArt) { this.desArt = desArt;}
}
I want to filter my List using the desArt field, and for test I used the String "test".
I used the Guava from google which allows me to filter an ArrayList.
this is the code I tried :
private List<gestionstock.Article> listArticles = new ArrayList<>();
//Here the I've filled my ArrayList
private List<gestionstock.Article> filteredList filteredList = Lists.newArrayList(Collections2.filter(listArticles, Predicates.containsPattern("test")));
but this code isn't working.
In Java 8, using filter
List<Article> articleList = new ArrayList<Article>();
List<Article> filteredArticleList= articleList.stream().filter(article -> article.getDesArt().contains("test")).collect(Collectors.toList());
This is normal: Predicates.containsPattern() operates on CharSequences, which your gestionStock.Article object does not implement.
You need to write your own predicate:
public final class ArticleFilter
implements Predicate<gestionstock.Article>
{
private final Pattern pattern;
public ArticleFilter(final String regex)
{
pattern = Pattern.compile(regex);
}
#Override
public boolean apply(final gestionstock.Article input)
{
return pattern.matcher(input.getDesArt()).find();
}
}
Then use:
private List<gestionstock.Article> filteredList
= Lists.newArrayList(Collections2.filter(listArticles,
new ArticleFilter("test")));
However, this is quite some code for something which can be done in much less code using non functional programming, as demonstrated by #mgnyp...
You can use a for loop or for each loop to loop thru the list.
Do you want to create another list based on some condition?
This should work I think.
List<Article> secondList = new ArrayList<Article>();
for( Article a : listArticles) {
// or equalsIgnoreCase or whatever your conditon is
if (a.getDesArt().equals("some String")) {
// do something
secondList.add(a);
}
}
Guava is a library that allows you to use some functional programming in Java.
One of the winning things in functional programming is collection transformation like
Collection -> op -> op -> op -> transformedCollection.
Look here:
Collection<Article> filtered = from(listArticles).filter(myPredicate1).filter(myPredicate2).filter(myPredicate3).toImmutableList();
It's beautiful, isn't it?
The second one winning thing is lambda functions. Look here:
Collection<Article> filtered = from(listArticles)
.filter((Predicate) (candidate) -> { return candidate.getCodeArt() > SOME_VALUE })
.toImmutableList();
Actually, Java has not pure lambda functions yet. We will be able to do it in Java 8. But for now we can write this in IDE Inellij Idea, and IDE transforms such lambda into Predicate, created on-the-fly:
Collection<Article> filtered = from(listArticles)
.filter(new Predicate<Article>() {
#Override
public boolean apply(Article candidate) {
return candidate.getCodeArt() > SOME_VALUE;
}
})
.toImmutableList();
If your filter condition requires regexp, the code become more complicated, and you will need to move condition to separate method or move whole Predicate to a separate class.
If all this functional programming seems too complicated, just create new collection and fill it manually (without Guava):
List<Article> filtered = new ArrayList<Article>();
for(Article article : listArticles)
{
if(article.getCodeArt() > SOME_VALUE)
filtered.add(article);
}
With Guava, I would say that the easiest way by far would be by using Collections2.filter, such as:
Collections2.filter(YOUR_COLLECTION, new Predicate<YOUR_OBJECT>() {
#Override
public boolean apply(YOUR_OBJECT candidate) {
return SOME_ATTRIBUTE.equals(candidate.getAttribute());
}
});
Try this:
private List<gestionstock.Article> listArticles = new ArrayList<>();
private List<gestionstock.Article> filteredList filteredList = Lists.newArrayList(Collections2.filter(listArticles, new Predicate<gestionstock.Article>(){
public boolean apply(gestationstock.Article article){
return article.getDesArt().contains("test")
}
}));
The idea being is since you're using a custom object, you should implement your own predicate. If you're using it anywhere else, define it in a file, otherwise, this implementation works nicely.
Please, Is there an elegant and efficient way of doing the following in Post Java 8 i.e.
1. Looping through an arraylist
2. Reading the object
3. Calling different methods of potentially different objects using the values contained in the arraylist items
I did look at streams, switch statement, but it was much messy than my if-else.
Any help will be appreciated. Just looking for continuous improvements
List<JAXBElement<? extends Serializable>> bodyElements = eventRequestBodyTypeSrc.getContent();
for(JAXBElement element: bodyElements){
if(element.getName().getLocalPart().equalsIgnoreCase("A")){
methodA(element.getValue());
}else if(element.getName().getLocalPart().equalsIgnoreCase("B")){
methodB(element.getValue());
}else if(element.getName().getLocalPart().equalsIgnoreCase("C")){
methodC(element.getValue());
}else if(element.getName().getLocalPart().equalsIgnoreCase("D")){
methodD(element.getValue());
}else if(element.getName().getLocalPart().equalsIgnoreCase("E")){
methodE(element.getValue());
}else{
}
}
I think you have a bit of an XY Problem going on. I would refactor this at a higher level to encapsulate the strings and their related actions. Here's a rough concept using an enum:
enum Action {
A("a") {
#Override
void doSomething(Serializable value) {
// ...
}
},
B("b") {
#Override
void doSomething(Serializable value) {
// ...
}
};
private static final Map<String, Action> actionsByName = Arrays.stream(values())
.collect(Collectors.toMap(a -> a.name, a -> a));
private final String name;
Action(String name) {
this.name = name;
}
abstract void doSomething(Serializable value);
public static void processElement(JAXBElement<? extends Serializable> element) {
Action action = actionsByName.get(element.getName().getLocalPart().toLowerCase());
if (action != null) {
action.doSomething(element.getValue());
} else {
// ...
}
}
}
As a lean solution I would gather all mappings first as follows:
Map<String, Consumer<Serializable>> dispatchers = new HashMap<>();
dispatchers.put("A", this::methodA);
// etc.
...and dispatch the elements like that:
Consumer<Serializable> dispatcher = dispatchers.get(element.getName().getLocalPart().toUpperCase(Locale.US));
if (dispatcher != null) {
dispatcher.accept(element.getValue());
}
I have an ArrayList which is filled by Objects.
My object class called Article which has two fields ;
public class Article {
private int codeArt;
private String desArt;
public Article(int aInt, String string) {
this.desArt = string;
this.codeArt = aInt;
}
public int getCodeArt() {return codeArt; }
public void setCodeArt(int codeArt) {this.codeArt = codeArt;}
public String getDesArt() {return desArt;}
public void setDesArt(String desArt) { this.desArt = desArt;}
}
I want to filter my List using the desArt field, and for test I used the String "test".
I used the Guava from google which allows me to filter an ArrayList.
this is the code I tried :
private List<gestionstock.Article> listArticles = new ArrayList<>();
//Here the I've filled my ArrayList
private List<gestionstock.Article> filteredList filteredList = Lists.newArrayList(Collections2.filter(listArticles, Predicates.containsPattern("test")));
but this code isn't working.
In Java 8, using filter
List<Article> articleList = new ArrayList<Article>();
List<Article> filteredArticleList= articleList.stream().filter(article -> article.getDesArt().contains("test")).collect(Collectors.toList());
This is normal: Predicates.containsPattern() operates on CharSequences, which your gestionStock.Article object does not implement.
You need to write your own predicate:
public final class ArticleFilter
implements Predicate<gestionstock.Article>
{
private final Pattern pattern;
public ArticleFilter(final String regex)
{
pattern = Pattern.compile(regex);
}
#Override
public boolean apply(final gestionstock.Article input)
{
return pattern.matcher(input.getDesArt()).find();
}
}
Then use:
private List<gestionstock.Article> filteredList
= Lists.newArrayList(Collections2.filter(listArticles,
new ArticleFilter("test")));
However, this is quite some code for something which can be done in much less code using non functional programming, as demonstrated by #mgnyp...
You can use a for loop or for each loop to loop thru the list.
Do you want to create another list based on some condition?
This should work I think.
List<Article> secondList = new ArrayList<Article>();
for( Article a : listArticles) {
// or equalsIgnoreCase or whatever your conditon is
if (a.getDesArt().equals("some String")) {
// do something
secondList.add(a);
}
}
Guava is a library that allows you to use some functional programming in Java.
One of the winning things in functional programming is collection transformation like
Collection -> op -> op -> op -> transformedCollection.
Look here:
Collection<Article> filtered = from(listArticles).filter(myPredicate1).filter(myPredicate2).filter(myPredicate3).toImmutableList();
It's beautiful, isn't it?
The second one winning thing is lambda functions. Look here:
Collection<Article> filtered = from(listArticles)
.filter((Predicate) (candidate) -> { return candidate.getCodeArt() > SOME_VALUE })
.toImmutableList();
Actually, Java has not pure lambda functions yet. We will be able to do it in Java 8. But for now we can write this in IDE Inellij Idea, and IDE transforms such lambda into Predicate, created on-the-fly:
Collection<Article> filtered = from(listArticles)
.filter(new Predicate<Article>() {
#Override
public boolean apply(Article candidate) {
return candidate.getCodeArt() > SOME_VALUE;
}
})
.toImmutableList();
If your filter condition requires regexp, the code become more complicated, and you will need to move condition to separate method or move whole Predicate to a separate class.
If all this functional programming seems too complicated, just create new collection and fill it manually (without Guava):
List<Article> filtered = new ArrayList<Article>();
for(Article article : listArticles)
{
if(article.getCodeArt() > SOME_VALUE)
filtered.add(article);
}
With Guava, I would say that the easiest way by far would be by using Collections2.filter, such as:
Collections2.filter(YOUR_COLLECTION, new Predicate<YOUR_OBJECT>() {
#Override
public boolean apply(YOUR_OBJECT candidate) {
return SOME_ATTRIBUTE.equals(candidate.getAttribute());
}
});
Try this:
private List<gestionstock.Article> listArticles = new ArrayList<>();
private List<gestionstock.Article> filteredList filteredList = Lists.newArrayList(Collections2.filter(listArticles, new Predicate<gestionstock.Article>(){
public boolean apply(gestationstock.Article article){
return article.getDesArt().contains("test")
}
}));
The idea being is since you're using a custom object, you should implement your own predicate. If you're using it anywhere else, define it in a file, otherwise, this implementation works nicely.
I'm trying to create a generic parsed string cache to prevent rebuilding the same object over and over. Essentially, what I'm building is something like the following:
public class ParsedStringCache {
// static Map<Pair<String, Parser<T>>, T> _cache
// = new HashMap<Pair<String, Parser<T>>, T>();
public interface StringParser<T> {
public T parseString(String stringToParse);
}
public static <T> T getParsedObject(String stringToParse, Parser<T> parser) {
return parser.parseString(stringToParse);
}
}
This works fine until I try to actually cache the results, such as by using something similar to the commented out hashmap in the code above, which would essentially memoize the result of getParsedObject. Is there a reasonably simple way to do this that avoids casting?
I would make the cache part of the parser. I'm sure you'll run into some kind of compile error that you'll need to workaround, but here's the basics of what I would do.
import java.util.*;
abstract class Parser<T> {
private Map<String,T> cache = new HashMap<String,T>();
public final T parseString(String str) {
T result = cache.get(str);
if(result == null) {
result = parseString0(str);
cache.put(str,result);
}
return result;
}
protected abstract T parseString0(String str);
}
public class IntParser extends Parser<Integer> {
protected Integer parseString0(String str) {
return Integer.parseInt(str.trim());
}
}
public class LongParser extends Parser<Long> {
protected Long parseString0(String str) {
return Long.parseLong(str.trim());
}
}
class ParserTest {
public static void main(String[] args) {
Parser<Integer> intParse = new IntParser();
Parser<Long> longParse = new LongParser();
Long long1 = longParse.parseString("10000");
Long long2 = longParse.parseString("20000");
Long long3 = longParse.parseString("30000");
Long equalLong = longParse.parseString("20000"); // repeat long2
Long fakeLong = new LongParser().parseString("20000"); // repeated with fake
System.out.println("Expecting true: " + (long2 == equalLong));
System.out.println("Expecting false: " + (fakeLong == equalLong));
}
}
C:\Documents and Settings\glowcoder\My Documents>javac Parser.java
C:\Documents and Settings\glowcoder\My Documents>javac IntParser.java
C:\Documents and Settings\glowcoder\My Documents>javac LongParser.java
C:\Documents and Settings\glowcoder\My Documents>javac ParserTest.java
C:\Documents and Settings\glowcoder\My Documents>java ParserTest
Expecting true: true
Expecting false: false
I would keep them managed how they are, but add methods to gather information and report to a central manager. Obviously you'll need to add some more methods to the parser to keep track of the cache. Or (and this is probably a better idea) you could consolidate the cache logic into a Cache class, and let the manager track those caches.
By doing it this way you allow the cache itself to determine how it trims its information. It could be by oldest, it could be by size, it could be all kinds of criteria. Perhaps it has user information and you want your paid users to stay in the cache more.
import java.util.*;
class ParserCacheManager {
private Set<Parser<?>> parsers = new HashSet<Parser<?>>();
public void addParser(Parser<?> p) { parsers.add(p); }
public int size() {
int size = 0;
for(Parser<?> p : parsers) size += p.cacheSize();
return size;
}
public void trimToSize(int maxSize) {
while(size() > maxSize) {
Parser<?> p = largestParser();
p.trimToPercent(0.90);
}
}
/**
* This would be better perhaps with a set sorted by
* size, or something like that. But this works for example.
*/
private Parser<?> largestParser() {
Parser<?> largest = null;
for(Parser<?> p : parsers) {
if(largest == null || p.size() > largest.size())
largest = p;
}
return largest;
}
}
Unless you're doing this as an exercise, consider using Guava's CacheBuilder (or the more general MapMaker) for this task. It will handle all concurrency and expiration under the hood and is easily and fluently configurable:
Cache<String, T> parsedObjectCache = CacheBuilder.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(
new CacheLoader<String, T>() {
public T load(String str) throws MyParseException {
return parse(str); //called either on or from inside a Parser<T>
}
});
If you want a centralized cache, you would need to compose a custom key class that wrapped references to the String and Parser<?> and overrode hashcode/equals.
However, you're probably better off implementing a separate Cache<String, T> for each parser since type information will be lost trying to store everything in the same Collection.
A compromise would be to keep a master Map<Parser<?>, Cache<String, ?>> which you could use to look up each Cache based on the Parser (or alternatively use each Class<?> of T as the key). However you're still losing generic type information this way and casting would be necessary.
You might consider #glowcoder's suggestion and integrate a Cache<String, T> into each Parser<T>. This seems a reasonable way to maintain generic type information without casting.
You have to make ParserStringCache generic and you cannot use arbitrary generic parameter on a static class variable, it has to be a non-static member.
public class ParsedStringCache< T > {
Map<Pair<String, Parser<T>>, T> _cache =
new HashMap<Pair<String, Parser<T>>, T>();
}
Suppose I have this:
public class Unit<MobileSuit, Pilot> {
...
List<MobileSuit> mobileSuits;
List<Pilot> pilots;
...
}
And I would like to iterate through the pair of each in the simplest way outside of that class. How should I go about doing that? I thought about doing this:
public class Unit<MobileSuit, Pilot> {
...
Iterator<MobileSuit> iteratinMechas;
Iterator<Pilot> iteratinPeople;
class IteratorCustom<MobileSuit, Pilot> implements Iterator {
public boolean hasNext() {
return iteratinMechas.hasNext() && iteratinPeople.hasNext();
}
public void remove() {
iteratinMechas.remove();
iteratinPeople.remove();
}
public Object next() {
// /!\
}
}
public Iterator iterator() {
return new IteratorCustom<MobileSuit, Pilot>(mobileSuits, pilots);
}
}
Something along those lines.
Anyway, the problem is that I can't really return just a single object from next(), and I also can't have a Iterator take more than one type. So, any thoughts?
Also, I can't make a new class to combine MobileSuit and Pilot. I need to keep them separate, even though I'm iterating through both at a time. The reason is that there might be Mobile Suits that have no pilots, and I'm not sure how to fix that by keeping them at the same class. This class needs to be processed in other places, so I'd have to unify a interface around that and a lot of other stuff. Basically, assume MobileSuit and Pilot need to be separated.
Anyway, the problem is that I can't really return just a single object from next(), and I also can't have a Iterator take more than one type. So, any thoughts?
Obviously you are going to need a light-weight "pair" class. This is roughly analogous to the Map.Entry inner class.
Here's a rough cut at a generic solution:
public class ParallelIterator <T1, T2> implements Iterator<Pair<T1, T2>> {
public class Pair<TT1, TT2> {
private final TT1 v1;
private final TT2 v2;
private Pair(TT1 v1, TT2 v2) { this.v1 = v1; this.v2 = v2; }
...
}
private final Iterator<T1> it1;
private final Iterator<T2> it2;
public ParallelIterator(Iterator<T1> it1, Iterator<T2> it2) {
this.it1 = it1; this.it2 = it2;
}
public boolean hasNext() { return it1.hasNext() && it2.hasNext(); }
public Pair<T1, T2> next() {
return new Pair<T1, T2>(it1.next(), it2.next());
}
...
}
Note: this doesn't explicitly deal with cases where the lists have different lengths. What will happen is that extra elements at the end of the longer list will be silently ignored.
This is copied+edited from Stephen C's answer. Feel free to use:
public class Pair<T1, T2> {
private final T1 v1;
private final T2 v2;
Pair(T1 v1, T2 v2) {
this.v1 = v1;
this.v2 = v2;
}
public T1 first(){
return v1;
}
public T2 second(){
return v2;
}
}
public class ParallelIterator <T1, T2> implements Iterator<Pair<T1, T2>> {
private final Iterator<T1> it1;
private final Iterator<T2> it2;
public ParallelIterator(Iterator<T1> it1, Iterator<T2> it2) {
this.it1 = it1; this.it2 = it2;
}
#Override
public boolean hasNext() { return it1.hasNext() && it2.hasNext(); }
#Override
public Pair<T1, T2> next() {
return new Pair<T1, T2>(it1.next(), it2.next());
}
#Override
public void remove(){
it1.remove();
it2.remove();
}
}
public class IterablePair <T1, T2> implements Iterable<Pair<T1,T2>> {
private final List<T1> first;
private final List<T2> second;
public IterablePair(List<T1> first, List<T2> second) {
this.first = first;
this.second = second;
}
#Override
public Iterator<Pair<T1, T2>> iterator(){
return new ParallelIterator<T1,T2>( first.iterator(), second.iterator() );
}
}
void someFunction(){
IterablePair<X,Y> listPair = new IterablePair<X,Y>( x, y );
for( Pair<X,Y> pair : listPair ){
X x = pair.first();
...
}
}
This stops as soon as either list is out of elements, so you might want to check lists have equal size before creating an IterablePair.
Also, I can't make a new class to combine MobileSuit and Pilot.
That doesn't sound correct. It sounds like you can't replace MobileSuit and Pilot by a single class, but I don't see any reason why you can't have a single class that combines them - i.e. one which just has a getPilot() method and a getMobileSuit() method. You could use a generic Pair class for the same purpose, but a custom class would be easier to use.
On the other hand, if you want to do this sort of "zipping" operation in multiple places, it might be one solution. Alternatively, you could write a generic interface to represent the act of combining the two distinct items - which could return a SuitedPilot or whatever your combination class is.
The reason is that there might be Mobile Suits that have no pilots, and I'm not sure how to fix that by keeping them at the same class.
You can use null values, right? Which is the correct way of doing it - have each suit keep track of its pilot. If it has no pilot, then indicate that with a null value there.
But, if you're dead set on not doing that for some reason...
public class SuitAndPilot
{
public MobileSuit suit;
public Pilot pilot;
public SuitAndPilot(Suit s, Pilot p) {
suit = s;
pilot = p;
}
}
Why not have a class MannedMobileSuit as a subclass of MobileSuit that contains an instance of a pilot ? That would solve your problem by having a getPilot method.
Usually when you get such problems (needing to return two instances) it is because your Object model is not appropriate and should be changed. Keep your options open
Came across this page trying to solve this issue, and turns out that there's a library out there that's already solved it using Java 8 streams (check out the Zip function).
You can convert a list to a stream just by calling list.stream()
https://github.com/poetix/protonpack
Stream<String> streamA = Stream.of("A", "B", "C");
Stream<String> streamB = Stream.of("Apple", "Banana", "Carrot", "Doughnut");
List<String> zipped = StreamUtils.zip(streamA,
streamB,
(a, b) -> a + " is for " + b)
.collect(Collectors.toList());
assertThat(zipped,
contains("A is for Apple", "B is for Banana", "C is for Carrot"));
Basically, assume MobileSuit and Pilot need to be separated.
That's fine, but here you're trying to treat them as a unit, so structure your code that way. The suggestions above use a Pair class or Map.Entry, but it's much better to provide a clearly-named object that represents a MobileSuit with a Pilot, e.g.:
public class OccupiedSuit {
private final MobileSuit suit;
private final Pilot pilot;
public OccupiedSuit(MobileSuit suit, Pilot pilot) {
this.suit = checkNotNull(suit);
this.pilot = checkNotNull(pilot);
}
// getters, equals, hashCode, toString
// or just use #AutoValue: https://github.com/google/auto/tree/master/value
}
Then, rather than constructing a custom Iterator/Iterable, just write a helper function that zips up the two lists. For example:
public static List<OccupiedSuit> assignPilots(
Iterable<MobileSuit> suits, Iterable<Pilot> pilots) {
Iterator<MobileSuit> suitsIter = suits.iterator();
Iterator<Pilot> pilotsIter = pilots.iterator();
ImmutableList.Builder<OccupiedSuit> builder = ImmutableList.builder();
while (suitsIter.hasNext() && pilotsIter.hasNext()) {
builder.add(new OccupiedSuit(suitsIter.next(), pilotsIter.next()));
}
// Most of the existing solutions fail to enforce that the lists are the same
// size. That is a *classic* source of bugs. Always enforce your invariants!
checkArgument(!suitsIter.hasNext(),
"Unexpected extra suits: %s", ImmutableList.copyOf(suitsIter));
checkArgument(!pilotsIter.hasNext(),
"Unexpected extra pilots: %s", ImmutableList.copyOf(pilotsIter));
return builder.build();
}
Now you don't need to maintain a complex custom Iterator implementation - just rely on one that already exists!
We can also generalize assignPilots() into a generic utility that works for any two inputs, like so:
public static <L,R,M> List<M> zipLists(
BiFunction<L,R,M> factory, Iterable<L> left, Iterable<R> right) {
Iterator<L> lIter = left.iterator();
Iterator<R> rIter = right.iterator();
ImmutableList.Builder<M> builder = ImmutableList.builder();
while (lIter.hasNext() && rIter.hasNext()) {
builder.add(factory.apply(lIter.next(), rIter.next()));
}
checkArgument(!lIter.hasNext(),
"Unexpected extra left elements: %s", ImmutableList.copyOf(lIter));
checkArgument(!rIter.hasNext(),
"Unexpected extra right elements: %s", ImmutableList.copyOf(rIter));
return builder.build();
}
Which you'd then invoke like so:
List<OccupiedSuit> occupiedSuits = zipLists(OccupiedSuit::new, suits, pilots);
Example code uses Guava's Preconditions and ImmutableList - if you don't use Guava it's easy enough to inline and swap to ArrayList, but just use Guava :)
for(int i=0; i < mobileSuits.size(); i++) {
MobileSuit suit = mobileSuits.get(i);
Pilot pilot = pilots.get(i);
...
}
You could just use a Map<MobileSuit, Pilot>, where a null value mapped to a MobileSuit indicates no pilot. The Iterator could just be an Iterator<Map.Entry<MobileSuit, Pilot>> retrieved by map.entrySet().iterator().
Improving on the answer by user2224844, here is a simple version that will try no to run into an exception:
final Iterator<String> pilotIterator = pilots.iterator();
mobileSuits.forEach(m -> {
Pilot p = pilotIterator.hasNext()? pilotIterator.next():nullOrWahtever;
<Now do your work with m and p variables>
...
});
Isn't that enough ?
for(MobileSuit ms : MobileSuits) {
for(Pilot p : pilots){
//TODO
}
}