非常多的時候,我們不想在分享的時候採用長長的連結,主要的原因有:
- URL太長佔顯示空間、難於輸入,轉成二維碼點點小,難於識別
- 長的URL提供的資訊太多,不利於資訊保安,甚至容易造成倒庫
- 其他理由……
今天的理由不是重點,重點是如何用一點點程式碼實現這個短連結的生成。
我們的注意點有:
- 生成的短連結地址長度可調整,畢竟不能因為4位、5位、6位的長度變化就讓我們重新寫一套程式
- 要有防衝突處理機制,不能因為演算法導致兩個長連結地址生成的短地址一樣就把前一個覆蓋掉
- 有時候,一個地址用過之後覺得不需要使用了,哪麼這個地址失效之後,短地址可以有效收回。
首先設計介面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
/** * Created by luoguo on 2017/3/16. */ public interface UrlShortener { /** * 設定產生短連結長度 * @param length * @return */ void setLength(int length); /** * 返回短連結長度 * @return */ int getLength(); /** * 返回指定地址對應的短連結 * @param url * @return */ String get(String url); /** * 儲存對應關係 * @param url * @param shortUrl */ void put(String url, String shortUrl); /** * 到庫裡檢視是不是存在對映,如果不存在返回null * @param shortUrl * @return */ String seek(String shortUrl); /** * 據地址產生短地址 * @param url * @return */ String generate(String url); /** * 根據地址和種子產生一個短地址 * @param url * @param seed * @return */ String generate(String url, int seed); /** * 清除指定URL的短連結資訊 * @param url */ void clean(String url); /** * 清除指定時間以前沒有使用的所有短連結 * @param date */ void clean(Date date); } |
只是個示例,註釋比較簡陋,接下來是抽象類實現,把公共的內容放這裡
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
import java.util.Random; /** * Created by luoguo on 2017/3/16. */ public abstract class AbstractUrlShortener implements UrlShortener { public static char[] VALID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".toCharArray(); private static Random random = new Random(System.currentTimeMillis()); protected int length = 4; public AbstractUrlShortener() { } public AbstractUrlShortener(int length) { this.length = length; } public void setLength(int length) { this.length = length; } public int getLength() { return length; } public String get(String url) { String sortUrl = seek(url); if (sortUrl == null) { sortUrl = generate(url); put(url, sortUrl); } return sortUrl; } public String generate(String url, int seed) { char[] sortUrl = new char[length]; for (int i = 0; i < length; i++) { sortUrl[i] = VALID_CHARS[seed % VALID_CHARS.length]; seed = random.nextInt(Integer.MAX_VALUE) % VALID_CHARS.length; } return new String(sortUrl); } public String generate(String url) { String shortUrl; shortUrl = generate(url, random.nextInt(Integer.MAX_VALUE)); while (seek(shortUrl) != null) { shortUrl = generate(url, random.nextInt(Integer.MAX_VALUE)); } put(url, shortUrl); return shortUrl; } } |
實際的需要在資料庫層實現,這裡在記憶體裡面儲存的實現一個意思一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 記憶體儲存的實現 * <p> * Created by luoguo on 2017/3/16. */ public class UrlShortenerMemory extends AbstractUrlShortener { private Map<String, String> url2ShortUrl = new ConcurrentHashMap<String, String>(); private Map<String, String> shortUrl2Url = new ConcurrentHashMap<String, String>(); public UrlShortenerMemory() { super(); } public UrlShortenerMemory(int length) { super(length); } public void put(String url, String shortUrl) { url2ShortUrl.put(url, shortUrl); shortUrl2Url.put(shortUrl, url); } public String seek(String shortUrl) { return shortUrl2Url.get(shortUrl); } public void clean(String url) { String sortUrl = url2ShortUrl.get(url); if (sortUrl != null) { url2ShortUrl.remove(url); shortUrl2Url.remove(sortUrl); } } public void clean(Date date) { throw new UnsupportedOperationException(); } } |
弄點測試程式碼,試試看效果怎麼樣
1 2 3 4 5 6 7 8 9 10 11 |
public class UrlShortenerTest { public static void main(String[] args) { for(int j=1;j<10;j++) { UrlShortener urlShortener = new UrlShortenerMemory(j); for (int i = 0; i < 5; i++) { System.out.println(urlShortener.get("http://www.tinygroup.org")); } System.out.println(); } } } |
這裡是執行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
T Q l i I 0R VB fN so Nw fw1 fpu WT7 pNL M5V 0cZF H09A AjYM 1AMf sdkz USsgK ZMn9U 7S48u oBb1L yB14g DRVGWg gh3oJa BapdY7 yJgcZj mivGN3 TEiQG2M 8SkokOY NZww5GT fxloZ0R hkiOpID 3ZpdIYrF eFlSSDyd bB4RCFgE 25mceTMK AAJ2Lp6S 5IUVoFSqZ zHZeuZljY njCGDBKVs IgH21Tg5V WvGCy5ZtE |
感覺還可以,當然裡面還有一些不完善的地方
- 當產生的數量達到一定程度的時候,再獲取時由於衝突比較大,導致效能會降低
- 因此如果是小型系統,建議採用4個長度,大型系統建議採用6個長度
- 現在還沒有多長時間之後自動失效的API,建議新增
- 需要實現支援序列化的方案
- 現在沒有同步處理,實際實現中需要考慮
這個只是化幾分鐘展示一個思路,並沒有經過系統的思考的驗證,正式系統請慎重使用。