简体   繁体   中英

How to merge ArrayList of Objects based on value in java 8?

I want to DB connections in a list from various sources. Some sources may/may not provide the Url.

I want to keep the records which has the complete info(ie dbName, connUrl, isActive) + if any db doesn't have the complete record with any of the sources.

Record.java,

String dbName;
String connUrl;
String isActiveYN;

MainApp.java,

List <Record> connRecords = new ArrayList<>();
connRecords = someFunc(sourceName) // some func takes source as param and returns connection List
connRecords.addAll(someFunc(sourceName));
connRecords.addAll(someFunc(sourceName));
//logic for getting all unique DBs

Eg: List 1 from Source Alpha: [{A,Y},{B,Y},{C, Y},{D,Y}]

List 2 from Source Beta: [{A, abc-url, Y}, {B, xyz-url, Y}]

List 3 from Source Gamma: [{A,Y},{C,Y}]

After merging all the list we get:

I/P List: [{A,Y}, {B,Y}, {C, Y}, {D,Y}, {A, abc-url, Y}, {B, xyz-url, Y}, {A,Y}, {C,Y}]

for dbName A, source Beta has the complete record info , so we'll take that

for dbName B, source Beta has the complete record info, so we'll take that

for dbName C, even none of the sources have the complete info, still we'll keep it since it's an available DB and some source might have the url connection at some point in future. for dbName D, even none of the sources have the complete info, still we'll keep it since it's an available DB and some source might have the url connection at some point in future.

Required O/P List: [{A,abc-url,Y}, {B,xyz-url,Y}, {C,Y},{D,Y}]

Can anyone suggest a java 8 way (using streams) way of doing this?

old school way of doing this:

Map<String, Record> map = new HashMap<>();

        for (Record record: recordList) {
            String dbName = record.getDBName();
            String isActiveYN = record.getIsActiveYN();
            if (map.containsKey(dbName)) {
                if(record.getConnURL() != null)
                  map.replace(dbName, record);
            } else {
                map.put(dbName, record);
            }
        }

List<Record> merged = new ArrayList<>(map.values());

It can be implemented using by implementing a couple of merge functions:

  1. merge two Record instances into one
  2. merge two String instances into one
  3. override toString method to provide desired output
  4. fix setting of isActiveN : if merge needed, then set to Y
// using Lombok annotations for brevity
@Data
@AllArgsConstructor
@EqualsAndHashCode
class Record {
    String dbName;
    String connUrl;
    String isActiveYN;

    Record merge(Record other) {
        if (this.dbName.equals(other.dbName)) {
            this.connUrl = merge(this.connUrl, other.connUrl);
            this.isActiveYN = "Y"; // if merge called, there are at least two entries
        }
        return this;
    }

    static String merge(String thiss, String thats) {
        return thiss == null ^ thats == null ? (thiss == null ? thats : thiss) : thiss;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        String sep = "";
        if (null != dbName) {
            sb.append(dbName);
            sep = ", ";
        }
        if (null != connUrl) {
            sb.append(sep).append(connUrl);
            sep = ", ";
        }
        if (null != isActiveYN) {
            sb.append(sep).append(isActiveYN);
        }
        sb.insert(0, '{');
        sb.append('}');
        return sb.toString();
    }
}

Then the records may be merged as simply as using Collectors.toMap collector and pass reference to Record::merge as the third parameter:

// Source Alpha
List<Record> connRecords = new ArrayList<>(Arrays.asList(
        new Record("A", null, "N"),
        new Record("B", null, "Y"),
        new Record("C", null, null),
        new Record("D", null, "N")
));

// Source Beta
connRecords.addAll(Arrays.asList(
        new Record("A", "abc-url", null),
        new Record("B", "xyz-url", "N")
));

// Source Gamma
connRecords.addAll(Arrays.asList(
        new Record("A", null, "N"),
        new Record("C", null, "N")
));

List<Record> merged = new ArrayList<>(
        connRecords.stream()
                   .collect(Collectors.toMap(Record::getDbName, rec -> rec, Record::merge))
                   .values()
);
System.out.println(merged);

Output:

[{A, abc-url, Y}, {B, xyz-url, Y}, {C, Y}, {D, N}]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM