class UniqueHashIndex extends HashIndex {

    private ConcurrentHashMap<Value, Long> rows;

    public UniqueHashIndex(Table table, int id, String indexName, IndexType indexType,
            IndexColumn[] columns) {
        super(table, id, indexName, indexType, columns);
        reset();
    }

    @Override
    protected void reset() {
        rows = new ConcurrentHashMap<>();
    }

    @Override
    public Future<Integer> add(ServerSession session, Row row) {
        Object old = rows.putIfAbsent(getKey(row), row.getKey());
        if (old != null) {
            throw getDuplicateKeyException();
        }
        return Future.succeededFuture(Transaction.OPERATION_COMPLETE);
    }

    @Override
    public Future<Integer> remove(ServerSession session, Row row, boolean isLockedBySelf) {
        rows.remove(getKey(row));
        return Future.succeededFuture(Transaction.OPERATION_COMPLETE);
    }

    @Override
    public Cursor find(ServerSession session, SearchRow first, SearchRow last) {<FILL_FUNCTION_BODY>}

    @Override
    public long getRowCount(ServerSession session) {
        return getRowCountApproximation();
    }

    @Override
    public long getRowCountApproximation() {
        return rows.size();
    }

    /**
     * A cursor with at most one row.
     */
    private static class SingleRowCursor implements Cursor {

        private Row row;
        private boolean end;

        /**
         * Create a new cursor.
         *
         * @param row - the single row (if null then cursor is empty)
         */
        public SingleRowCursor(Row row) {
            this.row = row;
        }

        @Override
        public Row get() {
            return row;
        }

        @Override
        public boolean next() {
            if (row == null || end) {
                row = null;
                return false;
            }
            end = true;
            return true;
        }
    }
}

        checkSearchKey(first, last);
        Row result;
        Long pos = rows.get(getKey(first));
        if (pos == null) {
            result = null;
        } else {
            result = table.getRow(session, pos.intValue());
        }
        return new SingleRowCursor(result);
    
<methods>public boolean canGetFirstOrLast() ,public boolean canScan() ,public void checkRename() ,public void close(com.lealone.db.session.ServerSession) ,public com.lealone.db.result.SearchRow findFirstOrLast(com.lealone.db.session.ServerSession, boolean) ,public double getCost(com.lealone.db.session.ServerSession, int[], com.lealone.db.result.SortOrder) ,public long getDiskSpaceUsed() ,public boolean isInMemory() ,public boolean needRebuild() ,public void remove(com.lealone.db.session.ServerSession) ,public void truncate(com.lealone.db.session.ServerSession) <variables>protected final non-sealed int indexColumn