S-JIS[2014-07-05/2018-10-01] 変更履歴

java.util.List

Javaコレクションのひとつであるリストについて。


概要

リストは、末尾に要素を追加してゆき、先頭から順番に全要素を処理するのに適したコレクション。
同じ値(要素)を複数保持することも出来る。
リストでは要素の並び順が保証される。

Setは値(要素)は1種類ずつしか保持できず、並び順も(基本的には)不定 。その代わり、存在有無のチェック(containsメソッド)は得意。


リストはjava.util.Listインターフェースで表される。
具象クラスとしては、java.util.ArrayListが使われることが多い。
保持する要素の型(クラス)はジェネリクスで指定するので、以下のようにして使う。

import java.util.List;
import java.util.ArrayList;
		List<String>  slist = new ArrayList<>(10); // 要素がStringのリスト
		List<Integer> ilist = new ArrayList<>(10); // 要素がIntegerのリスト

具象クラスの種類に関わらず、変数の型はListにすることが多い。
これは、その変数を「リストとして扱いたい」という意図であり、具象クラスの種類は(とりあえず)何でもいいから。
実際、Arrays.asList()の戻り値UnmodifiableListを受け取ることもあるので、インターフェースで宣言しておく方が都合が良い。

Listの上にはCollectionというインターフェースもあるのだが、これはSetと共通なので、リストとして扱いたい場合はやはりListインターフェースを指定するのが妥当だろう。


ListはIterableインターフェースを継承しているので、for each構文で使うことが出来る。
順次アクセス処理


リストの具象クラス

Listの主な具象クラス。

クラス名 JDK 説明 インスタンスを生成する例
java.util ArrayList 1.2 内部では配列(Array)で要素を保持する。
リストだが、ランダムアクセスが得意。

ArrayListに要素を追加すると、内部の配列に保持される。
配列のサイズを超えると、サイズを増やした新しい配列(概ね、元の1.5倍)が作られ、全要素がコピーされる。
したがって、事前にサイズが分かっている場合は、ArrayList作成時に初期サイズを指定しておく方が良い。
初期サイズを省略した場合、空の状態では配列は用意されない。要素が追加されたときにサイズ10の配列が作られる。

List<String> list = new ArrayList<String>(10);
java.util LinkedList 1.2 双方向リスト。
先頭と末尾の要素への追加・削除・取得が得意。ランダムアクセスは苦手。
(Java以外の言語で「リスト」と言ったら、大抵はLinkedListに似たものを指す)
ArrayListより若干メモリー消費量が多い。
List<String> list = new LinkedList<String>();
java.util Vector 1.0 古いライブラリー以外で使うことは無い。
位置づけとしてはsynchronizedされたArrayListだが、その目的ならCopyOnWriteArrayListの方が良い。
 
java.util.Arrays ArrayList 1.5 配列を扱うArraysクラスに、配列からリストに変換するasListメソッド(可変長引数)がある。
このメソッドは初期値を指定したリストを作るのに使える。
ただし、このメソッドが返す具象クラスはArraysの内部クラスのArrayListである。(単純名はArrayListだが、java.util.ArrayListとは別もの)
このArrayListは要素の追加・削除が出来ない(setによって変更することは出来る)ので注意が必要。
List<String> list = Arrays.asList("a", "b", "c");
java.util.Collections EmptyList 1.2 空の不変リスト。
このEmptyListは要素の追加は出来ない。
要素の取得・削除も出来ない。(要素が無いので)
List list = Collections.EMPTY_LIST; // JDK1.4
List<String> list = Collections.emptyList();
java.util.Collections SingletonList 1.2 要素が1つの不変リスト。
このSingletonListは要素の追加・削除・変更は出来ない。
List<String> list = Collections.singletonList("a");
java.util.Collections UnmodifiableList
UnmodifiableRandomAccessList
1.2 不変リスト。
元となるリストを指定すると、値取得専用のリストになって返ってくる。
(元となったリストがRandomAccessかどうかは配慮される)
このUnmodifiableListは要素の追加・削除・変更は出来ない。
元となったリストを変更すると、その内容は反映される。
List<String> list = 〜;
List<String> ulist = Collections.unmodifiableList(list);
java.util.
ImmutableCollections
AbstractImmutableList
List0, List1, ListN
9 不変リスト。[2018-06-02]
List.ofメソッドで返されるリスト。
List<String> list = Llist.of("a", "b");
java.util.Collections CheckedList 1.5 動的型保証されるリスト。
不正な型の値(要素)を入れたらClassCastExceptionを発生させてくれる。
 
java.util.Collections SynchronizedList
SynchronizedRandomAccessList
1.2 同期化されるリスト。
元となるリストを指定すると、更新が同期化(synchronized)されるリストになる。
が、これを使うよりCopyOnWriteArrayListの方が良い。
 
java.util.concurrent CopyOnWriteArrayList 1.5 マルチスレッドセーフなリスト(ArrayList相当)。
MTセーフなコレクション
List<String> list = new CopyOnWriteArrayList<String>();

リストの初期化

配列だと初期値を列挙する初期化構文がある(Scalaのリスト(Seq)にも初期値を指定する方法がある)が、JavaのListでは構文としては用意されていない。
初期値を設定したリストを作るには以下のような方法が考えられる。

方法 JDK 説明
通常   List<String> list = new ArrayList<>(10);
list.add("a");
list.add("b");
list.add("c");
普通にaddする方法。
asList 1.5 List<String> list = Arrays.asList("a", "b", "c"); Arrays.asListメソッドは可変長引数なので、初期値を指定する感じで使える。
ただし、このリストは要素の追加・削除が出来ないので注意。
なお、要素を変更(setやsort)することは出来るので、不変リストというわけでもない。
addAll 1.5 List<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c");
Collections.addAllメソッドで複数の値を追加することが出来る。
ちなみにList#addAll()は他のコレクションの全要素を追加するメソッドであり、複数の値を追加するメソッドはListには無い。
初期化子   List<String> list = new ArrayList<String>() {{ add("a");add("b");add("c"); }}; インスタンス初期化子を使う方法。
とてもカッチョイイ書き方なのだが(笑)、この方法を知らない人が見たら非常に不可解に思えるので、実務的には使用すべきではないだろう^^;
Stream 1.8 List<String> list = Stream.of("a", "b", "c").collect(Collectors.toList()); Streamを使う方法。
ちょっと長めだが、Arrays.asListを使う方法とは異なり、java.util.ArrayListが返ってくる。
of 9 List<String> list = List.of("a", "b", "c"); List.ofメソッドは不変リストを返す。[2017-09-24]
要素にnullを指定するとNullPointerExceptionが発生する。
連番が欲しい場合 1.7以前 List<Integer> list = new ArrayList<>(10);
for (int i = 1; i <= 10; i++) {
  list.add(i);
}
 
1.8 List<Integer> list = IntStream.rangeClosed(1, 10).boxed().collect(Collectors.toList());
同じ値で埋める場合 1.7以前 List<String> list = new ArrayList<>(4);
for (int i = 0; i < 4; i++) {
  list.add("a");
}
Collections.fill()というメソッドもあるのだが、これは既に存在している部分を全て同じ値にするもの。空のリストには使えない。
1.8 List<String> list = Stream.generate(() -> "a").limit(4).collect(Collectors.toList());
1.2 List<String> list = Collections.nCopies(4, "a"); Collections.nCopiesメソッドは不変リストを返す。[2018-01-21]
他のコレクション
から作る場合
  List<String> list2 = new ArrayList<>(list);  
10 List<String> list2 = List.copyOf(list); List.copyOfメソッドは不変リストを返す。[2018-06-02]

“インデックスを使ったアクセスメソッドしか存在しないクラス”から、その一覧(リスト)を作成する例。

	// Excelのシート一覧を作成する(POI)
	Workbook workbook = WorkbookFactory.create(new File(excelFile));
	List<Sheet> sheets = IntStream.range(0, workbook.getNumberOfSheets()).mapToObj(i -> workbook.getSheetAt(i)).collect(Collectors.toList());

IntStream.rangeメソッドに開始インデックス(0)と終了インデックス(要素の個数)を指定し、インデックスのStreamを作成する。
後は、mapToObjメソッドで、インデックスを使ったアクセスメソッドを呼び出し、欲しいオブジェクトを取得する。


リストのメソッド

List関連の主なメソッド。

クラス 戻り型 メソッド名 引数 JDK 説明
コーディング 結果
java.util
Collection<E>
 
boolean add E element   (末尾に)要素を追加する。
常にtrueが返る。(addメソッドはCollectionで定義されているメソッドなので、List以外ではfalseが返ることもある)
List<String> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");
list1.add("c");
[a, b, c]
java.util
List<E>
boolean add int index,
E element
  指定された位置に要素を追加する。(それまで有った要素は後ろにずれる) List<String> list2 = new ArrayList<>(list1);
list2.add(1, "z");
[a, z, b, c]
java.util
Collection<E>
boolean addAll Collection<E> c   渡されたコレクションの全要素を自分のList(の末尾)に追加する。
渡されたコレクションが空の場合はfalseが返る。
List<String> list2 = new ArrayList<>(list1);
list2.addAll(Arrays.asList("x", "y", "z"));
[a, b, c, x, y, z]
java.util
List<E>
boolean addAll int index,
Collection<E> c
  渡されたコレクションの全要素を自分のListの指定された位置に追加する。 List<String> list2 = new ArrayList<>(list1);
list2.addAll(1, Arrays.asList("x", "y", "z"));
[a, x, y, z, b, c]
java.util
List<E>
E set int index,
E element
  指定された位置に新しい値をセットする。
指定する位置は、要素が存在している範囲内である必要がある。位置が範囲外の場合はIndexOutOfBoundsExceptionが発生する。
セット前に保持していた要素(値)を返す。
List<String> list2 = new ArrayList<>(list1);
list2.set(1, "z");
[a, z, c]
java.util
List<E>
E get int index   指定された位置の要素(値)を返す。
位置が範囲外の場合はIndexOutOfBoundsExceptionが発生する。
ランダムアクセス
String s = list1.get(1); b
java.util
Iterable<E>
void forEach (E) -> void action 1.8 全要素を順次処理する。
引数として、処理を行う関数を渡す。
list1.forEach(element -> System.out.println(element)); a
b
c
java.util
Iterable<E>
Iterator<E> iterator     Iteratorを返す。 for (Iterator<String> i = list1.iterator(); i.hasNext();) {
  String element = i.next();
  System.out.println(element);
}
a
b
c
java.util
List<E>
ListIterator<E> listIterator     ListIteratorを返す。
通常のIteratorは次の要素に移ることしか出来ないが、ListIteratorは前の要素に移動することが出来る。

nextIndex()やpreviousIndex()でインデックス(位置)を取得することも出来る。
nextIndex()の例

ListIterator<String> i = list1.listIterator();
while(i.hasNext()){
  String element = i.next();
  System.out.println(element);
}
while(i.hasPrevious()){
  String element = i.previous();
  System.out.println(element);
}
a
b
c
c
b
a
java.util
List<E>
ListIterator<E> listIterator int index   for (ListIterator<String> i = list1.listIterator(list1.size()); i.hasPrevious();) {
  String element = i.previous();
  System.out.println(element);
}
c
b
a
java.util
Iterable<E>
Spliterator<E> spliterator   1.8 Streamに変換する際に使われる。
stream()
   
java.util
Collection<E>
int size     保持している要素の個数を返す。

LinkedListの場合でも内部で要素数を保持しているので、すぐに要素数が返る。
(Java以外の言語のリストでは、要素を全て辿らないと要素数が分からない事も多い)

int size = list1.size(); 3
java.util
Collection<E>
boolean isEmpty     空リスト(要素を保持していない)の場合、trueを返す。 boolean empty = list1.isEmpty(); false
java.util
List<E>
int indexOf Object element   要素の位置を返す。(先頭から順番に探索する)
保持していなかった場合は-1が返る。
int n = list1.indexOf("b"); 1
java.util
List<E>
int lastIndexOf Object element   要素の位置を返す。(末尾から順番に探索する)
保持していなかった場合は-1が返る。
int n = list1.lastIndexOf("b"); 1
java.util
Collection<E>
boolean contains Object element   指定された要素を保持している場合、trueを返す。
線形探索になるので、保持している要素数が非常に多いと遅くなるので注意。
boolean exists = list1.contains("b"); true
java.util
Collection<E>
boolean containsAll Collection<?> c   渡されたコレクションに入っている全要素が自分のList内に存在していたらtrueを返す。 Set<String> set = new HashSet<>();
Collections.addAll(set, "b","c");
boolean exists = list1.containsAll(set);
true
java.util
Collection<E>
void clear     全要素を削除する。 list2.clear(); []
java.util
Collection<E>
boolean remove Object element   要素を削除する。
実際に削除した(要素を保持していた)場合はtrueが返る。
List<String> list2 = new ArrayList<>(list1);
list2.remove("b");
[a, c]
java.util
List<E>
E remove int index   指定された位置にある要素を削除する。
位置が範囲外の場合はIndexOutOfBoundsExceptionが発生する。
削除された要素(値)を返す。
List<String> list2 = new ArrayList<>(list1);
list2.remove(1);
[a, c]
java.util
Collection<E>
boolean removeAll Collection<?> c   自分のList内の各要素に対し、渡されたコレクションにその要素が含まれていたら、自分のListからその要素を削除する。
1つでも削除した場合はtrueが返る。
Set<String> set = new HashSet<>();
Collections.addAll(set, "b", "c", "d");
List<String> list2 = new ArrayList<>(list1);
list2.removeAll(set);
[a]
java.util
Collection<E>
boolean removeIf (E) -> boolean filter 1.8 条件に合致する要素を削除する。
引数として、条件判定用の関数を渡す。
Stream#filter()(Stream#filter()は、条件に一致するものを抽出する(残す))
List<String> list2 = new ArrayList<>(list1);
list2.removeIf(element -> element.equals("b"));
[a, c]
java.util
Collection<E>
boolean retainAll Collection<?> c   自分のList内の各要素に対し、渡されたコレクションにその要素が含まれていたら、その要素だけを残す(他の要素を削除する)。
1つでも削除した場合はtrueが返る。
Set<String> set = new HashSet<>();
Collections.addAll(set, "b", "c", "d");
List<String> list2 = new ArrayList<>(list1);
list2.retainAll(set);
[b, c]
java.util
List<E>
void replaceAll (E) -> E operator 1.8 各要素を別の値(要素の型は同じ)に変換する。
引数として、変換用の関数を渡す。
Stream#map()
List<String> list2 = new ArrayList<>(list1);
list2.replaceAll(element -> element.toUpperCase());
[A, B, C]
java.util
List<E>
void sort Comparator<E> c 1.8 自分のList内の要素をソートする。
Listをソートするには、JDK1.7以前はCollections.sort()を使っていたが、1.8ではList#sort()の方が具象クラス毎に実装されていて効率が良い。
List<String> list2 = new ArrayList<>(list1);
list2.sort(Comparator.reverseOrder());
[c, b, a]
java.util
List<E>
List<E> subList int fromIndex,
int toIndex
  指定された範囲のリストを返す。
このリストは元のリストの一部分であり、変更は共有される。
List<String> list2 = new ArrayList<>(list1);
List<String> slist = list2.subList(1, 2);
System.out.println(list2);
System.out.println(slist);
list2.set(1, "B");
slist.add("s");
System.out.println(list2);
System.out.println(slist);
[a, b, c]
[b]
[a, B, s, c]
[B, s]
java.util
Collection<E>
Object[] toArray     保持している全要素をObject配列にして返す。 Object[] array = list1.toArray(); [a, b, c]
java.util
Collection<E>
T[] toArray T[] array   保持している全要素を配列に入れて返す。
渡された配列に全要素が収まれば、その配列が返る。
渡された配列のサイズが小さい場合は拡張された配列が返る。
String[] array = list1.toArray(new String[list1.size()]); [a, b, c]
java.util
Collection<E>
T[] toArray IntFunction<T[]> 11 保持している全要素を配列に入れて返す。[2018-10-01] String[] array = list1.toArray(String[]::new); [a, b, c]
java.util
Collection<E>
Stream<E> stream   1.8 Stream(直列ストリーム)を返す。 Stream<String> stream = list1.stream();  
java.util
Collection<E>
Stream<E> parallelStream   1.8 Stream(並列ストリーム)を返す。 Stream<String> stream = list1.parallelStream();  

クラスやメソッドによっては、java.lang.UnsupportedOperationExceptionを返す実装もある。
例えばUnmodifiableListは不変リストなので、値を変更するaddやsetメソッドを呼び出すとUnsupportedOperationExceptionが発生する。


順次アクセス操作

リストでは、保持している値(要素)を先頭から順番に処理していくことがある。

Iteratorパターン JDK1.4
for (Iterator i = list.iterator(); i.hasNext();) {
	型 変数 = (型) i.next();
〜
}
 
JDK1.5
for (Iterator<型> i = list.iterator(); i.hasNext();) {
	型 変数 = i.next();
〜
}
JDK1.5ではジェネリクスが使えるようになった。
for each構文 JDK1.5
for (型 変数 : list) {
〜
}
ListはIterableを継承しているので、for each構文を使うことが出来る。
forEachメソッド JDK1.8
list.forEach(変数 -> {
〜
});
forEachメソッドを使い、ラムダ式で処理を記述する方法。

RandomAccessインターフェースを実装しているリストであっても、for each構文の場合は(内部では)Iteratorパターンになってしまうが、
forEachメソッドは具象クラスごとに効率の良い実装になっているので、forEachメソッドを使う方が良さそう。


変換処理

リストの全要素を別の型に変換して新しいリストを作成する例。

JDK1.4
List slist = Arrays.asList(new Object[]{ "a.txt", "b.txt", "c.txt"} );
List flist = new ArrayList(slist.size());
for (Iterator i = slist.iterator(); i.hasNext();) {
	String s = (String) i.next();
	flist.add(new File(s));
}
元のListの要素に順次アクセスして、新しいListにaddしていく。
JDK1.5
List<String> slist = Arrays.asList("a.txt", "b.txt", "c.txt");
List<File> flist = new ArrayList<File>(slist.size());
for (String s : slist) {
	flist.add(new File(s));
}
JDK1.8
List<String> slist = Arrays.asList("a.txt", "b.txt", "c.txt");
List<File> flist = slist.stream().map(s -> new File(s)).collect(Collectors.toList());
Streamのmapメソッドを使うと、一行で済む。

削除処理

特定条件の要素を削除する例。

JDK1.5
List<String> slist = new ArrayList<String>(Arrays.asList("a", "bb", "ccc"));
for (Iterator<String> i = slist.iterator(); i.hasNext();) {
	String s = i.next();
	if (s.length() == 2) {
		i.remove();
	}
}
Iteratorパターン。
JDK1.8
List<String> slist = new ArrayList<>(Arrays.asList("a", "bb", "ccc"));
slist.removeIf(s -> s.length() == 2);
 

重複排除処理

リスト内の重複した要素を1つにまとめる例。

JDK1.5
public static List<String> distinct(List<String> slist) {
	List<String> dlist = new ArrayList<String>();

	Set<String> set = new HashSet<String>();
	for (String s : slist) {
		if (!set.contains(s)) {
			set.add(s);
			dlist.add(s);
		}
	}

	return dlist;
}
別のリストを用意して、重複しない要素だけ追加していく方法。
JDK1.5
public static void distinct(List<String> slist) {
	Set<String> set = new HashSet<String>();
	for (Iterator<String> i = slist.iterator(); i.hasNext();) {
		String s = i.next();
		if (set.contains(s)) {
			i.remove();
		} else {
			set.add(s);
		}
	}
}
Iteratorパターンで、重複する要素だけ削除していく方法。
(この方法では、元のリストを変更する)
JDK1.5
public static List<String> distinct(List<String> slist) {
	return new ArrayList<String>(new LinkedHashSet<String>(slist));
}
public static Collection<String> distinct(List<String> slist) {
	return new LinkedHashSet<String>(slist);
}
LinkedHashSetを使う方法。
LinkedHashSetは重複を排除してくれるし、並び順も保証される。
JDK1.8
public static List<String> distinct(List<String> slist) {
	return slist.stream().distinct().collect(Collectors.toList());
}
Streamのdistinctメソッドを使う方法。

ランダムアクセス操作

ArrayList(やVector)は、RandomAccessインターフェースを実装している。[2007-03-26]
これは、「順次アクセス操作(Iteratorを使って先頭から順番にアクセスする)よりも、ランダムアクセス操作(インデックスを使って途中の要素を直接指定する)の方が高速(であると想定される)」ということを意味する。
(RandomAccessを実装していないリストの代表は、LinkedList

リストの本来の姿では、全要素を順番に操作するにはIteratorパターンを使う。
しかしRandomAccessインターフェースを実装しているコレクションでは、インデックスで直接指定する方が実行速度が速い。
と言ってもそんなに大差ない(Iteratorインスタンスの生成分と、ループ1回につきメソッド呼び出しが1〜2回分増える程度)。

インデックスを使って指定するコーディング例
JDK1.4 JDK1.5以降
for (int i = 0; i < list.size(); i++) {
	型 要素 = (型) list.get(i);
〜
}
for (int i = 0; i < list.size(); i++) {
	型 要素 = list.get(i);
〜
}

(ArrayListの場合、インデックスを使う方がIteratorパターンより3倍くらい速い)
ArrayListとLinkedListの順次アクセス時間

ただしfor each構文ではRandomAccessまでは判断してくれず、ArrayListであってもIteratorで展開されてしまう。[2007-06-11]

forEachメソッドは(RandomAccessインターフェースとは無関係に)具象クラスごとに個別の実装になっている。[2014-07-05]


配列との変換

リストから配列を生成するにはtoArrayメソッドを使用する。[2007-12-07]

	List<String> list = new ArrayList<String>();
	list.add("abc");
	list.add("def");

	String[] array = list.toArray(new String[list.size()]);

配列からリスト(サイズ不変ArrayList)を生成するにはArrays.asListメソッドを使うのが便利。

	String[] array = { "abc", "def" };
	List<String> list = Arrays.asList(array);

asListメソッドはJDK1.5で可変長引数に置き換えられたので、以下のような書き方も可。

	List<String> list = Arrays.asList("abc", "def");

ただし、asList()が返すのはサイズが変えられないリストなので注意。[2008-08-11/2010-03-06]
(つまり、add()したりremove()したり出来ない。set()すると元の配列の値も書き換わる。要するにこのリストは配列のラッパー)


リストと配列のどちらを使うべきか?

リストと配列は、どちらも値(要素)を順番に保持することが出来るので、似ている。
では、メソッドの引数や戻り値の型としてリストと配列のどちらを使うべきか?というと、今はリストを使えばいいと思う。

JDK1.4以前ではジェネリクスが無かった為、リストの要素の種類(型)は何だか分からなかった。
その点、配列は要素の種類(型)が明示されるので、ソースを見たときにすぐ分かるし、異なる型を扱おうとしたらコンパイルエラーになるし、配列を使う利点があった。
JDK1.5以降ではリストに型引数(要素の種類(型))が指定できるようになったので、配列だけに有利な点は無くなった。
むしろ、全般的にリストの方が有利である。
(配列を使う必要があるのは、可変長引数くらいなのではなかろうか)

  リスト 配列 備考
戻り型に指定し、
変更を許す場合
public List<String> getResults() {
  List<String> result = new ArrayList<>();
  result.add("a");
  result.add("b");
  return result;
}
public String[] getResults() {
  String[] result = { "a", "b" };
  return result;
}
戻ってきたものが配列だと、変更がしづらい。
List<String> result = getResults();
result.add("z");
String[] result0 = getResults();
String[] result = Arrays.copyOf(result0, result0.length + 1);
result[result0.length] = "z";
戻り型に指定し、
変更を許さない場合
private static final List<String> RESULTS;
static {
  List<String> list = new ArrayList<>();
  list.add("a");
  list.add("b");
  RESULTS = Collections.unmodifiableList(list);
}
public List<String> getResults() {
  return RESULTS;
}
private static final String[] RESULTS = { "a", "b" };
public String[] getResults() {
  return Arrays.copyOf(RESULTS, RESULTS.length);
}
配列では、変更不可にする方法は無い。

Streamとの変換

リストからStreamを生成するにはstreamもしくはparallelStreamメソッドを使用する。

	List<String> list = new ArrayList<>();
	list.add("abc");
	list.add("def");

	Stream<String> stream = list.stream();

Streamからリストを生成するにはCollectors.toListを使用する。

	Stream<String> stream = Stream.of("abc", "def");

	List<String> list = stream.collect(Collectors.toList());

Collectors.toListの内部は、java.util.ArrayListを作成して、Stream内の要素をaddしていく形になっている。


コレクションへ戻る / Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま