如何高效合并Java中List<FinalValue>为单个FinalValue对象?
Hey, let's tackle this merging problem properly. Instead of writing messy nested loops manually, we can leverage Java Stream API plus custom merging logic to make the code cleaner, more readable, and maintainable. First, let's clarify the class structure with necessary additions (getters, setters, and equals/hashCode are critical for merging):
import java.math.BigDecimal; import java.util.*; class FinalValue { private List<Item> items; private List<Option> options; // Getters and Setters public List<Item> getItems() { return items; } public void setItems(List<Item> items) { this.items = items; } public List<Option> getOptions() { return options; } public void setOptions(List<Option> options) { this.options = options; } static class Item { private String itemId; private Type type; private Rate rate; private String currency; // Getters and Setters public String getItemId() { return itemId; } public void setItemId(String itemId) { this.itemId = itemId; } public Type getType() { return type; } public void setType(Type type) { this.type = type; } public Rate getRate() { return rate; } public void setRate(Rate rate) { this.rate = rate; } public String getCurrency() { return currency; } public void setCurrency(String currency) { this.currency = currency; } // Equals and HashCode for deduplication @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Item item = (Item) o; return Objects.equals(itemId, item.itemId) && Objects.equals(type, item.type) && Objects.equals(rate, item.rate) && Objects.equals(currency, item.currency); } @Override public int hashCode() { return Objects.hash(itemId, type, rate, currency); } } static class Option { private String optionId; private Type type; private Rate rate; private String currency; // Getters and Setters public String getOptionId() { return optionId; } public void setOptionId(String optionId) { this.optionId = optionId; } public Type getType() { return type; } public void setType(Type type) { this.type = type; } public Rate getRate() { return rate; } public void setRate(Rate rate) { this.rate = rate; } public String getCurrency() { return currency; } public void setCurrency(String currency) { this.currency = currency; } // Equals and HashCode for deduplication @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Option option = (Option) o; return Objects.equals(optionId, option.optionId) && Objects.equals(type, option.type) && Objects.equals(rate, option.rate) && Objects.equals(currency, option.currency); } @Override public int hashCode() { return Objects.hash(optionId, type, rate, currency); } } static class Rate { private Integer id; private BigDecimal rateAmount; private Boolean isActive; // Getters and Setters public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public BigDecimal getRateAmount() { return rateAmount; } public void setRateAmount(BigDecimal rateAmount) { this.rateAmount = rateAmount; } public Boolean getIsActive() { return isActive; } public void setIsActive(Boolean isActive) { this.isActive = isActive; } // Equals and HashCode for grouping/deduplication @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Rate rate = (Rate) o; return Objects.equals(id, rate.id) && Objects.equals(rateAmount, rate.rateAmount) && Objects.equals(isActive, rate.isActive); } @Override public int hashCode() { return Objects.hash(id, rateAmount, isActive); } } enum Type { TYPE_A, TYPE_B // Add your actual enum values here } }
Core Merging Logic
The approach follows three key steps:
- Collect all nested
Item/Optionobjects from everyFinalValuein the list - Group them by their unique identifier (e.g.,
itemIdforItem,optionIdforOption) - Merge each group using your rule: keep one instance if most entries agree, keep all unique variations if there's disagreement
Step 1: Generic Helper Methods
First, let's create reusable utilities to handle frequency counting and consistency checks:
import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; // Get the most frequent value from a frequency map private static <T> T getMajorityValue(Map<T, Long> frequencyMap) { return frequencyMap.entrySet().stream() .max(Map.Entry.comparingByValue()) .map(Map.Entry::getKey) .orElse(null); } // Check if all entries in a frequency map have the same value (no disagreements) private static <T> boolean hasSingleValue(Map<T, Long> frequencyMap) { return frequencyMap.size() == 1; }
Step 2: Merge Item Groups
Now implement the merging logic for Item lists (the Option logic will be nearly identical):
import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; // Merge all items from multiple FinalValue objects private static List<FinalValue.Item> mergeItems(List<FinalValue.Item> allItems) { return allItems.stream() // Group items by their unique itemId .collect(Collectors.groupingBy(FinalValue.Item::getItemId)) .values().stream() // Merge each group of items with the same itemId .map(ItemMerger::mergeItemGroup) // Flatten the list of merged groups (each group might return 1 or multiple items) .flatMap(List::stream) .collect(Collectors.toList()); } // Merge a group of items with the same itemId private static List<FinalValue.Item> mergeItemGroup(List<FinalValue.Item> itemGroup) { // Count frequency of each attribute in the group Map<FinalValue.Type, Long> typeFreq = itemGroup.stream() .collect(Collectors.groupingBy(FinalValue.Item::getType, Collectors.counting())); Map<FinalValue.Rate, Long> rateFreq = itemGroup.stream() .collect(Collectors.groupingBy(FinalValue.Item::getRate, Collectors.counting())); Map<String, Long> currencyFreq = itemGroup.stream() .collect(Collectors.groupingBy(FinalValue.Item::getCurrency, Collectors.counting())); // Check if all attributes are consistent across the group boolean allConsistent = hasSingleValue(typeFreq) && hasSingleValue(rateFreq) && hasSingleValue(currencyFreq); if (allConsistent) { // All items in the group are identical: return one instance return Collections.singletonList(itemGroup.get(0)); } else { // There are disagreements: return all unique attribute combinations return itemGroup.stream() .distinct() .collect(Collectors.toList()); } }
Step 3: Merge Option Groups
Copy the Item merging logic and adjust for Option (replace Item with Option and itemId with optionId):
private static List<FinalValue.Option> mergeOptions(List<FinalValue.Option> allOptions) { return allOptions.stream() .collect(Collectors.groupingBy(FinalValue.Option::getOptionId)) .values().stream() .map(OptionMerger::mergeOptionGroup) .flatMap(List::stream) .collect(Collectors.toList()); } private static List<FinalValue.Option> mergeOptionGroup(List<FinalValue.Option> optionGroup) { Map<FinalValue.Type, Long> typeFreq = optionGroup.stream() .collect(Collectors.groupingBy(FinalValue.Option::getType, Collectors.counting())); Map<FinalValue.Rate, Long> rateFreq = optionGroup.stream() .collect(Collectors.groupingBy(FinalValue.Option::getRate, Collectors.counting())); Map<String, Long> currencyFreq = optionGroup.stream() .collect(Collectors.groupingBy(FinalValue.Option::getCurrency, Collectors.counting())); boolean allConsistent = hasSingleValue(typeFreq) && hasSingleValue(rateFreq) && hasSingleValue(currencyFreq); if (allConsistent) { return Collections.singletonList(optionGroup.get(0)); } else { return optionGroup.stream() .distinct() .collect(Collectors.toList()); } }
Step 4: Merge the Entire List of FinalValue
Tie everything together to merge the full list into one FinalValue object:
import java.util.List; import java.util.stream.Collectors; public static FinalValue mergeFinalValues(List<FinalValue> finalValues) { // Collect all items from all FinalValue objects List<FinalValue.Item> allItems = finalValues.stream() .map(FinalValue::getItems) .flatMap(List::stream) .collect(Collectors.toList()); // Collect all options from all FinalValue objects List<FinalValue.Option> allOptions = finalValues.stream() .map(FinalValue::getOptions) .flatMap(List::stream) .collect(Collectors.toList()); // Merge items and options List<FinalValue.Item> mergedItems = mergeItems(allItems); List<FinalValue.Option> mergedOptions = mergeOptions(allOptions); // Create the merged FinalValue object FinalValue mergedFinalValue = new FinalValue(); mergedFinalValue.setItems(mergedItems); mergedFinalValue.setOptions(mergedOptions); return mergedFinalValue; }
Customization Notes
- Adjust "Majority" Definition: If you need "majority" to mean more than 50% (not just most frequent), modify
getMajorityValueto check if the top frequency exceedsitemGroup.size() / 2. - Generate Full Attribute Combinations: If "keep all possible values" means creating every combination of differing attributes (instead of keeping unique original items), replace the
distinct()step with code to generate a Cartesian product of all unique attribute values. - Strategy Pattern: For maximum flexibility, encapsulate different merging rules into separate strategy classes (e.g.,
ConsistentMergeStrategy,FullVariationMergeStrategy) and switch them as needed.
内容的提问来源于stack exchange,提问作者Amit

