The only way I see is putting the condition in the map call. If you use filter you lose the "else" part.
List<String> myList = source.stream()
.map(item -> {
if (!(item.get(bar) instanceof JSONObject)) {
return item.get(bar).toString();
} else {
return item.getJSONObject(attribute).get("key").toString();
}
})
.collect(Collectors.toList());
or, as Holger suggested in a comment, use the ternary conditional operator:
List<String> myList = source.stream()
.map(i -> (i.get(bar) instanceof JSONObject ? i.getJSONObject(attribute).get("key") : i.get(bar)).toString())
.collect(Collectors.toList());
Answer from Eran on Stack OverflowThe only way I see is putting the condition in the map call. If you use filter you lose the "else" part.
List<String> myList = source.stream()
.map(item -> {
if (!(item.get(bar) instanceof JSONObject)) {
return item.get(bar).toString();
} else {
return item.getJSONObject(attribute).get("key").toString();
}
})
.collect(Collectors.toList());
or, as Holger suggested in a comment, use the ternary conditional operator:
List<String> myList = source.stream()
.map(i -> (i.get(bar) instanceof JSONObject ? i.getJSONObject(attribute).get("key") : i.get(bar)).toString())
.collect(Collectors.toList());
What about something like this?
List<String> myList = source.stream()
.map(json -> !(json.get(bar) instanceof JSONObject) ?
json.get(bar).toString() :
json.getJSONObject(attribute).get("key").toString())
.collect(Collectors.toList());
Not tested but you get the idea.
I believe you're trying to simply sum the values grouping by date. So assuming you have parsed data as a List
List<Data> list = getDataAsList();
List<DataHistory> historyList = list.stream()
.collect(Collectors.groupingBy(data -> data.date)).entrySet().stream()
.map((entry) -> {
DataHistory history = new DataHistory();
history.date = entry.getKey();
List<Data> dataList = entry.getValue();
history.sum_Int1_beforeOrEqualDate = dataList.stream().mapToInt(data -> data.int1).sum();
history.sum_Int2_beforeOrEqualDate = dataList.stream().mapToInt(data -> data.int2).sum();
return history;
})
.collect(Collectors.toList());
Tell me if I got the logic correct.
What you could do is use Collections.reducing which works pretty good.
List<DataHistory> dataHistories =
list.stream()
.collect(Collectors.groupingBy(Data::getDate,
Collectors.reducing(DataHistory::new,
DataHistoryHelper::merge)))
.values();
This solution assumes you have a constructor in DataHistory taking a Data as parameter.
public DataHistory(Data o) {
this.date = o.getDate();
// and so on
}
And that you have a method (anywhere) that takes care of merging two DataHistory objects
public DataHistory merge(DataHistory o1, DataHistory o2) {
DataHistory merged = new DataHistory();
merged.setSum_Int1_beforeOrEqualDate(o1.getSum_Int1_beforeOrEqualDate + o2.getSum_Int1_beforeOrEqualDate);
// and so on
return merged;
}
Simple use map() method in stream api and collect results:
List<Emp> employe = Arrays.asList(new Emp(1, 100), new Emp(2, 200), new Emp(3, 300));
List<Emp> employeRise = employe.stream()
.map(emp -> new Emp(emp.getId(), emp.getSalary * 100))
.collect(Collectors.toList());
employeRise.stream()
.forEach(s -> System.out.println("Id :" + s.getId() + " Salary :" + s.getSalary()));
map() each Emp of the input List to a new Emp and then collect() to a List:
List<Emp> afterSalayHike =
empInList.stream()
.map(emp->new Emp(emp.getId(), emp.getSalary() * 100))
.collect(Collectors.toList());
You can use Map:
Map<String, Integer> otherMap = new HashMap<>(other.size());
int index = 0;
for(String otherElem : other) {
otherMap.put(otherElem, index++);
}
And then:
for(String oneElem : one) {
Integer index = otherMap.get(oneElem);
indices.add(index == null ? -1 : index);
}
Doing so, you get the index directly instead of iterating on a potentially very big list each time you look for and index.
You can use a HashMap<String, Integer> that would map every character to its position. Then use HashMap method .containsKey() to find out, if a certain String is present in the field and .get() to find out the position.
HashMap<String, Integer> another;
for (String str : one) {
if (another.contains(str)) {
result.add(another.get(str));
} else {
result.add(-1);
}
}
Don't worry about any performance differences, they're going to be minimal in this case normally.
Method 2 is preferable because
it doesn't require mutating a collection that exists outside the lambda expression.
it's more readable because the different steps that are performed in the collection pipeline are written sequentially: first a filter operation, then a map operation, then collecting the result (for more info on the benefits of collection pipelines, see Martin Fowler's excellent article.)
you can easily change the way values are collected by replacing the
Collectorthat is used. In some cases you may need to write your ownCollector, but then the benefit is that you can easily reuse that.
I agree with the existing answers that the second form is better because it does not have any side effects and is easier to parallelise (just use a parallel stream).
Performance wise, it appears they are equivalent until you start using parallel streams. In that case, map will perform really much better. See below the micro benchmark results:
Benchmark Mode Samples Score Error Units
SO28319064.forEach avgt 100 187.310 ± 1.768 ms/op
SO28319064.map avgt 100 189.180 ± 1.692 ms/op
SO28319064.mapWithParallelStream avgt 100 55,577 ± 0,782 ms/op
You can't boost the first example in the same manner because forEach is a terminal method - it returns void - so you are forced to use a stateful lambda. But that is really a bad idea if you are using parallel streams.
Finally note that your second snippet can be written in a sligthly more concise way with method references and static imports:
myFinalList = myListToParse.stream()
.filter(Objects::nonNull)
.map(this::doSomething)
.collect(toList());
List<Value> list = new ArrayList<Value>(map.values());
assuming:
Map<Key,Value> map;
The issue here is that Map has two values (a key and value), while a List only has one value (an element).
Therefore, the best that can be done is to either get a List of the keys or the values. (Unless we make a wrapper to hold on to the key/value pair).
Say we have a Map:
Map<String, String> m = new HashMap<String, String>();
m.put("Hello", "World");
m.put("Apple", "3.14");
m.put("Another", "Element");
The keys as a List can be obtained by creating a new ArrayList from a Set returned by the Map.keySet method:
List<String> list = new ArrayList<String>(m.keySet());
While the values as a List can be obtained creating a new ArrayList from a Collection returned by the Map.values method:
List<String> list = new ArrayList<String>(m.values());
The result of getting the List of keys:
Apple Another Hello
The result of getting the List of values:
3.14 Element World
There is no built-in way of doing this in Java - you have to write or use a helper class. Google Collections includes
public static <F,T> List<T> transform(List<F> fromList,
Function<? super F,? extends T> function)
This works well, but it's a bit clumsy to use, as you have to use a one-method anonymous class and a static method. This is no fault of Google Collections, it is just the nature of doing this type of task in Java.
Note that this lazily transforms items in the source list as needed.
I implemented something on the fly. See if this helps you. If not, use Google Collections as suggested.
public interface Func<E, T> {
T apply(E e);
}
public class CollectionUtils {
public static <T, E> List<T> transform(List<E> list, Func<E, T> f) {
if (null == list)
throw new IllegalArgumentException("null list");
if (null == f)
throw new IllegalArgumentException("null f");
List<T> transformed = new ArrayList<T>();
for (E e : list) {
transformed.add(f.apply(e));
}
return transformed;
}
}
List<CustomerDto> transformed = CollectionUtils.transform(l, new Func<Customer, CustomerDto>() {
@Override
public CustomerDto apply(Customer e) {
// return whatever !!!
}
});