【问题标题】:How can you create a unique serial number for a given string?如何为给定的字符串创建唯一的序列号?
【发布时间】:2016-05-11 17:03:44
【问题描述】:

所以我正在尝试创建一个代表给定人员的虚构许可证号的类。许可证号由以下内容构成:

  • 人名和姓氏的首字母
  • 许可证颁发年份
  • 一个随机的任意序列号

例如,Maggie Smith 的许可证于 1990 年颁发,其许可证号可能为 MS-1990-11,其中 11 是序列号。但是,Mark Sanders 可能在同一年获得了许可证,这意味着他的许可证的开头也可能是 MS-1990。

所以这就是问题发生的地方。我需要确保此人的序列号与 Maggie 的不同。所以我必须检查所有具有相同首字母和发行年份的记录,然后生成一个新的唯一序列号。到目前为止,这是我的代码:

public class LicenceNumber {

private final Name driverName;
private final Date issueDate;
private static final Map<String, LicenceNumber> LICENCENOS = new HashMap<String, LicenceNumber>();

public LicenceNumber(Name driverName, Date issueDate){

    this.driverName = driverName;
    this.issueDate = issueDate;
}

public static LicenceNumber getInstance(Name driverName, Date issueDate){
    Calendar tempCal = Calendar.getInstance();
    tempCal.setTime(issueDate);
    String issueYear = String.valueOf(tempCal.get(Calendar.YEAR));
    int serialNo = 1;
    String k = driverName.getForename().substring(0, 1) + driverName.getSurname().substring(0, 1) + "-" + issueYear + "-" + serialNo;

    if(!LICENCENOS.containsKey(k)){
        LICENCENOS.put(k, new LicenceNumber(driverName,issueDate));
    }

    return LICENCENOS.get(k);
}

public boolean isUnique(){
    return true;
}

public Name getDriverName() {
    return driverName;
}

public Date getIssueDate() {
    return issueDate;
}
}

以及如何实例化它的 sn-p:

public final class DrivingLicence {

private final Name driverName;
private final Date driverDOB;
private final Date issueDate;
private final LicenceNumber licenceNo;
private final boolean isFull;

public DrivingLicence(Name driverName, Date driverDOB, Date issueDate, boolean isFull){
    //TO-DO validate inputs
    this.driverName = driverName;
    this.driverDOB = driverDOB;
    this.issueDate = issueDate;
    this.licenceNo = LicenceNumber.getInstance(driverName, issueDate);
    //this.licenceNo = new LicenceNumber(driverName, issueDate);//instantiate a licence number using the driverName and dateOfIssue
    this.isFull = isFull;
}
}

我基于一些讨论如何使用工厂来实现独特性的讲义。我也不确定是否应该使用 getInstance 或通过创建新对象来创建 LicenceNumber。有谁知道我可以检查给定字符串的序列号的方法,例如XX-XXXX 已经存在?

【问题讨论】:

  • 这完全由数据库支持吗?我的意思是,什么构成“存在”——在什么时间范围内?
  • 不,不涉及数据库。如果我正确实现了它,LicenceNumbers 会存储在 Map 中,所以我必须在那里检查。
  • 我建议使用 AtomicInteger。稍后我将提供快速答复。
  • 您需要小心线程问题。此外,如果您不担心 MS-1980 序列号中的“间隙”,您可以拥有一个主序列生成器。否则,您将需要为每组首字母和年份的序列生成器。
  • 为什么它们必须是随机的?为什么不只是一个连续的系列?

标签: java unique factory


【解决方案1】:

这是一种为程序持续时间创建增量数字的方法。由于没有后备数据库,程序的每次运行都会重置。

它通过使用AtomicInteger 来确保唯一性。我使用了ConcurrentMap 来利用线程安全以及.putIfAbsent 方法。但是,它可以很容易地转换为使用标准Map。我也只是使用了String,但更好的方法是使用真正的域对象。足以处理 OP 的问题并用于说明目的。

// a Map for holding the sequencing
private ConcurrentMap<String, AtomicInteger> _sequence = 
        new ConcurrentHashMap<>();

/**
 * Returns a unique, incrementing sequence, formatted to
 * 0 prefixed, 3 places, based upon the User's initials
 * and the registration year
 */
public String getSequence(String initials, String year)
{
    String key = makePrefix(initials, year);
    AtomicInteger chk = new AtomicInteger(0);
    AtomicInteger ai = _sequence.putIfAbsent(key, chk);
    if (ai == null) {
        ai = chk;
    }

    int val = ai.incrementAndGet();

    String fmt = String.format("%03d", val);

    return fmt;
}

/**
 * A helper method to make the prefix, which is the
 * concatintion of the initials, a "-", and a year.
 */
private String makePrefix (String initials, String year)
{
    return initials + "-" + year;
}

测试示例:

public static void main(String[] args)
{
    LicensePlate_37169055 lp = new LicensePlate_37169055();
    System.out.println("ko, 1999: " + lp.getSequence("ko", "1999"));
    System.out.println("ac, 1999: " + lp.getSequence("ac", "1999"));
    System.out.println("ko, 1999: " + lp.getSequence("ko", "1999"));
    System.out.println("ac, 1999: " + lp.getSequence("ac", "1999"));
    System.out.println("ms, 1999: " + lp.getSequence("ms", "1999"));
    System.out.println("ko, 2001: " + lp.getSequence("ko", "2001"));

}

示例输出:

ko, 1999: 001
交流,1999:001
ko, 1999: 002
交流,1999:002
女士,1999:001
ko, 2001: 001

要集成到 OP 的代码中,建议进行以下修改:

public static LicenceNumber getInstance(Name driverName, Date issueDate){
  Calendar tempCal = Calendar.getInstance();
  tempCal.setTime(issueDate);
  String issueYear = String.valueOf(tempCal.get(Calendar.YEAR));

  // ** get the initials; I would actually move this functionality to be
  //   a method on the Name class
  String initials = driverName.getForename().substring(0, 1) + driverName.getSurname().substring(0, 1);

  // get the unique serial number
  String serial = getSequence(initials, issueYear);

  // make the full licenseplate String
  String k = makePrefix(initials, issueYear) + "-" + serial;

if(!LICENCENOS.containsKey(k)){
    LICENCENOS.put(k, new LicenceNumber(driverName,issueDate));
}

return LICENCENOS.get(k);

}

【讨论】:

  • 似乎在使用 getSequence() 设置 serialNumber 时,我收到一条错误消息,指出 getSequence 应该是静态的。另外,如何使用标准地图来实现?
  • 抱歉,错过了getInstance() 是静态的(相关问题:为什么会这样?)。因此,其他方法也需要是静态的。要使用标准 Map&lt;&gt;,请从其中删除 Concurrent,然后将 putIfAbsent 更改为类似于 ai = _sequence.get(key); if (ai == null) { ai = chk; _sequence.put(key, ai);} 的内容。基本上,如果您阅读putIfAbsent 的Javadoc,它会给出它正在使用的算法。请注意,进行这些更改会删除线程安全性。
  • 我的错,标准Map有一个putIfAbsent(),所以理论上你可以从ConcurrentMap/CurrentHashMap更改为Map/HashMapstatic final private Map&lt;String, AtomicInteger&gt; _sequence = new HashMap&lt;&gt;();
  • 啊,是的,效果很好。我将 getInstance 设为静态的原因是因为我将它基于一些讲义中给出的示例。有什么不应该的原因吗?
  • 很高兴答案有帮助!恕我直言,getInstance() 表示单例模式;我希望getInstance() 返回一些单例,而不是对象的特定实例化。如果我遵循这个想法,基本上你有一个构建器方法,而不是使用构造器。该方法有效,但我会在代码审查中推动重构,因为太多的方法和变量必须是静态的,但 LicenseNumber 的构造函数是公共的。这意味着我可以做new LicenseNumber(),但它不会在 `LICENCENOS` 映射中正确注册。
猜你喜欢
  • 2016-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-19
  • 2014-10-20
相关资源
最近更新 更多