Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/385.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 设计电话目录,将号码从池中返回_Java_Multithreading_Algorithm_Data Structures - Fatal编程技术网

Java 设计电话目录,将号码从池中返回

Java 设计电话目录,将号码从池中返回,java,multithreading,algorithm,data-structures,Java,Multithreading,Algorithm,Data Structures,我在面试中遇到了以下问题,我能够想出以下解决方案: 设计支持以下操作的电话目录: 获取:提供一个未分配给任何人的号码。检查:检查是否 号码是否可用。释放:回收或释放一个号码 示例://Init包含3个号码的电话目录:0, 一,二 PhoneDirectory目录=新的PhoneDirectory(3) //它可以返回任何可用的电话号码。这里我们假设它返回 0. get()目录 //假设它返回1 get()目录 //数字2可用,因此返回true 目录检查(2) //它返回2,这是唯一剩下的数字

我在面试中遇到了以下问题,我能够想出以下解决方案:

设计支持以下操作的电话目录:

获取:提供一个未分配给任何人的号码。检查:检查是否 号码是否可用。释放:回收或释放一个号码

示例://Init包含3个号码的电话目录:0, 一,二

PhoneDirectory目录=新的PhoneDirectory(3)

//它可以返回任何可用的电话号码。这里我们假设它返回 0.

get()目录

//假设它返回1

get()目录

//数字2可用,因此返回true

目录检查(2)

//它返回2,这是唯一剩下的数字

get()目录

//数字2不再可用,因此返回false

目录检查(2)

//将编号2释放回池中

目录发布(2)

//数字2再次可用,返回true

目录检查(2)

采访者问,如果我们谈论的是10位数的真实电话号码,并且初始化需要大约o(n)个时间,那么这个解决方案的可伸缩性有多大。此外,如果我们经常删除,那么保留每一个未使用的数字可能会浪费空间。他提到了如果在多线程情况下使用它会发生什么

这里有什么我们可以优化的吗

public class PhoneDirectory {
  private final Set<Integer> used = new HashSet<Integer>();
  private final Queue<Integer> available = new LinkedList<Integer>();
  private final int max;

  public PhoneDirectory(int maxNumbers) {
    this.max = maxNumbers;
    for (int i = 0; i < maxNumbers; i++) {
      this.available.offer(i);
    }
  }

  public int get() {
    Integer ret = available.poll();
    if (ret == null) {
      return -1;
    }
    used.add(ret);
    return ret;
  }

  public boolean check(int number) {
    if (number >= max || number < 0) {
      return false;
    }
    return !used.contains(number);
  }

  public void release(int number) {
    if (used.remove(number)) {
      available.offer(number);
    }
  }
}
公共类电话目录{
使用的私有最终集=新HashSet();
private final Queue available=new LinkedList();
私人最终整数最大值;
公用电话目录(int-maxNumbers){
this.max=maxNumbers;
对于(int i=0;i=最大值| |数字<0){
返回false;
}
返回!已使用。包含(编号);
}
公共无效释放(整数){
如果(已使用。删除(编号)){
可用。报价(数量);
}
}
}

正如面试官建议的那样,存储所有未使用的电话号码并不实际。我想从应聘者那里了解一个很好的澄清问题,即
get()
release()
调用的频率是多少。在现实世界中,它们的发生频率可能大致相同,因此以下方法可行:

我们可以通过观察使用了任何不可用的东西来优化您的解决方案,因此实际上没有必要同时存储这两种状态。因此,让我们只跟踪未使用的

公共类电话目录{
私有最终集可用=新HashSet();
公用电话目录(int-maxNumbers){
对于(int i=0;i
这为我们提供了除构造之外的所有调用的摊销
O(1)
操作。为了处理构造函数调用的优化问题,我们可以做一些暗指的事情,并观察到我们可以跟踪到目前为止提供的最大数量,这意味着可以提供以上任何内容。此外,如果存在可用条目的稀疏集,我们可以首先将其耗尽。看起来像这样

公共类电话目录{
私有最终集可用=新HashSet();
私有最终整数最大值;
提供私人综合服务;
公用电话目录(int-maxNumbers){
this.maxNumbers=maxNumbers;
此.largestProvided=0;
}
公共int get(){
如果(可用。isEmpty()){
返回largestOffered最大报价;
}
公共无效释放(整数){
可用。添加(编号);
}
}
这就摆脱了我们的
O(n)
构造函数。回到最初对频率的假设,这种方法之所以有效,是因为如果
get()
release()
以相同的频率相对不可预测地发生,那么可用
的大小将保持相对稳定。这使得数据结构的大小总体上相当有效

如果调用的频率不同,例如,我们预计
release()
一次可以释放大的块,那么这个问题就会变得复杂得多。我相信一般来说,这个问题归结为位图操作,有效地利用空间本质上是位级压缩


关于面试官提出的后续问题,他们可能希望对分布式哈希表进行一些讨论。您还可以优化
available.iterator().next()
然后
available.remove()
调用,因为通过对底层数据结构的访问可以简化调用。

如果不使用它,那么它是可用的。这意味着您实际上不需要一个可用号码的队列。您只需要为下一个可用数字设置一个计数器/指针。我会说电话号码也不应该被当作数字。你也可以随机选择一个数字,看看它是否已经被使用,然后继续选择直到你得到一个,然后你只需要在更新时同步设置。Set.add()最酷的一点是,如果您