使用 Stream API 从 HashMap 创建列表,元素按特定顺序排列
Use the Stream API to create List from HashMap with elements arranged in specific order
我有一个 HashMap<String, List<Appliance>>
,其中对象 Appliance
的字段 name::String
用作键,HashMap
中的每个值都是 list
个 Appliance
个对象。每个列表根据 Appliance
对象的字段 "price::BigDecimal" 升序排序。我想创建一个 ArrayList<Appliance>
,使用 Stream API
,并通过提取预先存在的 HashMap
,首先提取 HashMap
中每个列表的第一个元素,然后是第二个,等等
所以如果 HashMap
有这些内容:
["Fridge", [<"Fridge", 100>, <"Fridge", 200>, <"Fridge", 300>],
"Oven", [<"Oven", 150>, <"Oven", 250>, <"Oven", 350>],
"DishWasher", [<"DishWasher", 220>, <"DishWasher", 320>, <"DishWasher", 420>]]
我希望最终名单如下:
[<"Fridge", 100>,
<"Oven", 150>,
<"DishWasher", 220>,
<"Fridge", 200>,
<"Oven", 250>,
<"DishWasher", 320>,
<"Fridge", 300>,
<"Oven", 350>,
<"DishWasher", 420>]
是否可以使用 Java 的 8 Stream API 以实用的方式做到这一点?
这是我的代码。我想以声明的方式实现相同的结果。
while(!appliancesMap.isEmpty()) {
for (Map.Entry<String, List<Appliance>> entry :
appliancesMap.entrySet()) {
String key = entry.getKey();
List<Appliance> value = entry.getValue();
finalList.add(value.get(0));
value.remove(0);
if (value.size() == 0) {
appliancesMap.entrySet()
.removeIf(predicate -> predicate.getKey().equals(key));
} else {
appliancesMap.replace(key, value);
}
}
}
map.keySet().stream().map(map::get).forEach(list::addAll);
.addAll()
.stream()
可以胜任。
现在您已经拥有列表中的所有元素,您可以对其进行排序:
list.sort(Comparator.comparing(Object::toString)
.thenComparingInt(s -> Integer.parseInt(
s.toString()
.substring(0, s.toString().length() - 1)
.split(",")[1].trim())));
步骤:
- 找出地图中最长列表的大小。这可以作为
map.keySet().stream().mapToInt(k -> map.get(k).size()).max().getAsInt()
- 使用
IntStream
迭代步骤#1 中获得的从 0 到最大大小的值
IntStream.range(0, map.keySet().stream().mapToInt(k -> map.get(k).size()).max().getAsInt())
- 使用
IntStream
的每个值(比如 i
)作为索引从列表中获取元素,例如如果 i = 0
,从地图中的每个列表中获取索引 0
处的元素并添加到 result
列表
List<Appliance> result = new ArrayList<>();
IntStream.range(0, map.keySet().stream().mapToInt(k -> map.get(k).size()).max().getAsInt())
.forEach(i -> map
.keySet()
.stream()
.filter(key -> i < map.get(key).size())
.forEach(k -> result.add(map.get(k).get(i))));
演示
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;
class Appliance {
private String name;
private double price;
public Appliance(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Appliance [name=" + name + ", price=" + price + "]";
}
}
public class Main {
public static void main(String[] args) {
Map<String, List<Appliance>> map = Map.of("Fridge",
List.of(new Appliance("Fridge", 100), new Appliance("Fridge", 200), new Appliance("Fridge", 300)),
"Oven", List.of(new Appliance("Oven", 150), new Appliance("Oven", 250), new Appliance("Oven", 350)),
"DishWasher", List.of(new Appliance("DishWasher", 220), new Appliance("DishWasher", 320),
new Appliance("DishWasher", 420)));
List<Appliance> result = new ArrayList<>();
IntStream.range(0, map.keySet().stream().mapToInt(k -> map.get(k).size()).max().getAsInt())
.forEach(i -> map
.keySet()
.stream()
.filter(key -> i < map.get(key).size())
.forEach(k -> result.add(map.get(k).get(i))));
// Display
result.forEach(System.out::println);
}
}
输出:
Appliance [name=Fridge, price=100.0]
Appliance [name=Oven, price=150.0]
Appliance [name=DishWasher, price=220.0]
Appliance [name=Fridge, price=200.0]
Appliance [name=Oven, price=250.0]
Appliance [name=DishWasher, price=320.0]
Appliance [name=Fridge, price=300.0]
Appliance [name=Oven, price=350.0]
Appliance [name=DishWasher, price=420.0]
[更新]
下面给出了解决方案的惯用代码(感谢 ):
List<Appliance> result = IntStream.range(0, map.values().stream().mapToInt(List::size).max().getAsInt())
.mapToObj(i -> map.values()
.stream()
.filter(list -> i < list.size())
.map(list -> list.get(i)))
.flatMap(Function.identity()).collect(Collectors.toList());
如果您不介意顺序 Fridge -> Oven -> DishWasher
,下面的代码对您有帮助:
map.values().stream().flatMap((Function<List<Appliance>, Stream<Appliance>>) Collection::stream)
.collect(Collectors.groupingBy(appliance -> {
List<Appliance> appliances = map.get(appliance.getName());
for (int i = 0;i<appliances.size();i++) {
if (appliance.getPrice() == appliances.get(i).getPrice()) {
return i;
}
}
return 0;
})).values().stream().flatMap((Function<List<Appliance>, Stream<Appliance>>) Collection::stream)
.forEach(System.out::println);
我有一个 HashMap<String, List<Appliance>>
,其中对象 Appliance
的字段 name::String
用作键,HashMap
中的每个值都是 list
个 Appliance
个对象。每个列表根据 Appliance
对象的字段 "price::BigDecimal" 升序排序。我想创建一个 ArrayList<Appliance>
,使用 Stream API
,并通过提取预先存在的 HashMap
,首先提取 HashMap
中每个列表的第一个元素,然后是第二个,等等
所以如果 HashMap
有这些内容:
["Fridge", [<"Fridge", 100>, <"Fridge", 200>, <"Fridge", 300>],
"Oven", [<"Oven", 150>, <"Oven", 250>, <"Oven", 350>],
"DishWasher", [<"DishWasher", 220>, <"DishWasher", 320>, <"DishWasher", 420>]]
我希望最终名单如下:
[<"Fridge", 100>,
<"Oven", 150>,
<"DishWasher", 220>,
<"Fridge", 200>,
<"Oven", 250>,
<"DishWasher", 320>,
<"Fridge", 300>,
<"Oven", 350>,
<"DishWasher", 420>]
是否可以使用 Java 的 8 Stream API 以实用的方式做到这一点?
这是我的代码。我想以声明的方式实现相同的结果。
while(!appliancesMap.isEmpty()) {
for (Map.Entry<String, List<Appliance>> entry :
appliancesMap.entrySet()) {
String key = entry.getKey();
List<Appliance> value = entry.getValue();
finalList.add(value.get(0));
value.remove(0);
if (value.size() == 0) {
appliancesMap.entrySet()
.removeIf(predicate -> predicate.getKey().equals(key));
} else {
appliancesMap.replace(key, value);
}
}
}
map.keySet().stream().map(map::get).forEach(list::addAll);
.addAll()
.stream()
可以胜任。
现在您已经拥有列表中的所有元素,您可以对其进行排序:
list.sort(Comparator.comparing(Object::toString)
.thenComparingInt(s -> Integer.parseInt(
s.toString()
.substring(0, s.toString().length() - 1)
.split(",")[1].trim())));
步骤:
- 找出地图中最长列表的大小。这可以作为
map.keySet().stream().mapToInt(k -> map.get(k).size()).max().getAsInt()
- 使用
IntStream
迭代步骤#1 中获得的从 0 到最大大小的值
IntStream.range(0, map.keySet().stream().mapToInt(k -> map.get(k).size()).max().getAsInt())
- 使用
IntStream
的每个值(比如i
)作为索引从列表中获取元素,例如如果i = 0
,从地图中的每个列表中获取索引0
处的元素并添加到result
列表
List<Appliance> result = new ArrayList<>();
IntStream.range(0, map.keySet().stream().mapToInt(k -> map.get(k).size()).max().getAsInt())
.forEach(i -> map
.keySet()
.stream()
.filter(key -> i < map.get(key).size())
.forEach(k -> result.add(map.get(k).get(i))));
演示
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;
class Appliance {
private String name;
private double price;
public Appliance(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Appliance [name=" + name + ", price=" + price + "]";
}
}
public class Main {
public static void main(String[] args) {
Map<String, List<Appliance>> map = Map.of("Fridge",
List.of(new Appliance("Fridge", 100), new Appliance("Fridge", 200), new Appliance("Fridge", 300)),
"Oven", List.of(new Appliance("Oven", 150), new Appliance("Oven", 250), new Appliance("Oven", 350)),
"DishWasher", List.of(new Appliance("DishWasher", 220), new Appliance("DishWasher", 320),
new Appliance("DishWasher", 420)));
List<Appliance> result = new ArrayList<>();
IntStream.range(0, map.keySet().stream().mapToInt(k -> map.get(k).size()).max().getAsInt())
.forEach(i -> map
.keySet()
.stream()
.filter(key -> i < map.get(key).size())
.forEach(k -> result.add(map.get(k).get(i))));
// Display
result.forEach(System.out::println);
}
}
输出:
Appliance [name=Fridge, price=100.0]
Appliance [name=Oven, price=150.0]
Appliance [name=DishWasher, price=220.0]
Appliance [name=Fridge, price=200.0]
Appliance [name=Oven, price=250.0]
Appliance [name=DishWasher, price=320.0]
Appliance [name=Fridge, price=300.0]
Appliance [name=Oven, price=350.0]
Appliance [name=DishWasher, price=420.0]
[更新]
下面给出了解决方案的惯用代码(感谢
List<Appliance> result = IntStream.range(0, map.values().stream().mapToInt(List::size).max().getAsInt())
.mapToObj(i -> map.values()
.stream()
.filter(list -> i < list.size())
.map(list -> list.get(i)))
.flatMap(Function.identity()).collect(Collectors.toList());
如果您不介意顺序 Fridge -> Oven -> DishWasher
,下面的代码对您有帮助:
map.values().stream().flatMap((Function<List<Appliance>, Stream<Appliance>>) Collection::stream)
.collect(Collectors.groupingBy(appliance -> {
List<Appliance> appliances = map.get(appliance.getName());
for (int i = 0;i<appliances.size();i++) {
if (appliance.getPrice() == appliances.get(i).getPrice()) {
return i;
}
}
return 0;
})).values().stream().flatMap((Function<List<Appliance>, Stream<Appliance>>) Collection::stream)
.forEach(System.out::println);