scala - जावा सूची से स्कैला सूची कैसे प्राप्त करें?




(5)

मेरे पास एक जावा एपीआई है जो एक सूची देता है जैसे:

public List<?> getByXPath(String xpathExpr)

मैं नीचे स्कैला कोड का उपयोग कर रहा हूँ:

val lst = node.getByXPath(xpath)

अब अगर मैं स्कैला सिंटैक्स चीनी की तरह कोशिश करता हूं:

lst.foreach{ node => ... }

यह काम नहीं करता। मुझे त्रुटि मिलती है:

value foreach is not a member of java.util.List[?0]

ऐसा लगता है कि मुझे जावा सूची को स्कैला सूची में परिवर्तित करने की आवश्यकता है। उपर्युक्त संदर्भ में ऐसा कैसे करें?


Answers

मैं जावा में लिखे गए एक उत्तर की तलाश में था और आश्चर्यजनक रूप से यहां कोई साफ समाधान नहीं मिला। थोड़ी देर के बाद मैं इसे समझने में सक्षम था इसलिए मैंने इसे यहां जोड़ने का फैसला किया अगर कोई अन्य जावा कार्यान्वयन की तलाश में है (मुझे लगता है कि यह स्कैला में भी काम करता है?):

JavaConversions.asScalaBuffer(myJavaList).toList()

यदि आपको जावा List<ClassA> को List[ClassB] करना है, तो आपको निम्न कार्य करना होगा:

1) जोड़ें

import scala.collection.JavaConverters._

2) asScala , toList और फिर map विधियों का उपयोग map

List <ClassA> javaList = ...
var scalaList[ClassB] = javaList.asScala.toList.map(x => new ClassB(x))

3) ClassB कन्स्ट्रक्टर को निम्न जोड़ें जो ClassB को पैरामीटर के रूप में प्राप्त करता है:

case class ClassB () {
   def this (classA: ClassA) {
      this (new ClassB (classA.getAttr1, ..., classA.getAttrN))
   }
}

स्केल 2.8.1 उपयोगकर्ता JavaConvertest._ के बाद से

स्कैला और जावा संग्रह एस्काला और asJava विधियों का उपयोग कर।

import scala.collection.JavaConverters._

javalist.asScala

scalaSeq.asJava

रूपांतरण संबंध स्कैला दस्तावेज़ साइट देखें


स्कैला 2.8 के बाद से यह रूपांतरण अब भाषा में बनाया गया है:

import scala.collection.JavaConversions._

...

lst.toList.foreach{ node =>   .... }

काम करता है। asScala काम नहीं किया था


इस समस्या को समझने की कुंजी यह जानना है कि संग्रह पुस्तकालय में संग्रह के साथ निर्माण और काम करने के दो अलग-अलग तरीके हैं । एक सार्वजनिक संग्रह इंटरफेस है जो इसके सभी अच्छे तरीकों से है। दूसरा, जिसे संग्रह पुस्तकालय बनाने में बड़े पैमाने पर उपयोग किया जाता है, लेकिन जो लगभग इसके बाहर कभी भी उपयोग नहीं किया जाता है, बिल्डर्स है।

समृद्ध करने में हमारी समस्या बिल्कुल वही है जो संग्रह लाइब्रेरी को उसी प्रकार के संग्रह को वापस करने का प्रयास करते समय सामना करना पड़ता है। यही है, हम संग्रह बनाना चाहते हैं, लेकिन सामान्य रूप से काम करते समय, हमारे पास "उसी प्रकार का संग्रह है जो पहले से संग्रह है" का संदर्भ देने का कोई तरीका नहीं है। तो हमें बिल्डरों की जरूरत है।

अब सवाल यह है कि हम अपने बिल्डरों को कहां से प्राप्त करते हैं? स्पष्ट स्थान संग्रह से ही है। यह काम नहीं करता है । हमने पहले ही निर्णय लिया है कि, एक सामान्य संग्रह में जाने के लिए, हम संग्रह के प्रकार को भूलने जा रहे थे। तो भले ही संग्रह एक निर्माता को वापस कर सके जो हमारे इच्छित प्रकार के अधिक संग्रह उत्पन्न करेगा, यह नहीं पता कि यह किस प्रकार का था।

इसके बजाए, हम अपने बिल्डरों को CanBuildFrom से प्राप्त करने वाले CanBuildFrom से प्राप्त करते हैं जो आसपास तैर रहे हैं। ये विशेष रूप से इनपुट और आउटपुट प्रकारों के मिलान के उद्देश्य से और आपको उचित टाइप किए गए निर्माता को देने के उद्देश्य से मौजूद हैं।

तो, हमारे पास दो वैचारिक छलांग हैं:

  1. हम मानक संग्रह संचालन का उपयोग नहीं कर रहे हैं, हम बिल्डरों का उपयोग कर रहे हैं।
  2. हम इन बिल्डरों को अंतर्निहित CanBuildFrom से प्राप्त करते हैं, हमारे संग्रह से सीधे नहीं।

आइए एक उदाहरण देखें।

class GroupingCollection[A, C[A] <: Iterable[A]](ca: C[A]) {
  import collection.generic.CanBuildFrom
  def groupedWhile(p: (A,A) => Boolean)(
    implicit cbfcc: CanBuildFrom[C[A],C[A],C[C[A]]], cbfc: CanBuildFrom[C[A],A,C[A]]
  ): C[C[A]] = {
    val it = ca.iterator
    val cca = cbfcc()
    if (!it.hasNext) cca.result
    else {
      val as = cbfc()
      var olda = it.next
      as += olda
      while (it.hasNext) {
        val a = it.next
        if (p(olda,a)) as += a
        else { cca += as.result; as.clear; as += a }
        olda = a
      }
      cca += as.result
    }
    cca.result
  }
}
implicit def iterable_has_grouping[A, C[A] <: Iterable[A]](ca: C[A]) = {
  new GroupingCollection[A,C](ca)
}

आइए इसे अलग करें। सबसे पहले, संग्रह संग्रह का निर्माण करने के लिए, हम जानते हैं कि हमें दो प्रकार के संग्रह बनाने की आवश्यकता होगी: प्रत्येक समूह के लिए C[A] , और C[C[A]] जो सभी समूहों को एक साथ इकट्ठा करते हैं। इस प्रकार, हमें दो बिल्डरों की आवश्यकता है, जो A एस लेता है और C[A] बनाता C[A] , और वह जो C[A] लेता C[A] और C[C[A]] बनाता C[C[A]]CanBuildFrom के प्रकार हस्ताक्षर को देखते CanBuildFrom , हम देखते हैं

CanBuildFrom[-From, -Elem, +To]

जिसका अर्थ है कि CanBuildFrom हमारे द्वारा शुरू किए जा रहे संग्रह के प्रकार को जानना चाहता है - हमारे मामले में, यह C[A] , और फिर जेनरेट किए गए संग्रह के तत्व और उस संग्रह के प्रकार। इसलिए हम उनको अंतर्निहित पैरामीटर cbfcc और cbfc रूप में cbfc

यह महसूस करने के बाद, यह ज्यादातर काम है। हम बिल्डर्स को देने के लिए हमारे CanBuildFrom s का उपयोग कर सकते हैं (आपको बस उन्हें लागू करने की आवश्यकता है)। और एक निर्माता += साथ संग्रह बना सकता है, इसे उस संग्रह में रूपांतरित कर सकता है जिसे अंततः result साथ होना चाहिए, और खुद को खाली करें और clear से फिर से शुरू करने के लिए तैयार रहें। बिल्डर्स खाली शुरू होते हैं, जो हमारी पहली संकलन त्रुटि हल करता है, और चूंकि हम रिकर्सन के बजाय बिल्डर्स का उपयोग कर रहे हैं, दूसरी त्रुटि भी दूर हो जाती है।

एक आखिरी छोटी जानकारी - वास्तव में काम करने वाले एल्गोरिदम के अलावा - अंतर्निहित रूपांतरण में है। ध्यान दें कि हम new GroupingCollection[A,C] नहीं करते [A,C[A]] । ऐसा इसलिए है क्योंकि वर्ग घोषणा C के लिए एक पैरामीटर के साथ थी, जो इसे स्वयं को पारित A साथ भरती है। इसलिए हम इसे टाइप C टाइप करते हैं, और इसे C[A] बाहर बनाने दें। मामूली विस्तार, लेकिन यदि आप किसी अन्य तरीके से प्रयास करते हैं तो आपको संकलन-समय त्रुटियां मिलेंगी।

यहां, मैंने विधि को "समान तत्व" संग्रह से थोड़ा अधिक सामान्य बना दिया है - बल्कि, जब भी अनुक्रमिक तत्वों का परीक्षण विफल हो जाता है, तो विधि मूल संग्रह को अलग करती है।

आइए कार्यवाही में हमारी विधि देखें:

scala> List(1,2,2,2,3,4,4,4,5,5,1,1,1,2).groupedWhile(_ == _)
res0: List[List[Int]] = List(List(1), List(2, 2, 2), List(3), List(4, 4, 4), 
                             List(5, 5), List(1, 1, 1), List(2))

scala> Vector(1,2,3,4,1,2,3,1,2,1).groupedWhile(_ < _)
res1: scala.collection.immutable.Vector[scala.collection.immutable.Vector[Int]] =
  Vector(Vector(1, 2, 3, 4), Vector(1, 2, 3), Vector(1, 2), Vector(1))

यह काम करता हैं!

एकमात्र समस्या यह है कि हम सामान्य रूप से इन तरीकों को सरणी के लिए उपलब्ध नहीं करते हैं, क्योंकि इसके लिए पंक्ति में दो अंतर्निहित रूपांतरण की आवश्यकता होती है। इसके चारों ओर जाने के कई तरीके हैं, जिनमें सरणी के लिए एक अलग निहित रूपांतरण WrappedArray , WrappedArray कास्टिंग करना WrappedArray शामिल हैं।

संपादित करें: सरणी और तारों से निपटने के लिए मेरा पसंदीदा दृष्टिकोण और इस तरह कोड को और भी सामान्य बनाना है और फिर उचित अंतर्निहित रूपांतरणों का उपयोग करना ताकि उन्हें फिर से और अधिक विशिष्ट बनाने के लिए एरे काम भी कर सकें। इस विशेष मामले में:

class GroupingCollection[A, C, D[C]](ca: C)(
  implicit c2i: C => Iterable[A],
           cbf: CanBuildFrom[C,C,D[C]],
           cbfi: CanBuildFrom[C,A,C]
) {
  def groupedWhile(p: (A,A) => Boolean): D[C] = {
    val it = c2i(ca).iterator
    val cca = cbf()
    if (!it.hasNext) cca.result
    else {
      val as = cbfi()
      var olda = it.next
      as += olda
      while (it.hasNext) {
        val a = it.next
        if (p(olda,a)) as += a
        else { cca += as.result; as.clear; as += a }
        olda = a
      }
      cca += as.result
    }
    cca.result
  }
}

यहां हमने एक निहितार्थ जोड़ा है जो हमें C से एक Iterable[A] देता C - अधिकांश संग्रहों के लिए यह केवल पहचान होगी (उदाहरण के लिए List[A] पहले से ही एक Iterable[A] ), लेकिन सरणी के लिए यह एक होगा असली निहित रूपांतरण। और, इसके परिणामस्वरूप, हमने आवश्यकता को छोड़ दिया है कि C[A] <: Iterable[A] हमने मूल रूप से केवल <% स्पष्ट करने की आवश्यकता बनाई है, इसलिए हम इसे संकलक भरने के बजाय स्पष्ट रूप से इच्छानुसार उपयोग कर सकते हैं यह हमारे लिए है। साथ ही, हमने इस प्रतिबंध को आराम दिया है कि हमारे संग्रह-संग्रह संग्रह C[C[A]] जारी रखें, यह कोई D[C] , जिसे हम बाद में भरने के लिए भरेंगे। क्योंकि हम इसे बाद में भरने जा रहे हैं, हमने इसे विधि स्तर के बजाय कक्षा स्तर तक पहुंचा दिया है। अन्यथा, यह मूल रूप से वही है।

अब सवाल यह है कि इसका उपयोग कैसे करें। नियमित संग्रह के लिए, हम कर सकते हैं:

implicit def collections_have_grouping[A, C[A]](ca: C[A])(
  implicit c2i: C[A] => Iterable[A],
           cbf: CanBuildFrom[C[A],C[A],C[C[A]]],
           cbfi: CanBuildFrom[C[A],A,C[A]]
) = {
  new GroupingCollection[A,C[A],C](ca)(c2i, cbf, cbfi)
}

जहां हम D[C] लिए C और C[C[A]] लिए C[A] में प्लग करते हैं। ध्यान दें कि हमें new GroupingCollection पर कॉल पर स्पष्ट जेनेरिक प्रकारों की आवश्यकता है ताकि यह सीधे किस प्रकार के अनुरूप हो सके। implicit c2i: C[A] => Iterable[A] के लिए धन्यवाद implicit c2i: C[A] => Iterable[A] , यह स्वचालित रूप से सरणी संभालती है।

लेकिन रुको, क्या होगा यदि हम तारों का उपयोग करना चाहते हैं? अब हम परेशानी में हैं, क्योंकि आपके पास "तारों की स्ट्रिंग" नहीं हो सकती है। यह वह जगह है जहां अतिरिक्त अमूर्तता मदद करता है: हम D कुछ ऐसा कह सकते हैं जो तारों को पकड़ने के लिए उपयुक्त है। आइए Vector चुनें, और निम्न कार्य करें:

val vector_string_builder = (
  new CanBuildFrom[String, String, Vector[String]] {
    def apply() = Vector.newBuilder[String]
    def apply(from: String) = this.apply()
  }
)

implicit def strings_have_grouping(s: String)(
  implicit c2i: String => Iterable[Char],
           cbfi: CanBuildFrom[String,Char,String]
) = {
  new GroupingCollection[Char,String,Vector](s)(
    c2i, vector_string_builder, cbfi
  )
}

स्ट्रिंग्स के वेक्टर के निर्माण को संभालने के लिए हमें एक नया CanBuildFrom की आवश्यकता है (लेकिन यह वास्तव में आसान है, क्योंकि हमें केवल Vector.newBuilder[String] को कॉल करने की आवश्यकता है), और फिर हमें सभी प्रकारों को भरने की आवश्यकता है ताकि GroupingCollection Vector.newBuilder[String] समझदारी से टाइप किया। ध्यान दें कि हम पहले से ही [String,Char,String] CanBuildFrom के चारों ओर तैर रहे हैं, इसलिए तारों के संग्रह से तारों को बनाया जा सकता है।

आइए इसे आज़माएं:

scala> List(true,false,true,true,true).groupedWhile(_ == _)
res1: List[List[Boolean]] = List(List(true), List(false), List(true, true, true))

scala> Array(1,2,5,3,5,6,7,4,1).groupedWhile(_ <= _) 
res2: Array[Array[Int]] = Array(Array(1, 2, 5), Array(3, 5, 6, 7), Array(4), Array(1))

scala> "Hello there!!".groupedWhile(_.isLetter == _.isLetter)
res3: Vector[String] = Vector(Hello,  , there, !!)




scala