collections とは、dict・list・tuple を用途別に便利にしたデータ型をまとめた Python の標準ライブラリです。 数える(Counter)、グループに分ける(defaultdict)、両端から出し入れする(deque)、名前で読めるタプルを作る(namedtuple)の4つが特によく使われます。

この記事では、6つのクラスを「何に使うか」「普通の dict・list で書くとどうなるか」で比較した一覧表から始め、普通の書き方 → collections の書き方の書き換えを、すべて Python 3.11.15 と 3.14.7 で実行した結果つきで載せています。さらに、deque と list の先頭追加・削除、Counter と手書きカウントの速度を timeit で実測し、namedtuple・typing.NamedTuple・dataclass の選び方や、defaultdict で存在確認すると要素が増えるといったつまずきも再現しています。

collectionsの早見表

まずはこの表で「どれを使えばよいか」を決められます。 上の4つが主役、下の2つは場面が限られる補助役です。

クラス 一言でいうと 主な用途 普通の dict / list で書くと 主なメソッド・引数
Counter 数を数える dict 出現回数、ランキング、在庫の差分 d[k] = d.get(k, 0) + 1 をループ most_common(n)、total()、+ - & |
defaultdict キーがなければ自動で初期値を作る dict グループ化、集計 if k not in d: d[k] = [] defaultdict(list)、defaultdict(int)
deque 両端の出し入れが速いリスト キュー、直近 N 件の保持 list.insert(0, x)、list.pop(0) appendleft、popleft、maxlen、rotate
namedtuple 名前で読めるタプル 関数の複数の戻り値、読み取り専用のレコード t[0]、t[1] で読む _replace、_asdict、_fields、_make
OrderedDict 順番操作ができる dict 並び替え、LRU キャッシュの自作 dict(3.7 以降は挿入順を保持) move_to_end、popitem(last=False)
ChainMap 複数の dict を重ねて1つに見せる 設定の優先順位(引数 > 環境 > 既定) {**a, **b} でマージ maps、new_child()

すべて from collections import Counter, defaultdict, deque, namedtuple, OrderedDict, ChainMap で使えます。やりたいことからの逆引きはこちらです。

やりたいこと 使うもの 1行の例
出現回数の多い順に上位を出す Counter Counter(words).most_common(3)
キーごとにリストへ振り分ける defaultdict(list) g[team].append(item)
キーごとに合計する defaultdict(int) total[item] += qty
最新の N 件だけ残す deque(maxlen=N) logs.append(line)
先頭から順に取り出す(FIFO キュー) deque q.popleft()
要素を回転させる deque.rotate d.rotate(1)
p[0] ではなく p.x で読みたい namedtuple Point(1, 2).x
設定を優先順に重ねる ChainMap ChainMap(cli, env, defaults)["size"]

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Python collectionsの使い方|Counter等4種の比較表とコード」

Counterで出現回数を数える

Counter は「要素 → 個数」を持つ dict の一種で、リストを渡すだけで数え終わります。 存在しないキーを読んでも KeyError にならず 0 を返します。

普通のdictで数える書き方

words = ["pen", "note", "pen", "clip", "pen", "note"]

counts = {}
for w in words:
    counts[w] = counts.get(w, 0) + 1
print(counts)
print(sorted(counts.items(), key=lambda kv: kv[1], reverse=True)[:2])

実行結果(Python 3.11.15 / 3.14.7 で同じ):

{'pen': 3, 'note': 2, 'clip': 1}
[('pen', 3), ('note', 2)]

Counterで書き換える

from collections import Counter

words = ["pen", "note", "pen", "clip", "pen", "note"]
c = Counter(words)
print(c)
print(c.most_common(2))
print(c.most_common(1)[0][0])   # 最頻値だけ
print(c["eraser"])              # ないキーは 0
print(c.total())                # 合計(3.10+)
print(Counter("hello world").most_common(3))
Counter({'pen': 3, 'note': 2, 'clip': 1})
[('pen', 3), ('note', 2)]
pen
0
6
[('l', 3), ('o', 2), ('h', 1)]

most_common(n) の戻り値は (要素, 個数) のタプルのリストです。 最頻値の要素だけ欲しいときは most_common(1)[0][0] と2回添字を付けます。個数が同じ要素は、最初に出てきた順で並びます。

Counter同士の足し算・引き算

Counter は + - & | で集計結果どうしを計算できます。 在庫の差分や、2つの集計の合算に便利です。

from collections import Counter

a = Counter(pen=3, note=1)
b = Counter(pen=1, note=2, clip=1)
print(a + b)   # 合計
print(a - b)   # 差(0 以下は消える)
print(a & b)   # 共通部分(小さいほう)
print(a | b)   # 和集合(大きいほう)

a.update(["pen", "clip"])     # 数を足す
print(a)
print(list(Counter(pen=2, note=1).elements()))
print(Counter("abc") == Counter("cba"))  # アナグラム判定
Counter({'pen': 4, 'note': 3, 'clip': 1})
Counter({'pen': 2})
Counter({'pen': 1, 'note': 1})
Counter({'pen': 3, 'note': 2, 'clip': 1})
Counter({'pen': 4, 'note': 1, 'clip': 1})
['pen', 'pen', 'note']
True
演算 意味 0 以下の扱い
a + b 個数を足す 結果から消える
a - b 個数を引く 結果から消える
a & b 小さいほう(min) 結果から消える
a | b 大きいほう(max) 結果から消える
a.update(b) その場で足す 残る
a.subtract(b) その場で引く 負の数も残る

defaultdictでグループ化・集計する

defaultdict(関数) は、存在しないキーにアクセスしたとき、その関数を呼んだ結果を初期値として自動で入れる dict です。 list を渡せば空リスト、int を渡せば 0 が入ります。

普通のdictでグループ化する書き方

orders = [("dev", "pen"), ("ops", "note"), ("dev", "clip")]

groups = {}
for team, item in orders:
    if team not in groups:
        groups[team] = []
    groups[team].append(item)
print(groups)

groups = {}
for team, item in orders:
    groups.setdefault(team, []).append(item)
print(groups)
{'dev': ['pen', 'clip'], 'ops': ['note']}
{'dev': ['pen', 'clip'], 'ops': ['note']}

defaultdictで書き換える

from collections import defaultdict

orders = [("dev", "pen"), ("ops", "note"), ("dev", "clip")]

groups = defaultdict(list)
for team, item in orders:
    groups[team].append(item)
print(groups)
print(dict(groups))   # 普通の dict に戻す

cnt = defaultdict(int)
for w in "abca":
    cnt[w] += 1
print(dict(cnt))
defaultdict(<class 'list'>, {'dev': ['pen', 'clip'], 'ops': ['note']})
{'dev': ['pen', 'clip'], 'ops': ['note']}
{'a': 2, 'b': 1, 'c': 1}

if の分岐や setdefault がなくなり、やりたいこと(追加・加算)だけが残るのが利点です。JSON に出力したり他の関数へ渡したりする前に dict() で普通の dict に戻しておくと、後述のつまずきを避けられます。

渡す関数 初期値 用途
list [] グループ化
int 0 個数・合計
set set() 重複なしで集める
dict {} 2段の dict
lambda: "未設定" "未設定" 任意の既定値

何段でも自動で作られる入れ子の dict も1行で作れます。

import json
from collections import defaultdict

tree = lambda: defaultdict(tree)
t = tree()
t["a"]["b"]["c"] = 1
print(json.dumps(t))
{"a": {"b": {"c": 1}}}

dequeで両端を出し入れする

deque(デック)は、先頭・末尾どちらへの追加・削除も速いリスト風のデータ型です。 maxlen を指定すると、上限を超えたとき反対側から自動で捨てます。

普通のlistで先頭を操作する書き方

lst = [1, 2, 3]
lst.insert(0, 0)     # 先頭に追加
print(lst.pop(0), lst)  # 先頭を取り出す
0 [1, 2, 3]

dequeで書き換える(maxlen・rotate)

from collections import deque

q = deque([1, 2, 3], maxlen=3)
q.append(4)          # 右に追加 → 左の 1 が押し出される
print(q)
q.appendleft(0)      # 左に追加 → 右の 4 が押し出される
print(q)

r = deque([1, 2, 3, 4, 5])
r.rotate(2)          # 右へ2つ回転
print(r)
r.rotate(-2)         # 左へ2つ回転(元に戻る)
print(r)

dq = deque()
dq.append("a")
dq.append("b")
print(dq.popleft(), dq)   # FIFO キュー

logs = deque(maxlen=3)    # 直近3件だけ保持
for i in range(1, 6):
    logs.append(f"log{i}")
print(list(logs))
deque([2, 3, 4], maxlen=3)
deque([0, 2, 3], maxlen=3)
deque([4, 5, 1, 2, 3])
deque([1, 2, 3, 4, 5])
a deque(['b'])
['log3', 'log4', 'log5']

maxlen 付きの deque は「最新 N 件」を保つのに最適です。自分で if len(lst) > N: lst.pop(0) を書く必要がありません。空の deque から popleft() すると IndexError: pop from an empty deque になるので、while dq: で中身を確認してから取り出します。

dequeとlistの先頭追加・削除の速度を実測

先頭への追加(list.insert(0, x) と deque.appendleft(x))と先頭からの削除(list.pop(0) と deque.popleft())を、要素数を変えて1万回ずつ実行し、1回あたりの時間を計測しました(timeit、5セット中の最小値。単位はナノ秒)。

要素数 list.insert(0, x) deque.appendleft(x) list.pop(0) deque.popleft()
1,000(3.11.15) 2,326 ns 30 ns 3,213 ns 18 ns
100,000(3.11.15) 38,599 ns 17 ns 20,420 ns 18 ns
1,000,000(3.11.15) 377,853 ns 19 ns 348,257 ns 18 ns
1,000,000(3.14.7) 331,649 ns 21 ns 366,710 ns 22 ns

list の先頭操作は要素数に比例して遅くなり(後ろの要素を全部ずらすため、O(n))、deque は要素数に関係なく約20ナノ秒で一定(O(1))でした。 100万件では約2万倍の差です(3.11.15)。

一方で、真ん中の要素を添字で読む操作は list のほうが速い結果でした。

要素数 list[n // 2] deque[n // 2]
1,000(3.11.15) 9 ns 23 ns
100,000(3.11.15) 9 ns 1,585 ns
1,000,000(3.11.15) 9 ns 19,853 ns

deque は内部がブロックの連結リストなので、真ん中へのアクセスは端からたどる分だけ時間がかかります。

主な操作 選ぶもの
両端の追加・削除が中心(キュー、履歴) deque
添字アクセス・スライス・並べ替えが中心 list
末尾だけの追加・削除(スタック) どちらでも速い(list で十分)

計測コード(そのまま実行できます):

import timeit
from collections import deque

def per_op(stmt, setup, number=10000):
    t = min(timeit.repeat(stmt, setup=setup, globals=globals(), number=number, repeat=5))
    return t / number * 1e9

for n in (1_000, 100_000, 1_000_000):
    print(n,
          round(per_op("x.insert(0, 1)", f"x = list(range({n}))")),
          round(per_op("x.appendleft(1)", f"x = deque(range({n}))")),
          round(per_op("x.pop(0)", f"x = list(range({n} + 20000))")),
          round(per_op("x.popleft()", f"x = deque(range({n} + 20000))")))

namedtupleで名前つきのタプルを作る

namedtuple は、p[0] の代わりに p.x のように名前で読めるタプルのクラスを作る関数です。 タプルなので値は変更できず、アンパック(x, y = p)もできます。

普通のタプルと比べる

p = (1, 2)
print(p[0], p[1])   # 何の値か名前からは読めない
1 2

namedtupleで書き換える(_replace・_asdict)

from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(1, 2)
print(p, p.x, p[1])

p2 = p._replace(x=10)    # 一部を変えた新しいタプル
print(p, p2)
print(p._asdict())       # dict に変換
print(Point._fields)     # フィールド名
print(Point._make([5, 6]))  # リストから作る

Item = namedtuple("Item", "name qty", defaults=[0])
print(Item("pen"))

x, y = p
print(x, y)
p.x = 3
Point(x=1, y=2) 1 2
Point(x=1, y=2) Point(x=10, y=2)
{'x': 1, 'y': 2}
('x', 'y')
Point(x=5, y=6)
Item(name='pen', qty=0)
1 2
AttributeError: can't set attribute

値は変更できないので、変えたいときは _replace() で新しいタプルを作ります。 元の p はそのままです。メソッド名が _ で始まるのは、フィールド名とぶつからないようにするためで、公開された使ってよいメソッドです。

namedtuple・typing.NamedTuple・dataclassの選び方

同じ「名前つきのレコード」を3つの書き方で作り、違いを確かめました。

from collections import namedtuple
from typing import NamedTuple
from dataclasses import dataclass, replace, asdict

P1 = namedtuple("P1", "name qty")

class P2(NamedTuple):
    name: str
    qty: int = 0

@dataclass
class P3:
    name: str
    qty: int = 0

@dataclass(frozen=True, slots=True)
class P4:
    name: str
    qty: int = 0

b, c = P2("pen", 2), P3("pen", 2)
print(P2("pen"), b._asdict())
print(replace(c, qty=5), asdict(c))
print(isinstance(b, tuple), isinstance(c, tuple))
print(b == ("pen", 2))
print(P2("pen", "二"))   # 型ヒントは実行時にはチェックされない

実行結果(Python 3.11.15 / 3.14.7 で同じ):

P2(name='pen', qty=0) {'name': 'pen', 'qty': 2}
P3(name='pen', qty=5) {'name': 'pen', 'qty': 2}
True False
True
P2(name='pen', qty='二')

NamedTuple はタプルなので、普通のタプル ("pen", 2) と == で等しくなります。 意図せず別の種類のデータと等しいと判定されることがあるので、「レコードの種類」を区別したいなら dataclass が向いています。また P2("pen", "二") が通るように、型ヒントは実行時には検査されません(mypy などの型チェッカーで検出します)。

比べる点 namedtuple typing.NamedTuple dataclass
書き方 関数呼び出し クラス+型ヒント クラス+型ヒント+デコレータ
型ヒント なし あり あり
値の変更 不可 不可 可(frozen=True で不可)
タプルとして扱える はい(添字・アンパック) はい いいえ
一部変更のコピー _replace() _replace() dataclasses.replace()
dict 化 _asdict() _asdict() dataclasses.asdict()
メソッドの追加 しにくい できる できる
サイズの参考値(3.11 / 3.14) 56 / 64 バイト 56 / 64 バイト 336 / 328 バイト(__dict__ 込み)、slots=True で 48 / 48

サイズは sys.getsizeof() の参考値です(2フィールドの場合)。

こんなとき 選ぶもの
関数から複数の値を返したい、既存のタプルを置き換えたい typing.NamedTuple
型ヒントなしで手早く作りたい、古いコードに合わせたい namedtuple
値を変更したい、検証やメソッドを足したい dataclass
変更不可でメモリも抑えたい @dataclass(frozen=True, slots=True)

dataclass の詳しい使い方は「Python dataclassの使い方」の記事で解説しています。

OrderedDictとChainMapの使いどころ

OrderedDict:順番を動かしたいとき

Python 3.7 以降は普通の dict も挿入順を保持するので、OrderedDict が必要なのは「順番を動かす」「順番も含めて比較する」ときです。

from collections import OrderedDict

od = OrderedDict(a=1, b=2, c=3)
od.move_to_end("a")               # a を末尾へ
print(od)
od.move_to_end("c", last=False)   # c を先頭へ
print(od)
print(od.popitem(last=False))     # 先頭を取り出す
print(OrderedDict(a=1, b=2) == OrderedDict(b=2, a=1), dict(a=1, b=2) == dict(b=2, a=1))

実行結果(Python 3.14.7):

OrderedDict({'b': 2, 'c': 3, 'a': 1})
OrderedDict({'c': 3, 'b': 2, 'a': 1})
('c', 3)
False True

Python 3.12 から OrderedDict の表示形式が OrderedDict({...}) に変わりました。 3.11.15 では OrderedDict([('b', 2), ('c', 3), ('a', 1)]) と表示されます(中身は同じです)。move_to_end と popitem(last=False) を組み合わせると、「使ったものを末尾へ、あふれたら先頭から捨てる」LRU キャッシュを自作できます。関数の結果をキャッシュするだけなら functools.lru_cache が手軽です。

ChainMap:設定を優先順に重ねたいとき

ChainMap は複数の dict をコピーせずに重ね、前にある dict から順にキーを探します。

from collections import ChainMap

defaults = {"color": "blue", "size": "M"}
env = {"size": "L"}
cli = {"color": "red"}

cfg = ChainMap(cli, env, defaults)
print(cfg["color"], cfg["size"])
print(dict(cfg))
print({**defaults, **env, **cli})   # 普通のマージで同じ結果

cfg["theme"] = "dark"     # 書き込みは先頭の dict(cli)だけに入る
print(cli)
print(cfg.maps[-1])       # 既定値は変わらない

child = cfg.new_child({"size": "S"})  # 一時的に上書きを足す
print(child["size"], cfg["size"])

実行結果(Python 3.11.15 / 3.14.7 で同じ):

red L
{'color': 'red', 'size': 'L'}
{'color': 'red', 'size': 'L'}
{'color': 'red', 'theme': 'dark'}
{'color': 'blue', 'size': 'M'}
S L
方法 元の dict の変更が反映される 書き込み先 向いている場面
{**a, **b} や a | b(3.9+) されない(コピー) 新しい dict 1回マージして使い終わる
ChainMap(b, a) される(参照) 先頭の dict 優先順位を保ったまま使い続ける、一時的な上書き

Counterと手書きカウントの速度を実測

1,000種類の単語がランダムに並ぶ100万件のリストを、5つの書き方で数えました(timeit、3回×5セットの最小値、結果がすべて同じことを確認)。

書き方 3.11.15 3.14.7
if w in d: d[w] += 1 else: d[w] = 1 117.2 ms 99.6 ms
d[w] = d.get(w, 0) + 1 100.1 ms 88.7 ms
defaultdict(int) で d[w] += 1 96.4 ms 92.0 ms
Counter(words)(リストを丸ごと渡す) 68.8 ms 67.7 ms
Counter() にループで c[w] += 1 145.5 ms 156.7 ms

Counter(words) のようにリストを丸ごと渡す書き方が最速でした。内部で C 言語のヘルパー関数がまとめて数えるためです。一方、Counter をループで1件ずつ += 1 すると最も遅くなりました。Counter は存在しないキーで __missing__ を呼ぶ分の処理が加わるためです。1件ずつ足す必要があるときは defaultdict(int) を使い、まとめて数えられるときは Counter(iterable) か c.update(iterable) を使うと速くなります。

よくあるつまずきと直し方

defaultdictは存在確認しただけで要素が増える

in での確認は要素を増やしませんが、d[key] で読むと初期値が自動で追加されます。

from collections import defaultdict

d = defaultdict(list)
d["x"].append(1)
print("y" in d, len(d))   # in は増やさない

if d["y"]:                # 読んだだけで "y": [] が追加される
    pass
print(dict(d), len(d))

print(d.get("z"), len(d))  # get は増やさない
False 1
{'x': [1], 'y': []} 2
None 2
操作 キーがないとき 要素が増える
key in d False いいえ
d.get(key) None いいえ
d[key] 初期値を作って返す はい

読むだけの処理では in か .get() を使う、処理が終わったら dict(d) に変換して以降は普通の dict として扱う、の2つで防げます。

defaultdict(0)はTypeErrorになる

from collections import defaultdict

defaultdict(0)
TypeError: first argument must be callable or None

引数は「初期値」ではなく「初期値を作る関数」です。 0 ではなく int(呼ぶと 0 を返す)、任意の値なら lambda: 0 のように渡します。

Counterは負の数やゼロも保持する

Counter の値は普通の整数なので、引き算で負の数にもなります。 - 演算子は0以下を消しますが、-= や subtract() は負の数を残します。

from collections import Counter

inv = Counter(pen=2)
inv["pen"] -= 5
print(inv, inv["pen"])
print(+inv, sum(inv.values()), inv.total())   # +inv で正の数だけ残す

inv2 = Counter(pen=2)
inv2.subtract(pen=5)
print(inv2)
print(Counter(pen=2) - Counter(pen=5))

print(Counter(pen=0), bool(Counter(pen=0)), Counter(pen=0) == Counter())
Counter({'pen': -3}) -3
Counter() -3 -3
Counter({'pen': -3})
Counter()
Counter({'pen': 0}) True True
書き方 pen=2 から 5 を引いた結果
c["pen"] -= 5 -3(残る)
c.subtract(pen=5) -3(残る)
c - Counter(pen=5) 消える
+c 正の数だけの新しい Counter

在庫のように「マイナスは0として扱いたい」なら - 演算子か +c を使い、「不足数を知りたい」なら subtract() を使うと目的どおりになります。また、Counter(pen=0) は中身があるので bool() が True ですが、Python 3.10 以降は Counter() と == で等しいと判定されます。 空かどうかの判定には c.total() == 0 や not +c を使うと意図が明確です。

dequeの真ん中のアクセスは遅い

速度表のとおり、deque[n // 2] は要素数に比例して時間がかかります。真ん中を頻繁に読み書きするなら list を使います。 また deque はスライス(dq[1:3])に対応していないので、必要なら itertools.islice(dq, 1, 3) を使います。

よくある質問

Q. Counterとdefaultdict(int)はどちらを使えばよいですか? A. 数えるだけなら Counter が便利です。 most_common() や足し算・引き算が使え、リストを丸ごと渡すと計測でも最速でした。1件ずつ別の値を足し込む集計(例:品目ごとの数量の合計)は defaultdict(int) のほうが速く、意図も伝わりやすいです。

Q. dequeはlistの代わりにいつでも使えますか? A. 両端の追加・削除が多い処理(キュー、直近 N 件の保持)では deque が向いています。 添字での読み書きやスライス、sort() が多い処理では list のほうが速く、機能も揃っています。

Q. OrderedDictは今でも必要ですか? A. 挿入順を保つだけなら、Python 3.7 以降は普通の dict で十分です。 move_to_end() で順番を動かす、popitem(last=False) で先頭から取り出す、順番まで含めて等しいか比べる、といった場面で OrderedDict を使います。

Q. namedtupleとdataclassはどちらがよいですか? A. 値を変更しない小さなレコードで、タプルとしても扱いたいなら typing.NamedTuple、それ以外は dataclass が選びやすいです。 dataclass は frozen=True で変更不可にでき、slots=True でメモリも抑えられます。

まとめ

  • collections は dict・list・tuple を用途別に便利にした標準ライブラリ。主役は Counter・defaultdict・deque・namedtuple の4つ
  • 数えるなら Counter(iterable)。most_common(n) は (要素, 個数) のリストを返し、リストを丸ごと渡すと手書きより約3〜4割速い
  • グループ化は defaultdict(list)。ただし d[key] で読むだけで要素が増えるので、確認は in か .get() で行う
  • 先頭の追加・削除は deque。100万件では list の約2万倍速く、maxlen で直近 N 件を自動で保持できる。真ん中のアクセスは list が速い
  • 名前つきレコードは typing.NamedTuple か dataclass。変更したいなら dataclass、タプルとして扱いたいなら NamedTuple
  • Counter の -= と subtract() は負の数を残す。0以下を消したいなら - 演算子か +c
  • OrderedDict は順番を動かすとき、ChainMap は設定を優先順に重ねるときに使う

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Python collectionsの使い方|Counter等4種の比較表とコード」

株式会社RJCでは、collections のような標準ライブラリを、仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?