Java 8 में कई फ़ील्ड नामों से समूह


95

मुझे POJO के कुछ फ़ील्ड नाम से ऑब्जेक्ट्स को समूहीकृत करने के लिए कोड मिला। नीचे उस के लिए कोड है:

public class Temp {

    static class Person {

        private String name;
        private int age;
        private long salary;

        Person(String name, int age, long salary) {

            this.name = name;
            this.age = age;
            this.salary = salary;
        }

        @Override
        public String toString() {
            return String.format("Person{name='%s', age=%d, salary=%d}", name, age, salary);
        }
    }

    public static void main(String[] args) {
        Stream<Person> people = Stream.of(new Person("Paul", 24, 20000),
                new Person("Mark", 30, 30000),
                new Person("Will", 28, 28000),
                new Person("William", 28, 28000));
        Map<Integer, List<Person>> peopleByAge;
        peopleByAge = people
                .collect(Collectors.groupingBy(p -> p.age, Collectors.mapping((Person p) -> p, toList())));
        System.out.println(peopleByAge);
    }
}

और आउटपुट है (जो सही है):

{24=[Person{name='Paul', age=24, salary=20000}], 28=[Person{name='Will', age=28, salary=28000}, Person{name='William', age=28, salary=28000}], 30=[Person{name='Mark', age=30, salary=30000}]}

लेकिन क्या होगा अगर मैं कई क्षेत्रों द्वारा समूह बनाना चाहता हूं? मैं स्पष्ट रूप से उस POJO में groupingBy()विधि को लागू करने के बाद कुछ POJO पारित कर सकता हूं, equals()लेकिन क्या कोई अन्य विकल्प है जैसे कि मैं दिए गए POJO से एक से अधिक क्षेत्रों द्वारा समूह बना सकता हूं?

जैसे मेरे मामले में, मैं नाम और उम्र के आधार पर समूह बनाना चाहता हूं।


1
सभी क्षेत्रों से एक अद्वितीय स्ट्रिंग उत्पन्न करने के लिए एक चाल है।
मार्को टोपोलनिक

3
mappingएक डाउनस्ट्रीम कलेक्टर के रूप में BTW आपके द्वारा पोस्ट किए गए कोड में बेमानी है।
मार्को टोपोलनिक

8
त्वरित और गंदा समाधान है people.collect(groupingBy(p -> Arrays.asList(p.name, p.age)))
मिशा

जवाबों:


170

आपके पास यहां कुछ विकल्प हैं। सबसे सरल आपके कलेक्टरों की श्रृंखला है:

Map<String, Map<Integer, List<Person>>> map = people
    .collect(Collectors.groupingBy(Person::getName,
        Collectors.groupingBy(Person::getAge));

फिर फ्रेड नामक 18 वर्षीय लोगों की एक सूची प्राप्त करने के लिए आप उपयोग करेंगे:

map.get("Fred").get(18);

एक दूसरा विकल्प एक वर्ग को परिभाषित करना है जो समूहन का प्रतिनिधित्व करता है। यह व्यक्ति के अंदर हो सकता है। यह कोड JEP 359 जोड़े जाने से पहले जावा के संस्करणों recordमें आसानी से एक वर्ग (के साथ equalsऔर hashCodeपरिभाषित) हो सकता है लेकिन यह आसानी से उपयोग हो सकता है :

class Person {
    record NameAge(String name, int age) { }

    public NameAge getNameAge() {
        return new NameAge(name, age);
    }
}

तो आप का उपयोग कर सकते हैं:

Map<NameAge, List<Person>> map = people.collect(Collectors.groupingBy(Person::getNameAge));

और साथ खोजें

map.get(new NameAge("Fred", 18));

अंत में यदि आप अपने स्वयं के समूह रिकॉर्ड को लागू नहीं करना चाहते हैं तो आसपास के कई जावा फ्रेमवर्क में pairइस प्रकार की चीज़ के लिए डिज़ाइन किया गया एक वर्ग है। उदाहरण के लिए: अपाचे कॉमन्स जोड़ी यदि आप इन पुस्तकालयों में से किसी एक का उपयोग करते हैं तो आप मानचित्र की कुंजी को नाम की एक जोड़ी बना सकते हैं:

Map<Pair<String, Integer>, List<Person>> map =
    people.collect(Collectors.groupingBy(p -> Pair.of(p.getName(), p.getAge())));

और इसके साथ पुनः प्राप्त करें:

map.get(Pair.of("Fred", 18));

व्यक्तिगत रूप से मैं वास्तव में जेनेरिक ट्यूपल्स में बहुत अधिक मूल्य नहीं देखता हूं कि रिकॉर्ड भाषा में उपलब्ध हैं क्योंकि रिकॉर्ड बेहतर इरादे प्रदर्शित करते हैं और बहुत कम कोड की आवश्यकता होती है।


5
Function<T,U>इस अर्थ में भी आशय छिपाता है --- लेकिन आप किसी को भी प्रत्येक मैपिंग चरण के लिए अपने स्वयं के कार्यात्मक इंटरफ़ेस की घोषणा करते नहीं देखेंगे; इरादा पहले से ही लैम्ब्डा शरीर में है। टुपल्स के साथ भी: वे एपीआई घटकों के बीच गोंद प्रकार के रूप में महान हैं। BTW स्काला के केस क्लासेस IMHO हैं जो संक्षिप्तता और इरादे दोनों के प्रदर्शन के मामले में एक बड़ी जीत हैं।
मार्को टोपोलनिक

1
हां मैं आपकी बात देखता हूं। मुझे लगता है (हमेशा की तरह) यह निर्भर करता है कि उनका उपयोग कैसे किया जाता है। उदाहरण मैंने ऊपर दिया - मानचित्र की कुंजी के रूप में एक जोड़ी का उपयोग करना - यह कैसे करना है, इसका एक अच्छा उदाहरण है। मैं स्काला से बहुत परिचित नहीं हूँ - मुझे अच्छी बातें सुनते ही इसे सीखना शुरू करना होगा।
स्प्रिंट

1
बस की घोषणा करने में सक्षम होने की कल्पना NameAgeएक एक लाइनर के रूप में: case class NameAge { val name: String; val age: Int }--- और आपको मिल equals, hashCodeऔर toString!
बजे मार्को टोपोलनिक

1
अच्छा - मेरी 'पर एक और आइटम' कतार करना चाहिए। यह दुर्भाग्य से फीफो है!
स्प्रिंट

@sprinter प्रथम कोड स्निपेट का प्रकार सही नहीं है और इसे बदल दिया जाना चाहिएMap<String, Map<Integer, List<Person>>> map
कसूर

39

यहाँ कोड देखें:

आप बस एक फंक्शन बना सकते हैं और इसे आपके लिए काम कर सकते हैं, तरह तरह की फंक्शनल स्टाइल!

Function<Person, List<Object>> compositeKey = personRecord ->
    Arrays.<Object>asList(personRecord.getName(), personRecord.getAge());

अब आप इसे मानचित्र के रूप में उपयोग कर सकते हैं:

Map<Object, List<Person>> map =
people.collect(Collectors.groupingBy(compositeKey, Collectors.toList()));

चीयर्स!


2
मैंने इस समाधान का उपयोग किया लेकिन अलग। फंक्शन <व्यक्ति, स्ट्रिंग> कंपोजिट = personRecord -> StringUtils.join (personRecord.getName (), personRecord.getAge ());
5

8

groupingByविधि पहले पैरामीटर है Function<T,K>जहां:

@ अपरम <T>इनपुट तत्वों का प्रकार

@ कपम <K>के प्रकार

यदि हम आपके कोड में अनाम वर्ग के साथ लैम्ब्डा को प्रतिस्थापित करते हैं, तो हम कुछ इस प्रकार देख सकते हैं:

people.stream().collect(Collectors.groupingBy(new Function<Person, int>() {
            @Override
            public int apply(Person person) {
                return person.getAge();
            }
        }));

बस अब आउटपुट पैरामीटर बदलें <K>। इस मामले में, उदाहरण के लिए, मैंने नाम और उम्र के आधार पर समूहीकरण के लिए org.apache.commons.lang3.tuple से एक जोड़ी वर्ग का उपयोग किया, लेकिन आप अपनी आवश्यकतानुसार फ़िल्टरिंग समूहों के लिए अपनी कक्षा बना सकते हैं।

people.stream().collect(Collectors.groupingBy(new Function<Person, Pair<Integer, String>>() {
                @Override
                public YourFilter apply(Person person) {
                    return Pair.of(person.getAge(), person.getName());
                }
            }));

अंत में, लैम्ब्डा बैक के साथ बदलने के बाद, कोड इस तरह दिखता है:

Map<Pair<Integer,String>, List<Person>> peopleByAgeAndName = people.collect(Collectors.groupingBy(p -> Pair.of(person.getAge(), person.getName()), Collectors.mapping((Person p) -> p, toList())));

उपयोग करने के बारे में क्या List<String>?
एलेक्स78191

7

नमस्ते, आप बस अपने groupingByKeyजैसे को सम्‍मिलित कर सकते हैं

Map<String, List<Person>> peopleBySomeKey = people
                .collect(Collectors.groupingBy(p -> getGroupingByKey(p), Collectors.mapping((Person p) -> p, toList())));



//write getGroupingByKey() function
private String getGroupingByKey(Person p){
return p.getAge()+"-"+p.getName();
}

2

अपने समूह में मुख्य परिभाषा के लिए एक वर्ग को परिभाषित करें।

class KeyObj {

    ArrayList<Object> keys;

    public KeyObj( Object... objs ) {
        keys = new ArrayList<Object>();

        for (int i = 0; i < objs.length; i++) {
            keys.add( objs[i] );
        }
    }

    // Add appropriate isEqual() ... you IDE should generate this

}

अब आपके कोड में,

peopleByManyParams = people
            .collect(Collectors.groupingBy(p -> new KeyObj( p.age, p.other1, p.other2 ), Collectors.mapping((Person p) -> p, toList())));

3
यह सिर्फ पुनर्बलन है Ararys.asList()--- जो ओटी के मामले के लिए बीटीडब्ल्यू एक अच्छा विकल्प है।
मार्को टोपोलनिक

और Pairदूसरे उदाहरण में उल्लिखित उदाहरण के समान , लेकिन तर्क सीमा के बिना।
बेनी बोटेमा

साथ ही आपको इसे अपरिवर्तनीय बनाने की आवश्यकता है। (और hashCodeएक बार गणना )
रोबू

2

आप कई क्षेत्रों के लिए एक क्लासिफायरियर के रूप में सूची का उपयोग कर सकते हैं, लेकिन आपको वैकल्पिक में लपेटने योग्य मानों की आवश्यकता है:

Function<String, List> classifier = (item) -> List.of(
    item.getFieldA(),
    item.getFieldB(),
    Optional.ofNullable(item.getFieldC())
);

Map<List, List<Item>> grouped = items.stream()
    .collect(Collectors.groupingBy(classifier));

1

मुझे एक कैटरिंग फर्म के लिए रिपोर्ट बनाने की आवश्यकता थी जो विभिन्न ग्राहकों के लिए लंच परोसता है। दूसरे शब्दों में, खानपान में अधिक या अधिक फर्में हो सकती हैं जो खानपान से आदेश लेती हैं, और यह जानना चाहिए कि हर एक ग्राहक के लिए हर एक दिन में कितने लंच का उत्पादन करना चाहिए!

सिर्फ नोटिस करने के लिए, मैंने इस उदाहरण को जटिल नहीं करने के लिए, छँटाई का उपयोग नहीं किया।

यह मेरा कोड है:

@Test
public void test_2() throws Exception {
    Firm catering = DS.firm().get(1);
    LocalDateTime ldtFrom = LocalDateTime.of(2017, Month.JANUARY, 1, 0, 0);
    LocalDateTime ldtTo = LocalDateTime.of(2017, Month.MAY, 2, 0, 0);
    Date dFrom = Date.from(ldtFrom.atZone(ZoneId.systemDefault()).toInstant());
    Date dTo = Date.from(ldtTo.atZone(ZoneId.systemDefault()).toInstant());

    List<PersonOrders> LON = DS.firm().getAllOrders(catering, dFrom, dTo, false);
    Map<Object, Long> M = LON.stream().collect(
            Collectors.groupingBy(p
                    -> Arrays.asList(p.getDatum(), p.getPerson().getIdfirm(), p.getIdProduct()),
                    Collectors.counting()));

    for (Map.Entry<Object, Long> e : M.entrySet()) {
        Object key = e.getKey();
        Long value = e.getValue();
        System.err.println(String.format("Client firm :%s, total: %d", key, value));
    }
}

0

यह है कि मैंने कई फील्ड्स ब्रांचकोड और prdId द्वारा समूहीकरण किया, बस जरूरत में किसी के लिए पोस्ट करना

    import java.math.BigDecimal;
    import java.math.BigInteger;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;

    /**
     *
     * @author charudatta.joshi
     */
    public class Product1 {

        public BigInteger branchCode;
        public BigInteger prdId;
        public String accountCode;
        public BigDecimal actualBalance;
        public BigDecimal sumActBal;
        public BigInteger countOfAccts;

        public Product1() {
        }

        public Product1(BigInteger branchCode, BigInteger prdId, String accountCode, BigDecimal actualBalance) {
            this.branchCode = branchCode;
            this.prdId = prdId;
            this.accountCode = accountCode;
            this.actualBalance = actualBalance;
        }

        public BigInteger getCountOfAccts() {
            return countOfAccts;
        }

        public void setCountOfAccts(BigInteger countOfAccts) {
            this.countOfAccts = countOfAccts;
        }

        public BigDecimal getSumActBal() {
            return sumActBal;
        }

        public void setSumActBal(BigDecimal sumActBal) {
            this.sumActBal = sumActBal;
        }

        public BigInteger getBranchCode() {
            return branchCode;
        }

        public void setBranchCode(BigInteger branchCode) {
            this.branchCode = branchCode;
        }

        public BigInteger getPrdId() {
            return prdId;
        }

        public void setPrdId(BigInteger prdId) {
            this.prdId = prdId;
        }

        public String getAccountCode() {
            return accountCode;
        }

        public void setAccountCode(String accountCode) {
            this.accountCode = accountCode;
        }

        public BigDecimal getActualBalance() {
            return actualBalance;
        }

        public void setActualBalance(BigDecimal actualBalance) {
            this.actualBalance = actualBalance;
        }

        @Override
        public String toString() {
            return "Product{" + "branchCode:" + branchCode + ", prdId:" + prdId + ", accountCode:" + accountCode + ", actualBalance:" + actualBalance + ", sumActBal:" + sumActBal + ", countOfAccts:" + countOfAccts + '}';
        }

        public static void main(String[] args) {
            List<Product1> al = new ArrayList<Product1>();
            System.out.println(al);
            al.add(new Product1(new BigInteger("01"), new BigInteger("11"), "001", new BigDecimal("10")));
            al.add(new Product1(new BigInteger("01"), new BigInteger("11"), "002", new BigDecimal("10")));
            al.add(new Product1(new BigInteger("01"), new BigInteger("12"), "003", new BigDecimal("10")));
            al.add(new Product1(new BigInteger("01"), new BigInteger("12"), "004", new BigDecimal("10")));
            al.add(new Product1(new BigInteger("01"), new BigInteger("12"), "005", new BigDecimal("10")));
            al.add(new Product1(new BigInteger("01"), new BigInteger("13"), "006", new BigDecimal("10")));
            al.add(new Product1(new BigInteger("02"), new BigInteger("11"), "007", new BigDecimal("10")));
            al.add(new Product1(new BigInteger("02"), new BigInteger("11"), "008", new BigDecimal("10")));
            al.add(new Product1(new BigInteger("02"), new BigInteger("12"), "009", new BigDecimal("10")));
            al.add(new Product1(new BigInteger("02"), new BigInteger("12"), "010", new BigDecimal("10")));
            al.add(new Product1(new BigInteger("02"), new BigInteger("12"), "011", new BigDecimal("10")));
            al.add(new Product1(new BigInteger("02"), new BigInteger("13"), "012", new BigDecimal("10")));
            //Map<BigInteger, Long> counting = al.stream().collect(Collectors.groupingBy(Product1::getBranchCode, Collectors.counting()));
            // System.out.println(counting);

            //group by branch code
            Map<BigInteger, List<Product1>> groupByBrCd = al.stream().collect(Collectors.groupingBy(Product1::getBranchCode, Collectors.toList()));
            System.out.println("\n\n\n" + groupByBrCd);

             Map<BigInteger, List<Product1>> groupByPrId = null;
              // Create a final List to show for output containing one element of each group
            List<Product> finalOutputList = new LinkedList<Product>();
            Product newPrd = null;
            // Iterate over resultant  Map Of List
            Iterator<BigInteger> brItr = groupByBrCd.keySet().iterator();
            Iterator<BigInteger> prdidItr = null;    



            BigInteger brCode = null;
            BigInteger prdId = null;

            Map<BigInteger, List<Product>> tempMap = null;
            List<Product1> accListPerBr = null;
            List<Product1> accListPerBrPerPrd = null;

            Product1 tempPrd = null;
            Double sum = null;
            while (brItr.hasNext()) {
                brCode = brItr.next();
                //get  list per branch
                accListPerBr = groupByBrCd.get(brCode);

                // group by br wise product wise
                groupByPrId=accListPerBr.stream().collect(Collectors.groupingBy(Product1::getPrdId, Collectors.toList()));

                System.out.println("====================");
                System.out.println(groupByPrId);

                prdidItr = groupByPrId.keySet().iterator();
                while(prdidItr.hasNext()){
                    prdId=prdidItr.next();
                    // get list per brcode+product code
                    accListPerBrPerPrd=groupByPrId.get(prdId);
                    newPrd = new Product();
                     // Extract zeroth element to put in Output List to represent this group
                    tempPrd = accListPerBrPerPrd.get(0);
                    newPrd.setBranchCode(tempPrd.getBranchCode());
                    newPrd.setPrdId(tempPrd.getPrdId());

                    //Set accCOunt by using size of list of our group
                    newPrd.setCountOfAccts(BigInteger.valueOf(accListPerBrPerPrd.size()));
                    //Sum actual balance of our  of list of our group 
                    sum = accListPerBrPerPrd.stream().filter(o -> o.getActualBalance() != null).mapToDouble(o -> o.getActualBalance().doubleValue()).sum();
                    newPrd.setSumActBal(BigDecimal.valueOf(sum));
                    // Add product element in final output list

                    finalOutputList.add(newPrd);

                }

            }

            System.out.println("+++++++++++++++++++++++");
            System.out.println(finalOutputList);

        }
    }

आउटपुट नीचे है:

+++++++++++++++++++++++
[Product{branchCode:1, prdId:11, accountCode:null, actualBalance:null, sumActBal:20.0, countOfAccts:2}, Product{branchCode:1, prdId:12, accountCode:null, actualBalance:null, sumActBal:30.0, countOfAccts:3}, Product{branchCode:1, prdId:13, accountCode:null, actualBalance:null, sumActBal:10.0, countOfAccts:1}, Product{branchCode:2, prdId:11, accountCode:null, actualBalance:null, sumActBal:20.0, countOfAccts:2}, Product{branchCode:2, prdId:12, accountCode:null, actualBalance:null, sumActBal:30.0, countOfAccts:3}, Product{branchCode:2, prdId:13, accountCode:null, actualBalance:null, sumActBal:10.0, countOfAccts:1}]

इसे प्रारूपित करने के बाद:

[
Product{branchCode:1, prdId:11, accountCode:null, actualBalance:null, sumActBal:20.0, countOfAccts:2}, 
Product{branchCode:1, prdId:12, accountCode:null, actualBalance:null, sumActBal:30.0, countOfAccts:3}, 
Product{branchCode:1, prdId:13, accountCode:null, actualBalance:null, sumActBal:10.0, countOfAccts:1}, 
Product{branchCode:2, prdId:11, accountCode:null, actualBalance:null, sumActBal:20.0, countOfAccts:2}, 
Product{branchCode:2, prdId:12, accountCode:null, actualBalance:null, sumActBal:30.0, countOfAccts:3}, 
Product{branchCode:2, prdId:13, accountCode:null, actualBalance:null, sumActBal:10.0, countOfAccts:1}
]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.