Count the total boolean value of true using Java 8 - java

Im using java 8. I have a class Operator which has 3 fields.
class Operator{
private String type;
private boolean updateRequested;
private boolean deleteRequested;
}
I have list of Operator. I just want to count the updatedRequested and deleteRequested based on type whose value is true and add into the Map which is Map<String,Result>
class Result{
private int deleteReqCount;
private int updateReqCount;
}
Expected result
{
"Cricket":{ deleteReqCount:10, updateReqCount:0}, // count only the value == `true`
"Football":{ deleteReqCount:2, updateReqCount:10}, // count only the value == `true`
}
This question is bit simple and I did using for loops and if condition. But I'm impressed with Stream apis and Collectors framework. I'm a beginner, so tried list.stream().collect(Collectors.groupingBy(g -> g.getType())); but couldn't go further.
Thanks in advance

Here is an implementation using Collectors.toMap
class Operator {
public String type;
public boolean updateRequested;
public boolean deleteRequested;
Operator(String type, boolean updateRequested, boolean deleteRequested) {
this.type = type;
this.updateRequested = updateRequested;
this.deleteRequested = deleteRequested;
}
}
class Result {
public int deleteReqCount;
public int updateReqCount;
Result(int deleteReqCount, int updateReqCount) {
this.deleteReqCount = deleteReqCount;
this.updateReqCount = updateReqCount;
}
#Override
public String toString() {
return "Result{" +
"deleteReqCount=" + deleteReqCount +
", updateReqCount=" + updateReqCount +
'}';
}
}
Map<String, Result> solve(List<Operator> operatorList) {
return operatorList.stream()
.collect(Collectors.toMap(
v -> v.type,
v -> new Result(v.deleteRequested ? 1 : 0, v.updateRequested ? 1 : 0),
(result, result2) -> {
int deleteReqCount = result.deleteReqCount + result2.deleteReqCount;
int updateReqCount = result.updateReqCount + result2.updateReqCount;
return new Result(deleteReqCount, updateReqCount);
}
));
}
List<Operator> operatorList = Arrays.asList(
new Operator("cricket", true, true),
new Operator("cricket", true, false),
new Operator("cricket", true, true),
new Operator("soccer", false, true),
new Operator("soccer", true, true)
);
System.out.println(solve(operatorList));
Output:
{soccer=Result{deleteReqCount=2, updateReqCount=1}, cricket=Result{deleteReqCount=2, updateReqCount=3}}

An update to Sai Kiran's excellent answer:
Map<String, Result> solve(List<Operator> operatorList) {
return operatorList.stream()
.collect(Collectors.toMap(
v -> v.type,
v -> new Result(v.deleteRequested ? 1 : 0, v.updateRequested ? 1 : 0),
(result, result2) -> {
result.updateReqCount += result2.updateReqCount;
result.deleteReqCount += result2.deleteReqCount;
return result;
}
));
}

As per comment by #Andreas,
operators.stream().collect(Collectors.toMap(
Operator::getType,
v -> new Result(v.isDeleteRequested() ? 1 : 0, v.isUpdateRequested() ? 1 : 0),
Result::merge
));
Merge method,
public Result merge(Result result) {
this.updateReqCount+=result.updateReqCount;
this.deleteReqCount+=result.deleteReqCount;
return this;
}

For the time when you update to Java 12 or higher, the task can be achieved using Collectors.teeing :
List<Operator> ops = //your operators list
Map<String, Result> myMap =
ops.stream()
.collect(
Collectors.groupingBy(Operator::getType,
Collectors.teeing(
Collectors.filtering(Operator::isUpdateRequested,Collectors.counting()),
Collectors.filtering(Operator::isDeleteRequested,Collectors.counting()),
(updateCount, deleteCount) -> {
return new Result(updateCount.intValue(),deleteCount.intValue());
}
)));

Related

How to use two filters in stream for different transformations

I need to perform transformations only for a particular condition.
I do this transformation:
// filter 1: less date - group by max date by groupId
List<Info> listResult = new ArrayList<>(listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.groupingBy(Info::getGroupId, Collectors.collectingAndThen(
Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))).values());
But for the condition when there is more than the specified date, I do not need to convert anything, I just need to return this data:
// filter 2: more date - nothing change in list
List<Info> listMoreByDate = listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
.collect(Collectors.toList());
Next, to combine these two filters - I combine the two lists:
listResult.addAll(listMoreByDate);
My question is, can this be done in one stream? Because filter 2 is absolutely useless, it simply returns a list for this condition.
Is it possible to perform these transformations with one continuous expression?
My full code:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
public class App {
public static void main(String[] args) throws ParseException {
Info info1 = new Info(1L, getDateFromStr("2018-02-02T10:00:00"), 3L);
Info info2 = new Info(2L, getDateFromStr("2018-02-02T12:00:00"), 3L);
Info info3 = new Info(3L, getDateFromStr("2018-02-05T12:00:00"), 6L);
Info info4 = new Info(4L, getDateFromStr("2018-02-05T10:00:00"), 6L);
Date date = getDateFromStr("2018-02-03T10:10:10");
List<Info> listInfo = new ArrayList<>();
listInfo.add(info1);
listInfo.add(info2);
listInfo.add(info3);
listInfo.add(info4);
// filter 1: less date - group by max date by groupId
List<Info> listResult = new ArrayList<>(listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.groupingBy(Info::getGroupId, Collectors.collectingAndThen(
Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))).values());
// filter 2: more date - nothing change in list
List<Info> listMoreByDate = listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
.collect(Collectors.toList());
listResult.addAll(listMoreByDate);
System.out.println("result: " + listResult);
}
private static Date getDateFromStr(String dateStr) throws ParseException {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(dateStr);
}
}
class Info {
private Long id;
private Date date;
private Long groupId;
public Info(Long id, Date date, Long groupId) {
this.id = id;
this.date = date;
this.groupId = groupId;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Long getGroupId() {
return groupId;
}
public void setGroupId(Long groupId) {
this.groupId = groupId;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Info info = (Info) o;
return Objects.equals(id, info.id) &&
Objects.equals(date, info.date) &&
Objects.equals(groupId, info.groupId);
}
#Override
public int hashCode() {
return Objects.hash(id, date, groupId);
}
#Override
public String toString() {
final StringBuilder sb = new StringBuilder("Info{");
sb.append("id=").append(id);
sb.append(", date=").append(date);
sb.append(", groupId=").append(groupId);
sb.append('}');
return sb.toString();
}
}
I can’t see anything simpler than
List<Info> listResult = Stream.concat(
listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.toMap(Info::getGroupId, Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(Info::getDate))))
.values().stream(),
listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
)
.collect(Collectors.toList());
as these two operations are fundamentally different. Building a Map in the first step is unavoidable, as it will be used to identify the items with equal getGroupId property.
That said, you should consider switching from using Date to the java.time API.
Yes, you can merge the two conditions by using the partitioningBy collector as follows:
List<Info> resultSet =
listInfo.stream()
.collect(collectingAndThen(partitioningBy(info -> info.getDate().getTime() < date.getTime()),
map -> Stream.concat(map.get(true)
.stream()
.collect(toMap(Info::getGroupId,
Function.identity(),
(Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2))
.values().stream(), map.get(false).stream())
.collect(Collectors.toCollection(ArrayList::new))));
This essentially uses the partitioningBy collector to organise the elements in such a way that all the elements passing the criteria info.getDate().getTime() < date.getTime() aswell as where it's false i.e. where info -> info.getDate().getTime() >= date.getTime() is true into a Map<Boolean, List<T>>.
Further, we utilise the collectingAndThen collector to apply a finishing function upon the Map<Boolean, List<T>> returned by the partitioningBy collector, in this case we concatenate the result of the applying the logic of:
.collect(Collectors.groupingBy(Info::getGroupId,
Collectors.collectingAndThen(Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))))
.values();
which I've simplified to:
.collect(toMap(Info::getGroupId, Function.identity(), (Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2)))
.values();
with the elements returned where info.getDate().getTime() < date.getTime() returned false (map.get(false).stream()).
Finally, we collect the result into a ArrayList implementation with the toCollection collector.
Another approach (even more verbose in definition but much less verbose at use site) is to create a custom Collector:
List<Info> listResult = listInfo.stream().collect(dateThresholdCollector(date));
where
private static Collector<Info, ?, List<Info>> dateThresholdCollector(Date date) {
return Collector.of(
() -> new ThresholdInfoAccumulator(date), ThresholdInfoAccumulator::accept,
ThresholdInfoAccumulator::combine, ThresholdInfoAccumulator::addedInfos
);
}
and
class ThresholdInfoAccumulator {
private final Date date;
private final List<Info> addedInfos = new ArrayList<>();
ThresholdInfoAccumulator(Date date) {
this.date = date;
}
List<Info> addedInfos() {
return addedInfos;
}
ThresholdInfoAccumulator accept(Info newInfo) {
if (canAdd(newInfo)) {
addedInfos.add(newInfo);
}
return this;
}
boolean canAdd(Info newInfo) {
if (newInfo.getDate().compareTo(date) < 0) { // lower date - max date by groupId
return addedInfos.removeIf(addedInfo -> isEarlierDateInSameGroup(addedInfo, newInfo));
}
return true; // greater or equal date - no change
}
private boolean isEarlierDateInSameGroup(Info addedInfo, Info newInfo) {
return addedInfo.getGroupId().equals(newInfo.getGroupId())
&& addedInfo.getDate().compareTo(newInfo.getDate()) < 0;
}
ThresholdInfoAccumulator combine(ThresholdInfoAccumulator other) {
other.addedInfos().forEach(this::accept);
return this;
}
}
Note: it won't be that effective if you have huge number of groups/infos because it does not group by getGroupId (it iterates the entire list for every Info to be added).

Limit IntStream.iterate to a specific value

I'm generating, let's say, the following range:
IntStream.iterate(1, i -> 3*i)
How do I limit the stream to a specific element value e.g. 100 (not elements count with limit())?
Thank you!
UPDATE the function can be arbitrary
If you can’t use Java 9 yet, you can use the following reimplementation of the three-arg IntStream.iterate:
public static IntStream iterate(int seed, IntPredicate hasNext, IntUnaryOperator next) {
Objects.requireNonNull(next); Objects.requireNonNull(hasNext);
return StreamSupport.intStream(
new Spliterators.AbstractIntSpliterator(
Long.MAX_VALUE, Spliterator.ORDERED|Spliterator.NONNULL) {
private IntUnaryOperator op = i -> { op = next; return i; };
private int value = seed;
#Override
public boolean tryAdvance(IntConsumer action) {
Objects.requireNonNull(action);
if(op == null) return false;
int t = op.applyAsInt(value);
if(!hasNext.test(t)) { op = null; return false; }
action.accept(value = t);
return true;
}
#Override
public void forEachRemaining(IntConsumer action) {
Objects.requireNonNull(action);
IntUnaryOperator first = op;
if(first == null) return;
op = null;
for(int t = first.applyAsInt(value); hasNext.test(t); t = next.applyAsInt(t))
action.accept(t);
}
}, false);
}
It works similar to Java 9’s IntStream.iterate, except that you have to change the class you’re invoking the static method on (or adapt the import static statement):
iterate(1, i -> i < 100, i -> i*3).forEach(System.out::println);
1
3
9
27
81
A new method
Stream<T> iterate​(T seed,Predicate<? super T> hasNext,UnaryOperator<T> next)
was introduced in Java-9. So starting with that version it is possible to do something like this:
IntStream.iterate(1, i -> i < 100, i -> 3*i)
Which will produce 1 3 9 27 81
As addition to other answers, if you can use java-9 already, there is another possibility using Stream#takeWhile taking a Predicate as parameter.
Tested in jshell
jshell> IntStream.iterate(1, i -> 3 * i).takeWhile(i -> i < 100).toArray();
$3 ==> int[5] { 1, 3, 9, 27, 81 }
IntStream.range(0, N).forEach(this::doSomething);
int[] arr = IntStream.range(start, end).toArray();

How can I access an upstream object, later down stream in RxJava2

Let's say I have this Observable situation:
public void main() {
Observable.fromIterable(Arrays.asList(1, 2, 3, 4, 5))
.flatMap(id -> getEvenOdd(id))
.map(string -> {
// I now want to join string
// AND the last emitted ID integer
return null;
});
}
private Observable<String> getEvenOdd(Integer id) {
if (id % 2 == 0) {
return Observable.just("even");
} else {
return Observable.just("odd");
}
}
The flatmap has transformed Integer into String. How can I now get access to the Integer inside map?
I know that I could add a doOnNext and cache the Integer:
private Integer intCache;
public void main() {
Observable.fromIterable(Arrays.asList(1, 2, 3, 4, 5))
.doOnNext(integer -> intCache = integer)
.flatMap(id -> getEvenOdd(id))
.map(string -> {
return intCache.toString() + " " + string;
});
But this seems a little hacky and expands the scope of the Integer beyond my observable chain.
There is a specialized flatMap for this use-case:
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/Observable.html#flatMap(io.reactivex.functions.Function,%20io.reactivex.functions.BiFunction)
The second parameter combines the results from the flatmap with the item that caused them to be emitted.
So the updated example is
public void main() {
Observable.fromIterable(Arrays.asList(1, 2, 3, 4, 5))
.flatMap(id -> getEvenOdd(id),
(BiFunction<Integer, String, String>) (integer, string) -> { // LOOK HERE
return string + Integer.toString(integer);
})
.map(joinedStringAndInt -> {
... use joinedStringAndInt
});
}
Where the third type in the BiFunction is the type of the combined value. Here I just chose to combined the String and Integer into another String
As a sidenote, the doOnNext solution is hacky and unsafe if you consider there can be multiple instances of the same Observable accessing the intCache variable.
Another solution is just to use nesting. You can have both the last observable emission and your value from parameter in a nested structure:
public void main() {
Observable.fromIterable(Arrays.asList(1, 2, 3, 4, 5))
.flatMap(id -> getEvenOdd(id)
.map(string -> joinStringAndInt(string, id)))
.map(joinedStringAndInt -> {
// do your stuff here
return something;
});
}
private String joinStringAndInt(String string, Integer number) {
return number.toString() + " " + string;
}
private Observable<String> getEvenOdd(Integer id) {
if (id % 2 == 0) {
return Observable.just("even");
} else {
return Observable.just("odd");
}
}

Best way combining 4 lists into one

I have to preprocess 4 lists of medical data before we can import it into the software.
I have given 4 lists, each ordered already, that look like the following:
File 1) chapter
A00-B99;
C00-D48;
D50-D89;
C00-C99;
E00-E90;
...
Z00-Z99;
File 2) subchapter
A00-A09;
A15-A19;
A92-A99;
B95-B98;
B99-B99;
C00-C48;
...
Z80-Z99;
File 3) Groups
A00.-
A01.-
A02.-
...
C01.-
....
Z99.-
File 4) diagnoses
A00.0;
A00.1;
A00.7;
A00.8;
A01.7;
A02.8;
..
Z00.3;
Z00.4;
;
At the End it shoud be ordered as the list below.
Each line will be a line within a csv-file.
A00-B99; (Chapter)
A00-A09; (Subchapter)
A00.- (corresponding group)
A00.0 (corresponding diagnoses)
A00.1
A00.7
A00.8
A01.- (corresponding group)
A01.7 (corresponding diagnoses)
A02.- (corresponding group)
A02.8 (corresponding diagnoses)
...
B15-B99(Subchapter)
...
C00-C99 (Chapter)
C00-D48 (Subchapter)
C01.- (corresponding group)
C01.2 (corresponding diagnoses)
I've tried it so far by using some linked hasmaps but don't get the correct result.
while (entries_kapitel.hasNext()) {
Entry thisEntry_kapitel = (Entry) entries_kapitel.next();
String key_kapitel = (String) thisEntry_kapitel.getKey();
String text_kapitel = (String) thisEntry_kapitel.getValue();
// A00-B99 -> A und B
String kapitel_char1 = key_kapitel.split("-")[0].substring(0, 1);
String kapitel_char2 = key_kapitel.split("-")[1].substring(0, 1);
// A00-B99 -> 99
int kapitel_int2 = Integer.parseInt(key_kapitel.split("-")[1].substring(1, 3));
// subchapters
while (entries_gruppen.hasNext()) {
Entry thisEntry_gruppen = (Entry) entries_gruppen.next();
String key_gruppen = (String) thisEntry_gruppen.getKey();
String text_gruppen = (String) thisEntry_gruppen.getValue();
// Gruppe splitten T90-T89
String gruppe_char1 = key_gruppen.split("-")[0].substring(0, 1);
String gruppe_char2 = key_gruppen.split("-")[1].substring(0, 1);
int gruppe_int2 = Integer.parseInt(key_gruppen.split("-")[1].substring(1, 3));
if (gruppe_char1.equals(gruppe_char2) == false){
System.err.println("Subchapters start with the same capital!");
System.exit(1);
}
while (entries_gruppierung.hasNext()) {
Entry thisEntry_gruppierung = (Entry) entries_gruppierung.next();
String key_gruppierung = (String) thisEntry_gruppierung.getKey();
String text_gruppierung = (String) thisEntry_gruppierung.getValue();
String gruppierung_char1 = key_gruppierung.substring(0, 1);
int gruppierung_int1 = Integer.parseInt(key_gruppierung.substring(1, 3));
(gruppierung_char1.equals(gruppe_char1) && gruppierung_int1 <= gruppe_int2) {
System.out.println("Chapter: " + key_kapitel + " subchapter: " + key_gruppen + " group" + key_gruppierung);
while (diagnoses.hasNext()) {
....
The result does not look like it should (there are missing entries and they are not all ordered correctly)
What is the best way to solve this task?
I was not able to get a working tree, which probably is the best way to go, right?
If I understod well you're needs. I would use a SORT / MERGE join approach. Consider 4 lists containing the entries, properly sorted. Then you can merge the lists by scanning them alternately. I haven't tested the code but you'll get the general idea :
public class EntryComparator implements Comparator<Entry>
{
public boolean isSubsection(Entry e1, Entry e2)
{
// should return true if e2 subsection of e1
}
public int compare(Entry e1, Entry e2)
{
// see the Comparator interface documentation
}
}
List<Entry> chapters = new ArrayList<>();
List<Entry> subchapters = new ArrayList<>();
List<Entry> groups = new ArrayList<>();
List<Entry> diagnoses = new ArrayList<>();
List<Entry> result = new ArrayList<>(); // will hold the final result
// populate the lists, maybe sort them using Collections.sort and the Comparator above
int i1 = 0;
int i2 = 0;
int i3 = 0;
int i4 = 0;
EntryComparator c = new EntryComparator();
while( i1 < chapters.size() )
{
result.add(chapters.get(i1));
while( i2 < subchapters.size() &&
c.isSubsection(chapters.get(i1), subchapters.get(i2)) )
{
result.add(subchapters.get(i2));
while( i3 < groups.size() &&
c.isSubsection(subchapters.get(i2), groups.get(i3)) )
{
result.add(groups.get(i3));
while( i4 < subchapters.size() &&
c.isSubsection(groups.get(i3), diagnoses.get(i4)) )
{
result.add(diagnoses.get(i4));
i4++;
}
i3++;
}
i2++;
}
i1++;
}
EDIT : the advice given by 911DidBush is a good one, you may apply the same pattern with specialized classes.
thanks for your replies. As recommended, I will implement each list as a Class. This seems a really good idea. The Class Chapter will look like this. The other classes are having the same structure as well.
public class Chapter {
private String key_chapter;
private String text;
private ArrayList<Subchapter> subchapters = new ArrayList<>();
public Chapter(String chapter, String text) {
this.key_chapter = chapter;
this.text = text;
subchapters = new ArrayList<Subchapter>();
}
public String getChapter() {
return key_chapter;
}
public String getText() {
return text;
}
public void addSubchapter(String subchapter, String text) {
subchapters.add(new Subchapter(subchapter, text));
}
public Subchapter getKeyAtIndex(int index) {
return subchapters.get(index);
}
// get the entire ArrayList:
public ArrayList getListOfSubchapters() {
return subchapters;
}
}

how to make all possible power set(or subset) from arrayList objects?

Say I have the following class:
class A {
String name;
Double value;
}
and a list of the above class objects which might have:
[{f 2.1}, {c 1.1}, {a 0.3}... and so on]
[{n 0.5}, {f 1.9}, {x 0.1}, {a 1.9}, {b 1.1}... and so on]
... and so on
all I want is to do the followings:
1. Building power subsets from the internal list items(N.B: skip the single subsets).
2. Push the subset in another List as an object of the above class A like this:
a. if f,c is a subset of 1st element then f,c would be the name property of class A
and the value property will be the minimum of f and c from the list.
Like: {f,c 1.1} [ where f,c is a subset and min of 2.1(value of f)
and 1.1(value of c) is 1.1]
so, from the above list if I take 1st element the subsets and their values
in the pushing list would be like this(after skipping the single subsets):
[{f,c 1.1}, {c,a 0.3}, {f,a 0.3}, {f,c,a 0.3}]
and for the 2nd element this would be:
[{n,f 0.5}, {f,x 0.1}, {x,a 0.1}, {a,b 1.1}, {n,x 0.1}, {n,a 0.5}, {n,b 0.5},
{f,a 1.9}, {f,b 1.1}, {x,b 0.1}, {n,f,x 0.1}, {n,x,a 0.1},
{n,a,b 0.5}, {f,x,a 0.1}, {f,x,b 0.1}, {x,a,b 0.1}, {n,f,x,a 0.1},
{n,f,x,b 0.1}, {n,f,a,b 0.5}, {n,x,a,b 0.1}, {f,x,a,b 0.1},
{n,f,x,a,b 0.1}]
Can anybody suggest me please how can I do this in Java(with some sample code if possible).
Thanks!
Note power sets get large quickly, so this will run out of memory for even fairly small inputs. However if you have the memory, there are no other restrictions.
// As stated.
class A {
String name;
double value;
A(String name, double value) {
this.name = name;
this.value = value;
}
}
// Powerset set.
class ASet {
final ArrayList<String> names = new ArrayList<String>();
double value = Double.MAX_VALUE;
void adjoin(A a) {
names.add(a.name);
value = Math.min(value, a.value);
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('{');
for (String name : names) {
sb.append(name);
sb.append(',');
}
sb.append(value);
sb.append('}');
return sb.toString();
}
}
// Make power sets.
class PowerSetFactory {
// Stack for intermediate results.
final ArrayDeque<A> stack = new ArrayDeque<A>();
// Source data.
ArrayList<A> src;
// Powerset under construction
ArrayList<ASet> dst;
// Recursive powerset calculator
private void recur(int i) {
if (i >= src.size()) {
// Stack is complete. If more than 1 element,
// add its contents to the result.
if (stack.size() > 1) {
ASet set = new ASet();
for (A a : stack) set.adjoin(a);
dst.add(set);
}
}
else {
// Otherwise recur both without and with this element
// added to the stack. Clean up the stack before return.
recur(i + 1);
stack.offerLast(src.get(i));
recur(i + 1);
stack.pollLast();
}
}
// Get a powerset for the givens source data.
ArrayList<ASet> getPowerSet(ArrayList<A> src) {
this.src = src;
this.dst = new ArrayList<ASet>();
recur(0);
return dst;
}
public void test() {
ArrayList<A> data = new ArrayList<A>();
data.add(new A("f", 2.1));
data.add(new A("c", 1.1));
data.add(new A("a", 0.3));
for (ASet set : getPowerSet(data)) {
System.out.print(set);
}
System.out.println();
data.clear();
data.add(new A("n", 0.5));
data.add(new A("f", 1.9));
data.add(new A("x", 0.1));
data.add(new A("a", 1.9));
data.add(new A("b", 1.1));
for (ASet set : getPowerSet(data)) {
System.out.print(set);
}
System.out.println();
}
}
I am assuming that the order of the subsets on each element of your output list is irrelevant.
For inputs of any significant size, your output will be intractably large, so don't try to keep it in memory. You're better off implementing PowerList as its own collection. The following draft will only work for inputs of length 31 or less, and does not filter the singletons or the empty list.
public class PowerList extends AbstractList< A > {
private final List< A > laUnderlying;
public PowerList( List< A > laUnderlying ) {
this.laUnderlying = laUnderlying;
}
#Override
public A get( int index ) {
StringBuilder sbLabel;
A aOut = new A();
aOut.value = Double.MAX_VALUE;
int iUnderIndex = 0;
while ( 0 < index ) {
while ( 0 == ( index & 1 ) ) {
++iUnderIndex;
index = index >> 1;
}
A aComponent = laUnderlying.get( index );
sbLabel.append( ',' ).append( aComponent.name );
if ( aComponent.value < aOut.value )
aOut.value = aComponent.value;
}
if ( !sbLabel.isEmpty() )
aOut.name = sbLabel.substring( 1 );
return aOut;
}
public int size() {
return 1 << laUnderlying.size();
}
}
Now your original question reduces to
List< List< A > > llaOutput;
for ( List< A > laEach : llaInput )
llaOutput.add( new PowerList( laEach ) );

Categories

Resources