【问题标题】:Scala replacement for Arrays.binarySearch?Scala 替代 Arrays.binarySearch?
【发布时间】:2011-05-12 17:37:18
【问题描述】:

在 Scala 中是否有替代 Java 的 int Arrays.binarySearch(Object[] array, object)

问题是 Scala 的数组不是协变的,所以我必须先像这样转换我的 stringArray: Array[String]

stringArray.asInstanceOf[Array[Object]]

有没有更好的解决方案?

【问题讨论】:

    标签: java search scala programming-languages


    【解决方案1】:

    据我所知,没有任何内置功能,但您可以使用pimp-my-library pattern 轻松完成此操作。像这样:

    class ObjectArrayTools[T <: AnyRef](a: Array[T]) {                  
       def binarySearch(key: T) = {
         java.util.Arrays.binarySearch(a.asInstanceOf[Array[AnyRef]],key)
       }
    }
    implicit def anyrefarray_tools[T <: AnyRef](a: Array[T]) = new ObjectArrayTools(a)
    
    scala> Array("a","fish","is","some","thing").binarySearch("some")
    res26: Int = 3
    scala> Array("a","fish","is","some","thing").binarySearch("bye")  
    res28: Int = -2
    

    如果您也需要其他 java.util.Arrays 对象方法,您可以将它们添加到同一个类中。

    一般来说,我发现习惯于总是导入你最喜欢的 Scala 实用程序的集合是个好主意。添加这样的功能非常容易,您可以直接添加,而不是继续输入.asInstanceOf[Array[AnyRef]],并且只需稍加努力,您就可以显着提高工作效率。

    【讨论】:

    • def indexOf (elem: A, from: Int) : Int 不应该表现得像 binarySearch(虽然可能更慢?)
    • @soc 没有那个签名,因为binarySearch 取决于密钥是Comparable。 IIRC,Java 只是强制转换并返回一个运行时错误,这不是我们想要从 Scala 中得到的那种东西。此外,它仅适用于已排序的集合,同样,在没有类数量激增的情况下,目前无法静态强制执行(有一些想法)。
    • 如果 binarySearch 依赖于 Comparable 的项目,我们不应该添加诸如 T &lt;: Comparable[T] 之类的约束吗?
    【解决方案2】:

    数组是有趣的野兽。如果您尝试使用“ObjectArrayTools”提供的示例中的代码:

    Array(1, 2, 3, 4, 5).binarySearch(3)
    

    你得到

    error: value binarySearch is not a member of Array[Int]
              Array(1, 2, 3, 4, 5).binarySearch(3)
    

    关于 Scala 中的数组发生了什么,请参阅 this document。在任何情况下,您都可以使用此代码,尽管它使用 Seq 而不是 Array。但是,它还有一个额外的好处,那就是使用 Ordering(恰好也是 Java 比较器。因此,如果需要,您可以自定义有序行为。)

    import _root_.scala.collection.JavaConversions._
    import java.util.{Collections, List => JList}
    class SearchableSeq[T](a: Seq[T])(implicit ordering: Ordering[T]) {
        val list: JList[T] = a.toList
        def binarySearch(key: T): Int = Collections.binarySearch(list, key, ordering)
    }
    implicit def seqToSearchable[T](a: Seq[T])(implicit ordering: Ordering[T]) = 
            new SearchableSeq(a)(ordering)
    

    一些例子:

    scala> List(1, 2, 3, 4, 5).binarySearch(3)
    res0: Int = 2
    
    scala> List(1D, 2D, 3D, 4D, 5D).binarySearch(3.5)
    res1: Int = -4
    
    scala> List("a","fish","is","some","thing").binarySearch("bye")
    res2: Int = -2
    

    【讨论】:

    • 按索引访问java List 效率很低
    • @piotr。这取决于支持列表的内容。例如,ArrayList 由 Array 支持;它们在按索引获取元素方面都提供了相似的性能。
    • 不幸的是,这个解决方案不是最理想的。 Collections.binarySearch 检查提供的列表是否实现了 RandomAccess 接口(“如果指定的列表没有实现 RandomAccess 接口并且很大,则此方法将执行基于迭代器的二进制搜索,执行 O(n) 链接遍历和 O(log n)元素比较。”)。 JavaConverters 提供的列表都没有实现该接口。 (docs.oracle.com/javase/7/docs/api/java/util/…)
    • 好收获。我没有意识到这一点。看起来 scala 在 2.11 中添加了scala.collection.Searching。因此,您可以使用:import scala.collection.Searching._; Array(1, 2, 3, 4, 5).search(3)
    【解决方案3】:

    Scala 2.11 将scala.collection.Searching 添加到标准库中。它对索引序列使用二进制搜索,否则使用线性搜索。

    import scala.collection.Searching._
    Array(1, 2, 3, 4, 5).search(3)
    

    【讨论】:

    • 如何告诉搜索方法我的数组已排序并且可以使用二分搜索?
    • @samy 您的收藏必须经过排序才能使用search。也就是说,如果您在未排序的序列上调用search,则结果未定义。
    • 感谢您的解释,但似乎该方法在我的 Array[String] 类型数组上使用线性搜索,它比 java.util.Arrays.binarySearch() 慢很多
    • 似乎是我使用的版本 scala 2.11 中的一个错误,issues.scala-lang.org/browse/SI-9605
    • @AbhijitSarkar 您可以使用 Scala 搜索 API 轻松区分这些情况,因为返回的 SearchResultFoundInsertionPoint。使用match 来判断是哪种情况,这两种情况对于阅读代码的任何人来说都是明确的。
    【解决方案4】:

    在scala中写它并不难

      object BSearch {
        def interative[T](array: Array[T], value: T)(implicit arithmetic: Numeric[T]): Int = {
          var left: Int = 0;
          var right: Int = array.length - 1;
          while (right > left) {
            val mid = left + (right - left) / 2
            val comp = arithmetic.compare(array(mid), value)
            if (comp == 0)
              return mid; //negative if test < value
            else if (comp > 0) //list(mid) > value
              right = mid - 1;
            else if (comp < 0) //list(mid) < value
              left = mid + 1;
          }
          -1;
        }
    
    
    BSearch.interative(array, value)
    

    【讨论】:

      【解决方案5】:

      @moshe-beeri

      如果你打算用 Scala 编写它,你为什么要在 Scala 中用 Java 编写它?为什么不实际用 Scala 编写呢?

      def split(list:List[Char]): (List[Char], List[Char]) = {
          val len = list.size
          (list.slice(0, len/2), list.slice(len/2,len))
      }
      
      def search(target: Char, list: List[Char]):Boolean = {
          list match {
              case Nil => false
              case head :: Nil =>  if (head == target) true else false
              case _ => {
                  val c = split(list)
                  if (c._1.last >= target) search(target, c._1) else search(target, c._2)
              }
          }
      }
      

      【讨论】:

        【解决方案6】:

        这个问题提出已经好几年了,想做一些比较测试,希望它可以帮助一些人做出决定:

        import scala.collection.Searching._
        import _root_.scala.collection.JavaConversions._
        import java.util.{Collections, List => JList}
        import scala.reflect.ClassTag
        
        class ObjectArrayTools[T <: Int](a: Array[T]) {
           def binarySearch(key: T) = {
             java.util.Arrays.binarySearch(a.asInstanceOf[Array[Int]],key)
           }
        }
        
        class SearchableSeq[T](a: Seq[T])(implicit ordering: Ordering[T]) {
            val list: JList[T] = a.toList
            def binarySearch2(key: T): Int = Collections.binarySearch(list, key, ordering)
        }
        
        object BinarySearch {
          implicit def anyrefarray_tools[T <: Int](a: Array[T]) = new ObjectArrayTools(a)
          implicit def seqToSearchable[T](a: Seq[T])(implicit ordering: Ordering[T]) =
                  new SearchableSeq(a)(ordering)
        
          def main(args:Array[String]) {
            val informationArray = Array(1,2,3,4,5,6,7,8,9,10,11,12,14,15,18,20,22,23,25,26)
            val informationList = List(1,2,3,4,5,6,7,8,9,10,11,12,14,15,18,20,22,23,25,26)
            //val sortedArray = sortList(informationArray)
            val sortedArray = informationArray
            val sortedList = informationList
        
            for(x <- 0 to 2) {
              val startTime = System.nanoTime
              val result = binarySearch(sortedArray, 5)
              val result2 = binarySearch(sortedArray, 19)
              println(s"Custom search time elapsed: ${(System.nanoTime - startTime)}")
        
              val startTime2 = System.nanoTime
              val result3 = sortedArray.search(5)
              val result4 = sortedArray.search(19)
              println(s"Scala search time elapsed: ${(System.nanoTime - startTime2)}")
        
              val startTime3 = System.nanoTime
              val result5 = sortedArray.binarySearch(5)
              val result6 = sortedArray.binarySearch(19)
              println(s"Java search casting time elapsed: ${(System.nanoTime - startTime3)}")
        
              val startTime4 = System.nanoTime
              val result7 = sortedList.binarySearch2(5)
              val result8 = sortedList.binarySearch2(19)
              println(s"Java search as list time elapsed: ${(System.nanoTime - startTime4)}")
        
        
              val startTime9 = System.nanoTime
              val result10 = binarySearchWithImplicitConversion(sortedArray, 5)
              val result11 = binarySearchWithImplicitConversion(sortedArray, 19)
              println(s"Custom generic time elapsed: ${(System.nanoTime - startTime9)}")
        
              println("---")
            }
          }
        
          /*def sortList(list:Array[Int]):Array[Int] = {
            import com.walcron.etc.Quicksort._
            quickSort(list)
          }*/
        
          //def binarySearch[T <% Ordered[T]](list:Array[T], valueToBeSearch:T)(implicit t:ClassTag[T]):Int =  {
          def binarySearch(list:Array[Int], valueToBeSearch:Int):Int =  {
            def search(start:Int, end:Int):Int = {
              val pos = ((end - start) / 2) + start
              val curr = list(pos)
        
              if(curr == valueToBeSearch) {
                pos
              }
              else if((end - start) <= 1) {
                -1 * (pos + 1) // Indicates the value should be inserted
              }
              else if(valueToBeSearch > curr) {
                search(pos, end)
              }
              else {
                search(start, pos)
              }
            }
        
            search(0, list.length)
          }
        
          def binarySearchWithImplicitConversion[T <% Ordered[T]](list:Array[T], valueToBeSearch:T)(implicit t:ClassTag[T]):Int =  {
            def search(start:Int, end:Int):Int = {
              val pos = ((end - start) / 2) + start
              val curr = list(pos)
        
              if(curr == valueToBeSearch) {
                pos
              }
              else if((end - start) <= 1) {
                -1 * (pos + 1) // Indicates the value should be inserted
              }
              else if(valueToBeSearch > curr) {
                search(pos, end)
              }
              else {
                search(start, pos)
              }
            }
        
            search(0, list.length)
          }
        }
        

        运行 3 次后返回的结果(因为 Scala 编译器确实需要一些提升)

        Custom search time elapsed: 873373
        Scala search time elapsed: 9322723
        Java search casting time elapsed: 126380
        Java search as list time elapsed: 7375826
        Custom generic time elapsed: 4421972
        ---
        Custom search time elapsed: 10372
        Scala search time elapsed: 34885
        Java search casting time elapsed: 10861
        Java search as list time elapsed: 104596
        Custom generic time elapsed: 57964
        ---
        Custom search time elapsed: 9121
        Scala search time elapsed: 31667
        Java search casting time elapsed: 11815
        Java search as list time elapsed: 53387
        Custom generic time elapsed: 60773
        

        一般来说,java 二分搜索的表现要好得多;而斯卡拉的搜索做得很糟糕。还有另一个值得注意的性能,似乎泛型类型隐式地拖累了这里的性能(所以也许有人可以帮助修复泛型类型)......但间接地显示出很大的性能影响。

        【讨论】:

          猜你喜欢
          • 2015-03-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-10-08
          • 2019-12-19
          • 1970-01-01
          • 2016-10-19
          相关资源
          最近更新 更多